Fehlerbehebung bei Multithreading mithilfe von Parallel AssignIch arbeite auf einem Mac-System und versuche, Multithreading für eine große Datentabelle (>8 MM) bei zeilenweisen Operationen zum Laufen zu bringen. Es handelt sich lediglich um einfache Berechnungen wie Division oder das Kopieren von Werten in ihrer ursprünglichen Form. Ich kann meine proprietären Daten nicht anhängen, aber in diesem Beitrag füge ich in die JSL eine Zufallsmatrix ein, die dieselbe Größe wie mein Datensatz hat, aber Zufallsdaten enthält.
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 );Mein Problem ist, dass es auf meinem Mac-System so aussieht, als ob die parallele Zuweisung funktioniert, da alle meine Kernauslastungen auf 100 % steigen, wenn dieses Skript ausgeführt wird. Nachdem ich jedoch genügend Zeit zum Beenden gegeben habe (lange nach der Single-Thread-Version), muss ich das Beenden erzwingen.
Ich habe ein wenig herumgespielt, um zu verstehen, wie JSL es vorzieht, dass ich einen zeilenweisen Satz von Werten in eine andere Matrix „injiziere“. Nehmen Sie das folgende Beispiel:
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);
Die zweite Zeile von mat1 ist nun eine vollständige Kopie von mat2. Ich habe versucht, dieselbe Art von Matrixzuweisung zu emulieren, indem ich eine einzelne Zeile mit N Zeilen (Tabelle)/N Threads zurückgegeben habe. Hat jemand eine Idee, warum ich das nicht zum Laufen bekomme?
Betreff: Multithreading-Fehlerbehebung mit Parallel Assignnoch einmal über die langsame JSL rätseln: Ich denke, der tDT-Wert ist a riesig Matrix, die kopiert wird a riesig Anzahl der Übergaben an die tf-Funktion.8E6 * 8E6-Nummern werden kopiert. Die Threads warten auf Speicherzugriff.
tOutMat[i,j] = tf( tDT, i , j , tSS )
Diese Kopie findet im schnellen JSL nicht statt.
Betreff: Multithreading-Fehlerbehebung mit Parallel AssignHallo @msulzer_bsi
Eine allgemeine Aussage: Multithreading ist eine schlechte Idee, wenn das Problem, das Sie lösen möchten, nicht parallelisierbar ist oder wenn das Problem, das Sie berechnen möchten, so klein ist, dass das Erzeugen von Threads mehr Zeit kostet als die Berechnung in einem einzelnen Thread.
Wenn Ihr Problem in Teile unterteilt werden kann, die parallel ausgeführt werden können, ist dies möglicherweise eine gute Idee. Dies hängt jedoch davon ab, wie viel parallel ausgeführt werden kann und wie einfach es ist, die Ergebnisse der Teilprobleme zusammenzuführen. Wenn Ihr Algorithmus mehrere Verknüpfungen (zu einem Thread) erfordern würde, um in neue Unterprobleme in neuen Threads aufzubrechen, muss er sehr rechenintensiv sein, damit sich der Aufwand für die Parallelisierung lohnt, wenn Ihr Ergebnis hingegen nur ein ist Liste der Teilproblemergebnisse einmal zusammengefügt, dann besteht Potenzial.
Ich sehe keinen großen Nutzen in Ihrem Betrieb und die Geschwindigkeitsverbesserung, die Sie über 2 Sekunden erzielen würden, könnte nicht groß sein. Warum wollen Sie sich also diese Mühe machen und mit htis andere Aufgaben blockieren, die möglicherweise in den anderen Threads ausgeführt werden könnten/hätten? Manchmal lohnt es sich nicht, weitere 0,5 Sekunden für das zu gewinnen, was Sie tun müssen. Sofern es sich nicht um eine Anwendung mit hohem Durchsatz handelt, bei der alle 2-3 weiteren Datenblöcke verarbeitet werden müssen, sehe ich keinen guten Grund, diesen Vorgang zu beschleunigen, da die Daten größer als 8 Millionen sind, wie Sie sagten.
Betreff: Multithreading-Fehlerbehebung mit Parallel AssignHallo @Craige_Hales ,
Ich habe Ihren Rat befolgt, eine Funktionsdefinition innerhalb der parallelen Zuweisung zu erstellen. Siehe unten:
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 );Ich erhalte jetzt die Ausführung des Skripts und erhalte keine seltsame Ausgabe oder einen Fehler. Ich habe das, was Sie über die Verwendung der parallelen Zuweisung zur Auswertung jeder Kombination der Ausgabematrix erklärt haben, noch einmal bewertet als Funktion der Position der Ausgabematrix.In meinem Fall bin ich nur daran interessiert, Berechnungen für die Werte in den ersten beiden Spalten der ursprünglichen Datentabelle mit langen Zeilen durchzuführen, daher die Anweisungen Mod() ==1 und Mod() ==2. Aufgrund der strengen Eingabeanforderungen für die Tabellenformatierung ist dies nicht das flexibelste Skript, aber das ist ein Problem für später.
Jetzt tritt jedoch ein seltsames Phänomen auf, bei dem ich beim Ausführen der Multithread-Version des Skripts Ausführungszeiten von ca. 200 Sekunden erhalte. Wenn ich die parallele Zuweisungsanweisung so umschreibe, dass sie eine verschachtelte for-Schleife ist, wie unten dargestellt, als Singlethread-Skript:
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];
);
);
);
Ich kann Ausführungszeiten von ~2 Sekunden erreichen ... Ich habe die Eingabematrix so gestaltet, dass sie nicht erforderlich ist, damit ich den Prozess von Singlethread- und Multithread-Operationen gleichmäßiger vergleichen kann. Ich kann nicht verstehen, was für ein Overhead entsteht, der dazu führt, dass die Multithread-Version so viel langsamer wird. Ich habe die Matrixberechnung seit dem ersten Posten meiner Frage erheblich gekürzt. Haben Sie irgendwelche Erkenntnisse?
Danke, Msulzer
Betreff: Multithreading-Fehlerbehebung mit Parallel AssignHallo Craige,
Vielen Dank für Ihre Antwort auf meine Frage; Ich hatte gehofft, dass zumindest Sie den Thread sehen würden, da mein Wissen über Parallel Assign größtenteils auf einige Beispiele zurückzuführen ist, die Sie zuvor gepostet haben. Ich werde versuchen, die Kernbedeutung Ihrer Aussagen zu verstehen und mich mit allen Ergebnissen, die ich auf meinen konkreten Fall anwenden kann, bei Ihnen melden.
Betreff: Multithreading-Fehlerbehebung mit Parallel AssignIch denke, Sie hoffen auf etwas, das Parallel Assign nicht kann. Dieser Code
tOutMat[i,0] =
im Parallel Assign hat mich überrascht; Das Einzige, was Parallel Assign erwartet, sind JSL-Variablennamen für diese Indizes, keine Null. Es scheint, dass die Null stillschweigend ignoriert wird und die rechte Seite der Zuweisung keine Möglichkeit hat, den zweiten Index zu ermitteln.Hier sind einige Beispiele; Sie können den zweiten Index auf Null ändern und sehen, dass er ignoriert wird.
Eins. Verwenden Sie eine Variable i, die für jeden Thread lokal ist, und erhöhen Sie sie nach jeder Verwendung. Die Indexwerte j,k (oder j,0) werden nicht zur Bestimmung des für das Element berechneten Werts verwendet.
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];
Zwei. Ähnlich, zeigt das Gleiche anders. Die Threads erhalten jeweils einen eindeutigen Wert, der unverändert bleibt. h=4 gibt an, wie viele Prozessoren verfügbar sind. Sie sollten sich nicht darauf verlassen, dass JMP die Berechnungen immer wie gezeigt verschachtelt, aber im Moment ist es so.
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;
Drei. Verwenden Sie eine einzelne globale Variable. Nach jedem Gebrauch erhöhen. Die Threads sperren sich gegenseitig und können in einer bestimmten Reihenfolge ausgeführt werden oder auch nicht.
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;
In allen oben genannten Fällen ändert das Ersetzen von k durch 0 nichts. Intern berechnet Parallel Assign weiterhin alle Indexpaare und fordert einen der Threads auf, für jedes Paar einen Wert zu berechnen.
Wenn Sie diesen Weg weiter erkunden, würde ich vorschlagen, für jeden Thread separate Instanzen Ihrer Funktion zu erstellen, so etwas wie
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];
Dies führt dazu, dass das lokale f für jeden Thread eine separate Kopie der JSL-Funktion hat. Ich denke, dass der Nameexpr keine separaten Kopien erstellt, und das kann passieren brauchen getrennt sein, um korrekt parallel zu laufen.
Möglicherweise können Sie auch eine Zuweisung zu einem globalen Array-Element verwenden. Verwenden Sie ein eindimensionales Array mit der richtigen Länge, um die parallele Zuweisung zu steuern: (Beachten Sie auch den Wert auf der rechten Seite mit mehreren Anweisungen in Klammern.)
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 );global:results =
[ 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;
Treiber = [1, 2, 3, 4, 1];
Aber die globalen Variablen werden wahrscheinlich dafür sorgen, dass es genauso langsam läuft wie Single-Threaded.
Und schließlich: Parallel Assign versucht, Sie davon abzuhalten, unsicheren Code zu schreiben, der es zwei Threads ermöglichen könnte, gleichzeitig auf eine interne Struktur zuzugreifen, was entweder zu falschen Ergebnissen oder einem Absturz führt. Es ist möglich, JSL zu schreiben, das den Abwehrmaßnahmen von Parallel Assign entgeht. Speichern Sie Ihre Arbeit häufig, wenn Sie bis an die Grenzen gehen.