Risoluzione dei problemi di multithreading utilizzando l'assegnazione parallelaSono su un sistema Mac cercando di far funzionare il multithreading su un grande datatable (> 8MM) su operazioni per riga. Sono solo semplici calcoli come la divisione o la copia di valori così come sono. Non posso allegare i miei dati proprietari, ma in questo post includerò nel JSL una matrice casuale delle stesse dimensioni del mio set di dati ma con dati casuali.
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 );Il mio problema è che sul mio sistema Mac sembra che l'assegnazione parallela funzioni perché tutti i miei utilizzi principali passano al 100% quando viene eseguito questo script. Tuttavia, dopo avergli concesso un tempo sufficiente per terminare (molto tempo dopo la versione a thread singolo), devo forzare l'uscita.
Ho giocato un po 'per capire come JSL preferisca che io "inietti" un insieme di valori per riga in un'altra matrice. Prendi l'esempio qui sotto:
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 ora avrà la sua seconda riga una copia completa di mat2. Ho provato a emulare questo stesso tipo di assegnazione di matrice restituendo una singola riga con N righe (tabella)/N thread. Qualcuno ha qualche idea sul motivo per cui non riesco a farlo funzionare?
Ri: Risoluzione dei problemi relativi al multithreading utilizzando l'assegnazione parallelare-sconcertante sul lento JSL: penso che il valore tDT sia a Enorme matrice che viene copiata a Enorme numero di volte da passare alla funzione tf.I numeri 8E6 * 8E6 vengono copiati. I thread sono in attesa dell'accesso alla memoria.
tOutMat[i,j] = tf( tDT, i , j , tSS )
Quella copia non avviene nel veloce JSL.
Ri: Risoluzione dei problemi relativi al multithreading utilizzando l'assegnazione parallelaCIAO @msulzer_bsi
Qualche affermazione generale: il multithreading è una cattiva idea quando il problema che stai cercando di risolvere non è parallelizzabile o quando il problema che stai cercando di calcolare è così piccolo che la generazione di thread richiederà più tempo rispetto all'elaborazione in un singolo thread.
Se il tuo problema può essere suddiviso in parti che possono essere eseguite in parallelo, potrebbe essere una buona idea farlo, ma dipende da quanto può essere eseguito in parallelo e quanto è facile unire i risultati del sottoproblema. Se il tuo algoritmo richiederebbe più join (a un thread) per suddividersi in nuovi sottoproblemi su nuovi thread, deve essere molto intensivo di calcolo per valere la pena di parallelizzare, se il tuo risultato, d'altra parte, è solo un l'elenco dei risultati del sottoproblema è stato unito una volta, quindi esiste il potenziale.
Non vedo molti vantaggi nella tua operazione e il miglioramento della velocità che otterresti in 2 secondi potrebbe non essere molto, quindi perché vuoi fare questo sforzo e bloccare con htis altre attività che potrebbero/essere state eseguite sugli altri thread. A volte non vale la pena guadagnare altri 0,5 per quello che devi fare. A meno che non si tratti di un'applicazione ad alto throughput in cui si ottengono tutti i 2-3 altri blocchi di dati da elaborare, non vedo un buon motivo per accelerare questa operazione a causa di dati di> 8 mm come hai detto.
Ri: Risoluzione dei problemi relativi al multithreading utilizzando l'assegnazione parallelaCIAO @Craige_Hales ,
Ho utilizzato il tuo consiglio di creare una definizione di funzione all'interno dell'assegnazione parallela. Vedi sotto:
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 );Ora sto facendo eseguire lo script e non mi dà alcun output strano o genera un errore. Ho rivalutato ciò che hai spiegato sull'uso dell'assegnazione parallela per valutare ogni combinazione della matrice di output in funzione della posizione della matrice di output.Nel mio caso, sono interessato solo a eseguire calcoli sui valori nelle prime due colonne della tabella di dati originale a righe lunghe, quindi le istruzioni Mod() ==1 e Mod() ==2. Questo non è lo script più flessibile, a causa dei severi requisiti di input per la formattazione della tabella, ma questo è un problema per dopo.
Tuttavia, ora sto riscontrando uno strano fenomeno in cui quando eseguo la versione multithread dello script ottengo tempi di esecuzione di circa 200 secondi. Quando riscrivo l'istruzione di assegnazione parallela in modo che sia un ciclo for nidificato come di seguito come uno script a thread singolo:
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];
);
);
);
Posso ottenere tempi di esecuzione ~ 2 secondi ... Ho modellato la matrice di input anche se non è necessaria in modo da poter confrontare in modo più uniforme il processo delle operazioni a thread singolo rispetto a quelle multithread. Non riesco a capire quale tipo di sovraccarico stia accadendo che stia causando una versione multithread molto più lenta. Ho ridotto un po 'il calcolo della matrice da quando ho pubblicato la mia domanda per la prima volta. Hai qualche intuizione?
Grazie, msulzer
Ri: Risoluzione dei problemi relativi al multithreading utilizzando l'assegnazione parallelaCiao Craig,
Grazie mille per la tua risposta alla mia domanda; Speravo che almeno avresti visto il thread perché il mio apprendimento su Parallel Assign era dovuto in gran parte ad alcuni esempi che hai pubblicato in precedenza. Cercherò di digerire qual è il significato fondamentale delle tue affermazioni e ti ricontatterò con tutti i risultati che posso applicare al mio caso specifico.
Ri: Risoluzione dei problemi relativi al multithreading utilizzando l'assegnazione parallelaPenso che speri in qualcosa che Parallel Assign non può fare. Questo codice
tOutMat[i,0] =
in Parallel Assign mi ha sorpreso; l'unica cosa che Parallel Assign si aspetta sono i nomi delle variabili JSL per quei pedici, non uno zero. Sembra che lo zero sia tranquillamente ignorato e il lato destro dell'assegnazione non ha modo di sapere qual è il secondo indice.Ecco alcuni esempi; puoi cambiare il secondo pedice a zero e vedere che viene ignorato.
Uno. Usa una variabile i, locale per ogni thread, e incrementala dopo ogni utilizzo. I valori dell'indice j,k (o j,0) non vengono utilizzati per determinare il valore calcolato per l'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];
Due. Simile, mostra la stessa cosa in un altro modo. Ciascun thread ottiene un valore univoco che rimane invariato. h=4 indica quanti processori sono disponibili. Non dovresti dipendere da JMP che intercala sempre i calcoli come mostrato, ma per ora è così.
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;
Tre. Usa una singola variabile globale. Aumentare dopo ogni utilizzo. I thread si bloccano a vicenda e potrebbero o meno essere eseguiti in un ordine particolare.
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 tutti i casi precedenti, la sostituzione di k con 0 non cambia nulla. Internamente, Parallel Assign sta ancora calcolando tutte le coppie di indici e chiedendo a uno dei thread di calcolare un valore per ogni coppia.
Se continui a esplorare questo percorso, ti suggerisco di creare istanze separate della tua funzione per ogni thread, qualcosa del genere
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];
che fa sì che la f locale per ogni thread abbia una copia separata della funzione JSL. Penso che nameexpr non faccia copie separate, e potrebbero Bisogno essere separati per funzionare correttamente in parallelo.
Potresti anche essere in grado di utilizzare un'assegnazione a un elemento di matrice globale. Usa un array unidimensionale della giusta lunghezza per guidare l'assegnazione parallela: (nota anche il valore multi-istruzione a destra tra parentesi)
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 );globale:risultati =
[ 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;
conducente = [1, 2, 3, 4, 1];
Ma le variabili globali probabilmente lo faranno funzionare lento come un singolo thread.
E infine: Parallel Assign cerca di impedirti di scrivere codice non sicuro che potrebbe consentire a due thread di accedere a una struttura interna, contemporaneamente, in un modo che produrrà risultati errati o un arresto anomalo. È possibile scrivere JSL che eluda le difese di Parallel Assign... salva spesso il tuo lavoro se ti stai spingendo oltre.