Au fur et à mesure que l'informatique a progressé, je suis passé de 1 cœur à 2 cœurs à 4 cœurs... à 64 threads sur mon poste de travail. Pendant ce temps, .jsl est effectivement resté à thread unique. Je sais que certaines plates-formes utiliseront tous les threads, mais j'aimerais pouvoir gérer à volonté des opérations jsl simples sur plusieurs threads. Par exemple, si j'ai un script qui fonctionne par ligne sur 10 000 000 lignes de données, si j'itére par ligne dans une boucle for et que je sais que le résultat de chaque ligne est indépendant des autres lignes, j'aimerais diviser cela opération en 64 boucles for asynchrones, interrompez les autres exécutions pendant que les 64 boucles se terminent en parallèle, puis revenez à un seul script synchrone à thread une fois que tout est terminé, un peu comme je le ferais dans .py avec Pool() et poolObject.join.
Salut Jef,
Où puis-je trouver quelles plates-formes/tâches intégrées tirent parti du multithreading ?
Comme indiqué directement ci-dessus, ce souhait dépasse le cadre de ce que nous allons faire dans JSL. S'il y a des opérations spécifiques qui, selon vous, pourraient bénéficier du multithreading, veuillez ouvrir d'autres souhaits avec les détails.
Bien que ce souhait ait de nombreux votes, malheureusement rien de plus que ce qui est offert par Parallel Assign() sera difficile à réaliser. En raison de l'étroite intégration avec l'interface utilisateur et de la nature interactive de JMP, il n'est pas possible de fournir un environnement thread-safe sans limiter le JSL possible essentiellement à ce que Parallel Assign permet.
La meilleure façon de tirer parti du multithreading est d'utiliser autant que possible les plates-formes intégrées. C'est sur ces plates-formes que nous pouvons fournir directement des solutions multithread.Ouvrir d'autres souhaits pour les opérations intégrées où vous pensez que le multithreading serait utile est le meilleur moyen de nous faire savoir où vous aimeriez voir plus de multithreading.
En ce qui concerne l'exploitation d'opérations JSL simples sur plusieurs threads, il existe une fonction appelée Parallel Assign () pour cela. Elle divise le travail en morceaux où le code de chaque morceau donne un seul nombre, et le résultat de l'opération entière est une matrice de nombres.
Il y a un exemple jouet dans l'index de script pour Parallel Assign(), et le deuxième exemple pour Frame Size() montre un exemple plus élaboré pour calculer les données de l'ensemble de Mandelbrot. Un autre petit exemple est dans @Craige_Hales c'est JSL Cookbook post sur les boucles et il y a En savoir plus sur Mandelbot dans son blog.
J'ai inclus un autre exemple ci-dessous qui trouve le plus gros fichier dans une liste de fichiers utilisant le multi-threading. Le script exécute d'abord une version à thread unique, et vous pouvez voir les deux poussées d'activité dans cette capture d'écran du gestionnaire de tâches. La version à un seul fil est la longue mesa et la version parallèle est la grande crête. Pour 17 000 fichiers sur 8 cœurs, le temps est passé de 13s à 4,5s, soit 3x plus rapide. Pas idéal, mais comme c'est souvent le cas, les threads partagent certaines ressources, comme l'accès au disque dans ce cas.
Le JSL qui s'exécute en parallèle doit être simple pour ne pas modifier l'état global partagé. C'est pourquoi certaines variables globales sont copiées dans un premier temps. Cela signifie également que le code parallèle ne peut pas faire des choses comme créer des tables de données ou interagir avec l'affichage.
Nous sommes intéressés à savoir quel genre d'autres actions simples pour lesquelles nous pourrions avoir besoin d'une interface plus riche. L'une que je connaisse est que chaque travailleur renvoie des éléments autres que des numéros. Essentiellement, cela signifierait utiliser une liste pour les résultats au lieu d'une matrice.
----
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