A medida que avanza la informática, pasé de 1 núcleo a 2 núcleos a 4 núcleos... a 64 subprocesos en mi estación de trabajo. Mientras tanto, .jsl ha permanecido efectivamente en un solo subproceso. Sé que algunas plataformas utilizarán todos los subprocesos, pero me gustaría poder distribuir operaciones jsl simples a voluntad en varios subprocesos. Por ejemplo, si tengo una secuencia de comandos que opera por filas en 10 000 000 filas de datos, si estoy iterando por fila en un bucle for y sé que el resultado de cada fila es independiente de las otras filas, me gustaría dividir eso operación en 64 bucles for asíncronos, pause otra ejecución mientras los 64 bucles se completan en paralelo, luego regrese a un script síncrono de un solo subproceso después de que todos estén completos, como lo haría en .py con Pool() y poolObject.join.
Hola Jeff,
¿Dónde puedo encontrar qué plataformas/tareas integradas aprovechan los subprocesos múltiples?
Como se señaló directamente anteriormente, este deseo está más allá del alcance de lo que vamos a hacer en JSL. Si hay operaciones específicas que cree que se beneficiarían de los subprocesos múltiples, abra otros deseos con los detalles.
Si bien este deseo tiene muchos votos, desafortunadamente cualquier cosa más de lo que ofrece Parallel Assign() será difícil de cumplir. Debido a la estrecha integración con la interfaz de usuario y la naturaleza interactiva de JMP, no es posible proporcionar un entorno seguro para subprocesos sin limitar el posible JSL a esencialmente lo que permite la asignación paralela.
La mejor manera de aprovechar los subprocesos múltiples es usar las plataformas integradas tanto como sea posible. Estas plataformas son donde podemos proporcionar soluciones multiproceso directamente.Abrir otros deseos para operaciones integradas en las que cree que los subprocesos múltiples serían útiles es la mejor manera de informarnos dónde le gustaría ver más subprocesos múltiples.
En cuanto a la agricultura de operaciones JSL simples en múltiples hilos, hay una función llamada Parallel Assign() para eso. Divide el trabajo en partes donde el código para cada parte da como resultado un solo número, y el resultado de toda la operación es una matriz de números.
Hay un ejemplo de juguete en el índice de secuencias de comandos para Parallel Assign(), y el segundo ejemplo para Frame Size() muestra un ejemplo más elaborado para calcular datos de conjuntos de Mandelbrot. Otro pequeño ejemplo está en @Craige_Hales 's Publicación de JSL Cookbook en bucles y ahí está Más sobre Mandelbot en su blog.
He incluido otro ejemplo a continuación que encuentra el archivo más grande en una lista de archivos que usan subprocesos múltiples. El script ejecuta primero una versión de subprocesos únicos y puede ver ambos brotes de actividad en esta captura de pantalla del administrador de tareas. La versión de un solo hilo es la mesa larga y la versión paralela es el pico alto. Para 17 000 archivos en 8 núcleos, el tiempo pasó de 13 s a 4,5 s, o 3 veces más rápido. No es ideal, pero como suele ser el caso, los subprocesos comparten algunos recursos, como el acceso al disco en este caso.
El JSL que se ejecuta en paralelo debe ser simple para que no modifique el estado global compartido. Es por eso que algunos globales se copian como primer paso. También significa que el código paralelo no puede hacer cosas como crear tablas de datos o interactuar con la pantalla.
Estamos interesados en saber qué tipo de otras acciones simples en las que podríamos necesitar una interfaz más rica. Uno que conozco es que cada trabajador devuelva artículos que no sean números. Esencialmente, eso significaría usar una lista para los resultados en lugar de una matriz.
----
Names Default To Here( 1 );
// Example for Parallel Assign().
// get a list of a lot of files
dir = If(Host is("Mac"), "$SAMPLE_DATA", "$JMP_HOME");
all files = Files In Directory( dir, "recursive" );
naf = N Items( all files );
// convert relative paths to full paths
For( i = 1, i <= naf, i++,
all files[i] = dir || "/" || all files[i]
);
// Function that looks at a range of a given file list and returns
// the index of the largest file.
// Importantly, this function uses no global variables.
largest file = Function( {files, first, last},
{i, i large = -1, bytes large = -1, bytes},
For( i = first, i <= last, i++,
bytes = File Size( files[i] );
If( bytes > bytes large,
bytes large = bytes;
i large = i;
);
);
i large;
);
// Single-threaded approach: see all the files to the function.
t0 = Tick Seconds();
lf = largest file( all files, 1, naf );
t1 = Tick Seconds();
Show( naf, lf, all files[lf], t1 - t0 );
// about 13sec on my machine (17k files on Win)
wait(1); // so we can see a break in a CPU activity monitor
// Multi-threaded approach: break the list up into 8 ranges that
// can be handled by separate threads.
t0 = Tick Seconds();
num chunks = 8; // JMP may create this many threads, or fewer
// initialize to -1, so we can detect failure if desired
results = J( num chunks, 1, -1 );
Parallel Assign(
// copy these globals into the thread's local variables
{tf = Name Expr( largest file ), tfiles = all files, tn = num chunks},
// make this assignment for each chunk:
results[i] = Local( {n = N Items( tfiles )},
tf( tfiles, 1 + Floor( (i - 1) * n / tn ), Floor( i * n / tn ) )
)
);
// We need to call the function once more to see which of the top
// files is the overall largest.
top files = all files[results];
toplf = largest file( top files, 1, num chunks );
lf = results[toplf];
t1 = Tick Seconds();
Show( naf, lf, all files[lf], t1 - t0 );
// about 4.5sec