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 );私の問題は、このスクリプトを実行するとすべてのコア使用率が 100% に跳ね上がるため、私の Mac システムでは Parallel Assign が機能しているように見えることです。 ただし、終了するのに十分な時間を与えた後 (シングルスレッド バージョンが終了してからずっと後)、強制終了する必要があります。
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 の 2 行目には mat2 の完全なコピーが含まれます。 N Rows(table)/N Threads の単一行を返すことで、これと同じ種類の行列割り当てをエミュレートしようとしました。 これを機能させることができない理由について何か考えている人はいますか?
Re: Parallel Assign を使用したマルチスレッドのトラブルシューティング遅い JSL について再度困惑しています: tDT 値は巨大コピーされる行列巨大tf 関数に渡す回数。8E6 ※8E6の数字がコピーされます。 スレッドはメモリ アクセスを待機しています。
tOutMat[i,j] = tf( tDT, i , j , tSS )
そのコピーは高速 JSL では発生しません。
Re: Parallel Assign を使用したマルチスレッドのトラブルシューティングやあ@msulzer_bsi
一般的な説明: マルチスレッドは、解決しようとしている問題が並列化できない場合、または計算しようとしている問題が非常に小さい場合に、スレッドの生成に単一スレッドでの計算よりも時間がかかる場合には、悪い考えです。
問題を並列実行できる部分に分割できる場合は、そうすることをお勧めしますが、どれだけ並列実行できるか、およびサブ問題の結果を結合するのがどれだけ簡単かによって異なります。 アルゴリズムが新しいスレッド上の新しいサブ問題に分割するために (1 つのスレッドへの) 複数の結合を必要とする場合、結果が単にサブ問題の結果のリストを一度結合すると、可能性が生じます。
あなたの操作にはあまりメリットが見当たりませんし、2 秒で得られる速度の向上も大したことはありません。では、なぜこのような努力をして、他のスレッドで実行される可能性がある/実行された可能性のある他のタスクをブロックする必要があるのでしょうか。 やるべきことのためにさらに 0.5 秒稼ぐ価値がない場合もあります。 2〜3秒で別のデータチャンクを処理する高スループットアプリケーションでない限り、あなたが言ったように8MMを超えるデータのためにこの操作を高速化する十分な理由がわかりません。
Re: Parallel Assign を使用したマルチスレッドのトラブルシューティングやあ@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 );これで、スクリプトを実行できるようになりましたが、奇妙な出力やエラーは発生しません。 並列割り当てを使用して出力行列の各組み合わせを評価することについて説明した内容を再評価しました出力行列の位置の関数として。私の場合、元の行の長いデータ テーブルの最初の 2 列の値の計算のみに興味があるため、Mod() ==1 および Mod() ==2 ステートメントになります。 これは、厳密なテーブル形式の入力要件があるため、最も柔軟なスクリプトではありませんが、それは後ほど問題になります。
しかし、マルチスレッド バージョンのスクリプトを実行すると、実行時間が最大 200 秒かかるという奇妙な現象が発生しています。 並列 assign ステートメントをシングルスレッド スクリプトとして以下のようにネストされた 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 秒かかります... シングルスレッド操作とマルチスレッド操作のプロセスをより均等に比較できるように、不必要ではありますが、入力行列を整形しました。 どのようなオーバーヘッドが発生してマルチスレッド バージョンが大幅に遅くなっているのか理解できません。 最初に質問を投稿したときから行列の計算をかなり削減しました。 何か洞察はありますか?
ありがとう、ムスルザー
Re: Parallel Assign を使用したマルチスレッドのトラブルシューティングこんにちは、クレイジさん
私の質問に答えていただき、本当にありがとうございます。私が Parallel Assign について学んだのは主に、あなたが以前に投稿したいくつかの例によるものであったため、少なくともこのスレッドが表示されることを期待していました。 あなたの発言の核心的な意味を理解して、私の特定のケースに適用できる結果をお知らせします。
Re: Parallel Assign を使用したマルチスレッドのトラブルシューティングParallel Assign ではできないことを期待していると思います。 このコード
tOutMat[i,0] =
Parallel Assign では私は驚きました。 Parallel Assign が期待するのは、これらの添え字の JSL 変数名だけであり、ゼロではありません。 ゼロは静かに無視され、代入の右側では 2 番目のインデックスが何であるかを知る方法がないようです。以下にいくつかの例を示します。 2 番目の添え字をゼロに変更すると、無視されることがわかります。
一。 各スレッドに対してローカルな変数 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 は個別のコピーを作成しないと思います。必要正しく並列実行するには分離する必要があります。
グローバル配列要素への割り当てを使用することもできる場合があります。 適切な長さの 1 次元配列を使用して、並列代入を実行します (括弧内の複数ステートメントの右側の値にも注目してください)。
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 は、不正な結果やクラッシュを引き起こす方法で 2 つのスレッドが何らかの内部構造に同時にアクセスできる可能性のある安全でないコードを作成しないようにします。 Parallel Assign の防御を回避する JSL を作成することは可能です...限界に挑戦している場合は、作業を頻繁に保存してください。