C# AsParallel/Parallel:如何減少合并結果的頻率?
例如,許多迭代的嵌套回圈,充當并行歸約。(或稱其為 map reduce)
// Dictionary size is small and easily contend.
Dictionary<SumDataCategory, SumData> globalResult;
// A loop to be parallelized.
for (int i = 0; i < N; i ) {
for (int j = 0; j < N; j ) {
for (int k = 0; k < N; k ) {
// Pull inputs depending on i,j,k.
InputData[] inputs = fetchInputData(i, j, k);
// Do something independent calculation.
SumData sum1 = SumData.Sum(inputs);
// Finally reduction operation. <- to optimize
globalResult[sum1.Category].Sum(sum1);
}
}
}
目標是獲得唾手可得的成果 - 以較小的努力提高還原性能。(不要重寫整個回圈/處理體)
對作業負載特征的約束:
- 它不是完全規則的:不應該假設靜態磁區。
- 它不是高度動態的:動態作業/作業負載生成是不必要的。
分析:
- 每次迭代減少到全域狀態是一種浪費,并且高速快取爭用。
迭代/輸入比 CPU 內核多得多。 - 手動磁區和處理磁區并非易事。(與普通的 AsParallel/Parallel 或 OpenMP 相比)
因為 C# AsParallel/Parallel 是沒有編譯器幫助的庫(與 OpenMP 或資料并行解決方案不同),大多數臟活必須由用戶完成。 - 即使應用了磁區,將每個塊(磁區資料)減少到全域狀態仍然是一種浪費,而且更復雜。(與最終歸約相比,因為最終歸約可以是串行或并行的,而每塊歸約是并發的)
我想不出一個好的解決方案:
- 如果我使用執行緒本地,我找不到時間來拉執行緒本地并合并它們。
- 如果我對每個執行緒的結果進行手動記賬,我需要一個可靠的執行緒 ID 并提前知道 ID 范圍。(OpenMP 和資料并行解決方案有它們)
作為參考,在 OpenMP 中,我會這樣做:
- 在并行回圈之前,設定或查詢執行緒計數。
- 分配每個執行緒的減少資料。
- 并行回圈,通過執行緒id做執行緒區域縮減。
- 在并行回圈之后,合并執行緒本地導致串行或另一個并行減少(另一個并行回圈)。
uj5u.com熱心網友回復:
您可以先將回圈組合成 3 個元組(i,j,k),然后再運行AsParallel()。要限制并行處理操作的并發任務數量,請使用WithDegreeOfParallelism(x). 因此,您的資料將被磁區,并且每個磁區將被并行處理。要聚合此磁區的結果 - 使用Aggregate函式,但請注意使用為ParallelQuery提供的多載,而不是常規IEnumerable的。簡單求和的示例:
var result = Enumerable.Range(1, 1000).AsParallel()
.WithDegreeOfParallelism(4)
.Aggregate(
0, // initial seed
(acc, value) => acc value, // this is executed for accumulators in each partition, separately, updating partition accumulator
(acc1, acc2) => acc1 acc2, // this is to combine partition accumulators to get final one
(acc) => acc); // this is to convert the final accumulator into final result (if necessary), so what will be returned
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/520362.html
標籤:C#多线程林克并行处理
