Mit fortschreitender Rechenleistung bin ich von 1 Kern auf 2 Kerne auf 4 Kerne ... auf 64 Threads auf meiner Workstation übergegangen. In der Zwischenzeit ist .jsl effektiv Single-Thread geblieben. Ich weiß, dass einige Plattformen alle Threads verwenden, aber ich möchte in der Lage sein, einfache JSL-Operationen nach Belieben an mehrere Threads zu vergeben. Wenn ich zum Beispiel ein Skript habe, das zeilenweise mit 10.000.000 Datenzeilen arbeitet, wenn ich in einer for-Schleife zeilenweise iteriere und weiß, dass das Ergebnis jeder Zeile unabhängig von den anderen Zeilen ist, würde ich das gerne aufteilen Vorgang in 64 asynchrone for-Schleifen, unterbrechen Sie die andere Ausführung, während die 64 Schleifen parallel abgeschlossen werden, und kehren Sie dann zu einem synchronen Skript mit einem einzigen Thread zurück, nachdem alle abgeschlossen sind, ähnlich wie ich es in .py mit Pool() und poolObject.join tun würde.
Hallo Jeff,
Wo finde ich, welche Plattformen/integrierten Aufgaben Multithreading nutzen?
Wie direkt oben erwähnt, geht dieser Wunsch über den Rahmen dessen hinaus, was wir in JSL tun werden. Wenn es bestimmte Vorgänge gibt, von denen Sie glauben, dass sie von Multithreading profitieren würden, öffnen Sie bitte andere Wünsche mit den Details.
Obwohl dieser Wunsch viele Stimmen hat, wird es leider schwierig sein, mehr als das zu liefern, was Parallel Assign() bietet. Aufgrund der engen Integration mit der Benutzeroberfläche und der interaktiven Natur von JMP ist es nicht möglich, eine Thread-sichere Umgebung bereitzustellen, ohne die mögliche JSL auf das zu beschränken, was Parallel Assign im Wesentlichen zulässt.
Der beste Weg, Multithreading zu nutzen, besteht darin, die integrierten Plattformen so oft wie möglich zu verwenden. Auf diesen Plattformen können wir Multithread-Lösungen direkt bereitstellen.Wenn Sie andere Wünsche für integrierte Operationen öffnen, bei denen Sie denken, dass Multithreading nützlich wäre, können Sie uns am besten mitteilen, wo Sie mehr Multithreading sehen möchten.
Für das Auslagern einfacher JSL-Operationen an mehrere Threads gibt es dafür eine Funktion namens Parallel Assign(). Sie teilt die Arbeit in Blöcke auf, wobei der Code für jeden Block eine einzelne Zahl ergibt und das Ergebnis der gesamten Operation ist eine Zahlenmatrix.
Es gibt ein Spielzeugbeispiel im Scripting-Index für Parallel Assign(), und das zweite Beispiel für Frame Size() zeigt ein ausführlicheres Beispiel für die Berechnung von Mandelbrot-Set-Daten. Ein weiteres kleines Beispiel ist in @Craige_Hales 's JSL-Kochbuchbeitrag zu Schleifen und da ist mehr über Mandelbot in seinem Blog.
Ich habe unten ein weiteres Beispiel eingefügt, das die größte Datei in einer Liste von Dateien findet, die Multi-Threading verwenden. Das Skript führt zuerst eine Single-Thread-Version aus, und Sie können beide Aktivitätsschübe in diesem Task-Manager-Screenshot sehen. Die Single-Thread-Version ist die lange Mesa und die parallele Version ist die hohe Spitze. Bei 17.000 Dateien auf 8 Kernen ging die Zeit von 13 s auf 4,5 s oder 3x schneller. Nicht ideal, aber wie so oft teilen sich die Threads einige Ressourcen, wie in diesem Fall den Festplattenzugriff.
Die parallel ausgeführte JSL muss einfach sein, damit sie den gemeinsam genutzten globalen Zustand nicht ändert. Deshalb werden im ersten Schritt einige Globals hineinkopiert. Es bedeutet auch, dass der parallele Code keine Datentabellen erstellen oder mit der Anzeige interagieren kann.
Wir sind daran interessiert zu wissen, welche Art von anderen einfachen Aktionen, bei denen wir möglicherweise eine reichhaltigere Benutzeroberfläche benötigen. Eine, die ich kenne, ist, dass jeder Arbeiter andere Artikel als Nummern zurückgibt. Im Wesentlichen würde das bedeuten, anstelle einer Matrix eine Liste für die Ergebnisse zu verwenden.
----
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