Resolución de problemas de subprocesos múltiples mediante la asignación paralelaEstoy en un sistema Mac tratando de que los subprocesos múltiples funcionen en una tabla de datos grande (> 8 MM) en operaciones por filas. Son solo cálculos simples como la división o la copia de valores tal cual. No puedo adjuntar mis datos propietarios, pero en esta publicación incluiré en el JSL una matriz aleatoria del mismo tamaño que mi conjunto de datos pero con datos aleatorios.
Names Default To Here(1);<BR /><BR />//For JMP community.
dt = J(Random Integer(6000000 , 8000000) , 5 , Random Integer(10)); <BR />dt = As Table(dt); <BR /><BR />//dt = Current Data Table(); //Table needs to be formatted such that data table has 5 columns with names X , Y , Z , Filename , Radius.
//columns must be in that order for matrix functionality to work
edgeExclusion = 0.01; //in units of radial meters
stepSize = 0.0025; //in meters; this is the resolution of the desired particle size analysis. This is the side length of the square of the defect area resolution desired.
waferSize = 0.2; //in meters, diameter
If( Floor( waferSize / stepSize ) != Ceiling( waferSize / stepSize ) , Throw( "!Defect resolution size not a common factor of wafer size"));
//Takes data table as matrix (fDT) and returns matrix of x, y, stddev, stddev points, and UID.
//Requires datatable to have X , Y , Z , Filename , Radius , and UID so that matrix fDT has X , Y , Z , Radius , and UID; in that order.
stdDevCalculator2 = Function ( { fDT , fFirstRow , fLastRow , fWS , fEE , fSS } , {Default Local} ,
fSubDT = fDT[ fFirstRow :: fLastRow , 0];
outputMat = J(0,5);
For( i = 1 , i <= N Rows(fSubDT) , i++ ,
//If( fSubDT[i,4] > (fWS/2)-fEE , Continue()); //Possibly remove to keep number of columns same for Parallel Assign
rowMat = Matrix( { Floor(fSubDT[i,1]/fSS) , Floor(fSubDT[i,2]/fSS) , fSubDT[i,3] , fSubDT[i,4] , fSubDT[i,5] } );
rowMat = Shape(rowMat,1,5);
If(N Rows(outputMat) == 0 , outputMat = rowMat , Concat To(outputMat , rowMat));
);
outputMat[1,0];
);
t1 = Tick Seconds();
numThreads = 8;
//Number of rows to delete to make Parallel Assign function operate. Trimming original data table to even multiple of thread count will allow tOutMat to be a square matrix,
//so that each thread is assigning the same number of columns.
delNum = N Rows(dt) - (Floor(N Rows(dt)/numThreads)*numThreads);
dt << Select Randomly(delNum);
dt << Delete Rows(); //Trim data set
numCols = 5 * (N Rows(dt)/numThreads); //5 for number of colums in original stacked data table.
//outMat = stdDevCalculator2(dtMat , 1 , N Rows(dtMat) , waferSize , edgeExclusion , stepSize); //For single threaded testing
dtMat = dt << GetAsMatrix;
tOutMat = J(numThreads,numCols);
Parallel Assign(
{tDT = dtMat, tWS = waferSize, tEE = edgeExclusion, tSS = stepSize, tSDC = Name Expr(stdDevCalculator2), tNum = numThreads },
tOutMat[i,0] = tSDC( tDT, 1 + Floor((i - 1) * N Rows(tDT) / tNum) , Floor( i * N Rows(tDT) / tNum) , tWS, tEE, tSS );
);
Show(tOutMat);
Write( "elapsed time=", Tick Seconds() - t1 );Mi problema es que en mi sistema Mac, parece que la Asignación paralela funciona porque todas mis utilizaciones principales saltan al 100 % cuando se ejecuta este script. Sin embargo, después de darle tiempo suficiente para que finalice (mucho después de que lo haga la versión de subproceso único), tengo que forzar el cierre.
He jugado un poco para entender cómo JSL prefiere que "inyecte" un conjunto de valores por filas en otra matriz. Tome el ejemplo a continuación:
Names Default to Here(1);
mat1 = J(3,5,0);
mat2 = J(1,5,Random Integer(5));
show(mat1);
Show(mat2);
mat1[2,0] = mat2[1,0];
Show(mat1);
mat1 ahora tendrá su segunda fila como una copia completa de mat2. Intenté emular este mismo tipo de asignación de matriz devolviendo una sola fila con N filas (tabla)/N subprocesos. ¿Alguien tiene alguna idea de por qué no puedo hacer que esto funcione?
Re: Resolución de problemas de subprocesos múltiples mediante asignación paralelavolviendo a desconcertarme por el JSL lento: creo que el valor de tDT es un enorme matriz que se está copiando a enorme número de veces para pasar a la función tf.Se copian los números 8E6 * 8E6. Los subprocesos están esperando el acceso a la memoria.
tOutMat[i,j] = tf( tDT, i , j , tSS )
Esa copia no ocurre en el JSL rápido.
Re: Resolución de problemas de subprocesos múltiples mediante asignación paralelaHola @msulzer_bsi
Una declaración general: los subprocesos múltiples son una mala idea cuando el problema que está tratando de resolver no es paralelizable, o cuando el problema que está tratando de calcular es tan pequeño que generar subprocesos costará más tiempo que calcular en un solo subproceso.
Si su problema se puede dividir en partes que se pueden ejecutar en paralelo, podría ser una buena idea hacerlo, pero depende de cuánto se pueda ejecutar en paralelo y qué tan fácil sea unir los resultados del subproblema. Si su algoritmo requeriría múltiples uniones (a un hilo) para dividirse en nuevos subproblemas en nuevos hilos, tiene que ser muy intensivo en computación para que valga la pena el esfuerzo de paralelizar, si su resultado, por otro lado, es solo un la lista de los resultados del subproblema se unió una vez, entonces hay potencial.
No veo mucho beneficio en su operación y la mejora de la velocidad que ganaría en 2 segundos podría no ser mucho, entonces, ¿por qué quiere hacer este esfuerzo y bloquear con estas otras tareas que podrían/han ejecutado en los otros subprocesos? A veces no vale la pena ganar otros 0,5 s por lo que tienes que hacer. A menos que sea una aplicación de alto rendimiento en la que obtenga todos los 2-3, otra parte de los datos para procesar, no veo una buena razón para acelerar esta operación debido a los datos de> 8MM como dijo.
Re: Resolución de problemas de subprocesos múltiples mediante asignación paralelaHola @Craige_Hales ,
Utilicé su consejo de crear una definición de función dentro de la asignación paralela. Vea abajo:
Names Default To Here(1);
dt = Current Data Table(); //Table needs to be formatted such that data table has 5 columns with names X , Y , Z , Filename , Radius.
//columns must be in that order for matrix functionality to work
edgeExclusion = 0.01; //in units of radial meters
stepSize = 0.0025; //in meters; this is the resolution of the desired particle size analysis. This is the side length of the square of the defect area resolution desired.
waferSize = 0.2; //in meters, diameter
If( Floor( waferSize / stepSize ) != Ceiling( waferSize / stepSize ) , Throw( "!Defect resolution size not a common factor of wafer size"));
t1 = Tick Seconds();
numThreads = 8;
//Number of rows to delete to make Parallel Assign function operate. Trimming original data table to even multiple of thread count will allow tOutMat to be a square matrix,
//so that each thread is assigning the same number of columns.
delNum = N Rows(dt) - (Floor(N Rows(dt)/numThreads)*numThreads);
If( delNum != 0 , dt << Select Randomly(delNum));
dt << Delete Rows(); //Trim data set
dtMat = dt << GetAsMatrix;
dtMat = Shape(dtMat , numThreads);
tOutMat = J(numThreads , N Cols(dtMat));
Parallel Assign(
{tDT = dtMat, tSS = stepSize, tf = Function ( { fDT , x , y , fSS } ,
If( Mod(y,5) == 1 , Floor(fDT[x,y]/fSS) ,
Mod(y,5) == 2 , Floor(fDT[x,y]/fSS) ,
fDT[x,y];
);
); },
tOutMat[i,j] = tf( tDT, i , j , tSS );
);
Show(tOutMat);
Write( "elapsed time=", Tick Seconds() - t1 );Ahora estoy haciendo que el script se ejecute y no me dé ningún resultado extraño ni arroje un error. Volví a evaluar lo que explicaste sobre el uso de asignación paralela para evaluar cada combinación de la matriz de salida en función de la ubicación de la matriz de salida.En mi caso, solo estoy interesado en realizar cálculos sobre los valores en las dos primeras columnas de la tabla de datos de fila larga original, por lo tanto, las declaraciones Mod() ==1 y Mod() ==2. Este no es el script más flexible, debido a los estrictos requisitos de entrada de formato de tabla, pero ese es un problema para más adelante.
Sin embargo, ahora tengo un fenómeno extraño en el que, cuando ejecuto la versión multiproceso del script, obtengo tiempos de ejecución de aproximadamente 200 segundos. Cuando reescribo la declaración de asignación paralela para que sea un bucle for anidado como se muestra a continuación como un script de subproceso único:
For(i = 1 , i <= numThreads , i++ ,
For(j = 1 , j <= numCols , j++ ,
If( Mod(j,5) == 1 , tOutMat[i,j] = Floor(dtMat[i,j]/stepSize) ,
Mod(j,5) == 2 , tOutMat[i,j] = Floor(dtMat[i,j]/stepSize) ,
tOutMat[i,j] = dtMat[i,j];
);
);
);
Puedo obtener tiempos de ejecución ~ 2 segundos ... Le di forma a la matriz de entrada a pesar de que es innecesaria para poder comparar de manera más uniforme el proceso de operaciones de un solo subproceso frente a multiproceso. No puedo entender qué tipo de sobrecarga está causando que la versión multiproceso sea mucho más lenta. He reducido bastante el cálculo de la matriz desde la primera vez que publiqué mi pregunta. ¿Tienes alguna idea?
Gracias, msulzer
Re: Resolución de problemas de subprocesos múltiples mediante asignación paralelaHola Craige,
Muchas gracias por su respuesta a mi pregunta; Esperaba que al menos vieras el hilo porque mi aprendizaje sobre Asignación paralela se debió en gran parte a algunos ejemplos que publicaste anteriormente. Intentaré digerir cuál es el significado central de sus declaraciones y le responderé con los resultados que pueda aplicar a mi caso específico.
Re: Resolución de problemas de subprocesos múltiples mediante asignación paralelaCreo que está esperando algo que Parallel Assign no pueda hacer. este codigo
tOutMat[i,0] =
en la Asignación Paralela me sorprendió; lo único que espera Parallel Assign son nombres de variables JSL para esos subíndices, no un cero. Parece que el cero se ignora silenciosamente y el lado derecho de la tarea no tiene forma de saber cuál es el segundo índice.Aquí hay algunos ejemplos; puede cambiar el segundo subíndice a cero y ver que se ignora.
Uno. Use una variable i, local para cada subproceso, e increméntela después de cada uso. Los valores del índice j,k (o j,0) no se utilizan para determinar el valor calculado para el elemento.
x = J( 7, 11 );
Parallel Assign( {i = 0}, x[j, k] = (i++) );
Show( x );x =
[ 0 0 0 0 1 1 1 1 2 2 2,
2 3 3 3 3 4 4 4 4 5 5,
5 5 6 6 6 6 7 7 7 7 8,
8 8 8 9 9 9 9 10 10 10 10,
11 11 11 11 12 12 12 12 13 13 13,
13 14 14 14 14 15 15 15 15 16 16,
16 16 17 17 17 17 18 18 18 18 19];
Dos. Similar, muestra lo mismo de otra manera. Cada subproceso obtiene un valor único que permanece sin cambios. h=4 indica cuántos procesadores hay disponibles. No debe depender de que JMP siempre intercale los cálculos como se muestra, pero así es por ahora.
x = J( 7, 11 );
h = 0;
Parallel Assign( {i = (h++)}, x[j, k] = i );
Show( x, h );x =
[ 0 1 2 3 0 1 2 3 0 1 2,
3 0 1 2 3 0 1 2 3 0 1,
2 3 0 1 2 3 0 1 2 3 0,
1 2 3 0 1 2 3 0 1 2 3,
0 1 2 3 0 1 2 3 0 1 2,
3 0 1 2 3 0 1 2 3 0 1,
2 3 0 1 2 3 0 1 2 3 0];
h = 4;
Tres. Utilice una sola variable global. Aumenta después de cada uso. Los subprocesos se bloquean entre sí y pueden o no ejecutarse en un orden particular.
x = J( 7, 11 );
global:g = 0;
Parallel Assign( {i = (h++)}, x[j, k] = (global:g++) );
Show( x, g );x =
[ 0 20 39 58 1 21 40 59 2 22 41,
60 3 23 42 61 4 24 43 62 5 25,
44 63 6 26 45 64 7 27 46 65 8,
28 47 66 9 29 48 67 10 30 49 68,
11 31 50 69 12 32 51 70 13 33 52,
71 14 34 53 72 15 35 54 73 16 36,
55 74 17 37 56 75 18 38 57 76 19];
g = 77;
En todos los casos anteriores, reemplazar k con 0 no cambia nada. Internamente, Parallel Assign sigue calculando todos los pares de índices y solicitando a uno de los subprocesos que calcule un valor para cada par.
Si continúa explorando este camino, le sugiero que haga instancias separadas de su función para cada hilo, algo así como
x = J( 2, 3 );
Parallel Assign( {f = Function( {x, y}, x * y )}, x[i, j] = f( i, j ) );
Show( x );x = [1 2 3, 2 4 6];
lo que hace que la f local de cada subproceso tenga una copia separada de la función JSL. Creo que nameexpr no hace copias separadas, y pueden necesidad estar separados para funcionar en paralelo correctamente.
También puede utilizar una asignación a un elemento de matriz global. Use una matriz unidimensional de la longitud correcta para impulsar la asignación paralela: (observe también el valor del lado derecho de varias declaraciones entre paréntesis)
global:results = J( 5, 7, . );
driver = J( N Rows( results ), 1, 0 );
h = 0;
Parallel Assign(
{procNum = (1 + h++), NC = N Cols( results )},
driver[j] =
(
For( k = 1, k <= NC, k += 1,
global:results[j, k] = procNum * 100 + j * 10 + k
);
procNum// value assigned to driver
)
);
Show( global:results, h, driver );globales:resultados =
[ 111 112 113 114 115 116 117,
221 222 223 224 225 226 227,
331 332 333 334 335 336 337,
441 442 443 444 445 446 447,
151 152 153 154 155 156 157];
h = 4;
conductor = [1, 2, 3, 4, 1];
Pero las variables globales probablemente harán que funcione tan lento como un solo subproceso.
Y finalmente: Parallel Assign intenta evitar que escriba código inseguro que podría permitir que dos subprocesos accedan a alguna estructura interna, al mismo tiempo, de una manera que producirá resultados incorrectos o un bloqueo. Es posible escribir JSL que evada las defensas de Parallel Assign... guarde su trabajo a menudo si está empujando el sobre.