使用 Parallel Assign 進行多線程故障排除我在 Mac 系統上試圖讓多線程處理大型數據表 (>8MM) 的逐行操作。 它們只是簡單的計算,例如除法或按原樣複製值。 我無法附加我的專有數據,但在這篇文章中,我將在 JSL 中包含一個與我的數據集大小相同但包含隨機數據的隨機矩陣。
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 );我的問題是,在我的 Mac 系統上,Parallel Assign 似乎可以正常工作,因為當該腳本運行時,我的所有核心利用率都跳到了 100%。 然而,在給它足夠的時間結束後(在單線程版本結束後很久)我不得不強制退出。
我做了一些嘗試以了解 JSL 如何更喜歡我將一組按行的值“注入”到另一個矩陣中。 舉個例子:
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 現在的第二行將是 mat2 的完整副本。 我試圖通過返回具有 N Rows(table)/N Threads 的單行來模擬這種相同類型的矩陣賦值。 有沒有人對為什麼我不能讓它發揮作用有任何想法?
回复:使用並行分配進行多線程故障排除重新思考緩慢的 JSL:我認為 tDT 值是巨大的被複製的矩陣巨大的傳遞給 tf 函數的次數。8E6 * 8E6 個數字被複製。 線程正在等待內存訪問。
tOutMat[i,j] = tf( tDT, i , j , tSS )
該副本不會在快速 JSL 中發生。
回复:使用並行分配進行多線程故障排除你好@msulzer_bsi
一些一般性陳述:當您嘗試解決的問題不可並行化時,或者當您嘗試計算的問題非常小時,多線程是一個壞主意,以至於生成線程將比在單線程中計算花費更多的時間。
如果您的問題可以分解成可以並行運行的部分,那麼這樣做可能是個好主意,但這取決於有多少部分可以並行運行以及加入子問題結果的難易程度。 如果您的算法需要多個連接(到一個線程)以在新線程上分解為新的子問題,那麼它必須非常計算密集才能值得併行化,如果您的結果另一方面只是一個子問題結果列表加入一次,就有潛力。
我沒有看到您的操作有多大好處,並且您在 2 秒內獲得的速度提升不會太多,所以您為什麼要做出這種努力並阻止可能/已經在其他線程上運行的其他任務。 有時,為您需要做的事情再多花 0.5 秒是不值得的。 除非它是一個高吞吐量的應用程序,你得到所有 2-3s 的另一個數據塊來處理,我沒有看到一個很好的理由來加快這個操作,因為你說的 >8MM 的數據。
回复:使用並行分配進行多線程故障排除你好@Craige_Hales ,
我利用了您在並行分配中創建函數定義的建議。 見下文:
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 );我現在正在執行腳本,不會給我任何奇怪的輸出或拋出錯誤。 我重新評估了你解釋的關於使用並行分配來評估輸出矩陣的每個組合作為輸出矩陣位置的函數。在我的例子中,我只對對原始長行數據表的前兩列中的值執行計算感興趣,因此 Mod() ==1 和 Mod() ==2 語句。 這不是最靈活的腳本,因為有嚴格的表格格式輸入要求,但這是以後的問題。
但是,現在我遇到了一個奇怪的現象,當我執行腳本的多線程版本時,執行時間大約為 200 秒。 當我將並行分配語句重寫為嵌套 for 循環時,如下所示作為單線程腳本:
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];
);
);
);
我可以獲得執行時間~2秒...... 我塑造了輸入矩陣,即使它是不必要的,這樣我就可以更均勻地比較單線程和多線程操作的過程。 我不明白是什麼開銷導致多線程版本變得如此慢。 從我第一次發布問題開始,我已經大大減少了矩陣計算。 你有什麼見解嗎?
謝謝,姆蘇爾澤
回复:使用並行分配進行多線程故障排除嗨克雷奇,
非常感謝您對我的問題的回答;我希望至少您會看到該線程,因為我對 Parallel Assign 的學習主要是由於您之前發布的一些示例。 我將嘗試消化您陳述的核心含義,然後將我可以應用於我的具體案例的任何結果回复給您。
回复:使用並行分配進行多線程故障排除我認為您希望 Parallel Assign 無法做到。 這段代碼
tOutMat[i,0] =
在 Parallel Assign 中讓我感到驚訝; Parallel Assign 唯一期望的是這些下標的 JSL 變量名稱,而不是零。 零似乎被悄悄地忽略了,賦值的右側無法知道第二個索引是什麼。這裡有一些例子;您可以將第二個下標更改為零,然後看到它被忽略了。
一。 使用每個線程的局部變量 i,並在每次使用後遞增它。 j,k(或 j,0)索引值不用於確定為元素計算的值。
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];
二。 相似,以另一種方式顯示同一事物。 每個線程都獲得一個保持不變的唯一值。 h=4 暗示有多少處理器可用。 您不應依賴 JMP 總是如圖所示交錯計算,但現在就是這樣。
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;
三。 使用單個全局變量。 每次使用後增加。 線程相互鎖定,可能會或可能不會以任何特定順序運行。
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;
在上述所有情況下,將 k 替換為 0 不會有任何改變。 在內部,Parallel Assign 仍在計算所有索引對並要求其中一個線程為每一對計算一個值。
如果您繼續探索這條道路,我建議您為每個線程創建單獨的函數實例,例如
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];
這導致每個線程的本地 f 都有一個單獨的 JSL 函數副本。 我認為 nameexpr 不會製作單獨的副本,他們可能需要分開才能正確並行運行。
您也可以使用對全局數組元素的賦值。 使用合適長度的一維數組來驅動並行分配:(還要注意括號中的多語句右側值)
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 );全球:結果=
[ 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;
司機 = [1, 2, 3, 4, 1];
但是全局變量可能會使它運行得像單線程一樣慢。
最後:Parallel Assign 試圖阻止您編寫不安全的代碼,這些代碼可能允許兩個線程同時訪問某些內部結構,從而產生不正確的結果或崩潰。 可以編寫 JSL 來避開 Parallel Assign 的防禦……如果您正在挑戰極限,請經常保存您的工作。