問題分解:Output-centric 與 Input-centric (Problem Decomposition)
重點總覽 (Overview)
問題分解 (problem decomposition) 是平行程式三步驟(algorithm selection → problem decomposition → optimization/tuning)的第二步:把問題拆成可同時安全求解的子問題,關鍵是決定每個平行執行單元要做哪份工作。最常見的兩種策略:
| 策略 | thread 對應到 | 存取模式 | 累積位置 | atomics? | GPU 偏好 |
|---|---|---|---|---|---|
| Output-centric | 一個 output 元素 | Gather(收集多個 input) | private register | 否 | 通常較佳 |
| Input-centric | 一個 input 元素 | Scatter(散佈到多個 output) | global memory(眾人可寫) | 是 | 通常較差 |
兩種分解結果相同,但在特定硬體上效能可能天差地別。選擇不能只看 gather/scatter,還要綜合:平行度 (parallelism)、input→output 映射難易、load balance。
兩種分解策略 (Output-centric vs Input-centric)
- Output-centric:thread 各自處理不同的輸出單元 → 每個 thread gather/collect 多個 input 對該 output 的貢獻。
- Input-centric:thread 各自處理不同的輸入單元 → 每個 thread scatter/distribute 一個 input 的影響到多個 output。
(A) OUTPUT-CENTRIC = GATHER (B) INPUT-CENTRIC = SCATTER
in0 in1 in2 in3 in0 in1 in2 T1=in0 T2=in1
\ \ | / \ | / / | \ \ / | \ \
\ \ | / \ | / v v v v v v v v
Thread1 Thread2 out0 out1 ... out0 out1 ...
(out0) (out1) ^----同一 out 多人寫----^
| | 需 atomicAdd 防 race condition
out[0] <-register acc-> (多個 thread 同時更新同一格)
單一寫者,免 atomics 慢:atomics << register 存取
Gather access(output-centric 的優勢)
- 結果可累積在 private register(不需寫回 global 再讀)。
- 多個 thread 共享相同 input,可有效利用 constant memory caching 或 shared memory 省 global memory bandwidth。
- 每格輸出只有單一寫者 → 不需 atomics。
Scatter access(input-centric 的代價)
- 多個 thread 可能同時更新同一 output 格。
- output 必須放在所有 thread 都能寫的記憶體。
- 必須用 atomic operations 防 race condition / 寫入遺失;而 atomics 遠慢於 output-centric 用的 register 存取。
// Output-centric (gather): 一個 thread 對應一個 OUTPUT
__global__ void gatherKernel(float* out, const float* in) {
int o = blockIdx.x * blockDim.x + threadIdx.x; // output 索引
if (o < N_OUT) {
float acc = 0.0f; // 累積在 register
for (int k = 0; k < numInputsFor(o); ++k) // 掃過貢獻的 inputs
acc += contribution(in[inputOf(o, k)], o);
out[o] = acc; // 單一寫者 -> 免 atomics
}
}
// Input-centric (scatter): 一個 thread 對應一個 INPUT
__global__ void scatterKernel(float* out, const float* in) {
int i = blockIdx.x * blockDim.x + threadIdx.x; // input 索引
if (i < N_IN) {
for (int k = 0; k < numOutputsFor(i); ++k) // 掃過受影響的 outputs
atomicAdd(&out[outputOf(i, k)], // 多人寫 -> 必須 atomics
contribution(in[i], k));
}
}
選擇分解策略的考量 (Decision Criteria)
除了 gather vs scatter(atomics),還要看:
| 考量 | 說明 | 偏好 |
|---|---|---|
| Gather vs Scatter | scatter 需慢速 atomics | 多偏 gather (output-centric) |
| 平行度 (parallelism exposed) | 哪邊的元素數量多、能餵飽更多 thread | 元素數較多的那邊 |
| 映射難易 (input→output mapping) | thread 是否容易知道自己負責哪些對應元素 | 容易識別者 |
| Load balance | 每個 thread 工作量是否平均 | 工作量均勻者 |
「output-centric 永遠較好」是錯誤簡化。當輸出元素太少(平行度不足)、或輸出端工作量嚴重不均(load imbalance)、或 mapping 難以判定時,input-centric 反而較佳(histogram、SpMV/COO 即是)。最佳分解常取決於 input dataset。
當 update 是 idempotent(重複寫同值無害,如 BFS 的 level 設定)時,scatter 不需要 atomics,此時 gather/scatter 不再是考量點,只剩平行度與 load balance 決定勝負(graph traversal 即是)。
全書平行模式分解策略巡禮 (Survey Across the Book)
| 計算 / 章節 | 分解 | 存取 | 為何如此選擇 |
|---|---|---|---|
| Image / MatMul / Convolution / Stencil (Ch3,5,6,7,8) | Output-centric | Gather | thread 對應 output(pixel/矩陣元素/grid 點)並掃 input;輸出元素夠多(平行度足)、mapping 簡單、各輸出工作量相同(無 load imbalance)→ gather 免 atomics 全勝 |
| Histogram (Ch9) | Input-centric | Scatter (atomics) | output-centric 會:①bin 數 << input 數(平行度大減)②thread 不掃過全部 input 就不知哪些屬於它(不 work-efficient)③各 bin input 數不同(load imbalance)→ 三點皆不利,故選 input-centric |
| Merge (Ch12) | Output-centric | (gather 非主因) | 每個 output 只由一個 input 貢獻,input-centric 其實也不需 atomics;但 input-centric 要嘛使 input→output 映射昂貴,要嘛造成高 load imbalance,故仍選 output-centric |
| SpMV/COO (Ch14) | Input-centric | Scatter (atomics) | thread 對應 nonzero、atomically 更新 output vector;雖用 atomics,卻能抽取更多平行度且 load balance 更好 |
| SpMV 其他 kernel (Ch14) | Output-centric | Gather | thread 對應 output vector 元素並掃 nonzeros |
| Hybrid ELL-COO (Ch14) | 混合 | mixed | output-centric + input-centric 混合分解的範例;最佳取決於資料集 |
| Graph: vertex push / edge-centric (Ch15) | Input-centric | (idempotent, 免 atomics) | thread 對應 vertex/edge,更新鄰居 level;scatter/gather 非考量,由平行度與 load balance 決定,最佳依資料集 |
| Graph: vertex pull (Ch15) | Output-centric | (idempotent) | thread 只更新自己負責的 vertex level |
| MRI FhD (Ch17) | Output-centric | Gather | 每個 thread 用 gather 算一個 FhD 元素;避免 atomics;每個 k-space sample 被重複使用多次 |
| Electrostatic potential (Ch18) | Output-centric (grid-centric) | Gather | grid-centric(一 thread 算所有 atom 對一 grid 點的貢獻)優於 atom-centric(input-centric);gather 較佳、平行度足夠 |
Electrostatic potential 的兩個困難如何被克服(output-centric/grid-centric 的代價):
- input→output 映射難(grid 點不知哪些 atom 在 cutoff 內)→ 用 binning 解決。
- binning 造成 load imbalance(每 bin atom 數不同)→ 用 SmallBin-Overlap 實作(把序列式 overflow 處理與下個 kernel 重疊)解決。
關鍵量化 (Key Quantities)
| 量 | Output-centric | Input-centric |
|---|---|---|
| 平行度 (#threads) | ≈ #output 元素 | ≈ #input 元素 |
| Histogram 工作複雜度 | O(#bins × #inputs)(每 thread 須掃全部 input → 不 work-efficient) | O(#inputs)(每 input 處理一次 → work-efficient) |
| 同步成本 | register 累積,免 atomics | atomicAdd(慢) |
Histogram 的 output-centric 之所以不可行,正是因為其工作量為 O(#bins × #inputs) 而非 O(#inputs):缺少快速「哪些 input 屬於此 bin」的索引,每個 bin thread 都得掃過所有 input。
考試/面試重點 (Exam / Test Patterns)
| 情境 / 關鍵字 | 答案 / 技巧 |
|---|---|
| "gather access pattern" | Output-centric;每 thread 收集多個 input 成一個 output,累積在 register,可用 constant/shared memory 共享 input,免 atomics(GPU 偏好) |
| "scatter access pattern" | Input-centric;每 thread 把一個 input 散佈更新到多個 output,需 atomic operations 防 race condition,較慢 |
| 為何 GPU 通常偏好 output-centric | 結果存 private register、可共享 input 省 bandwidth、避免慢的 atomics |
| Histogram 為何用 input-centric | output-centric 三大缺點:平行度低(bin 少)、不 work-efficient(O(bins×inputs))、load imbalance |
| Merge 為何 output-centric(但 gather 非理由) | 每 output 只來自一個 input,input-centric 也免 atomics;但 input-centric 會 mapping 貴 或 load imbalance |
| SpMV/COO 用 input-centric 的代價/好處 | 代價=atomics;好處=更高平行度 + 更好 load balance;最佳依資料集 |
| Graph BFS push vs pull | push/edge = input-centric,pull = output-centric;level 更新 idempotent → 免 atomics,故看平行度與 load balance |
| 選分解策略要考量哪些面向 | ①gather vs scatter (atomics) ②平行度 ③input→output 映射難易 ④load balance |
| Hybrid ELL-COO 代表什麼 | output-centric 與 input-centric 的混合分解 |
| EP grid-centric 兩困難如何解 | mapping 難 → binning;load imbalance → SmallBin-Overlap |
| 兩種分解結果是否相同 | 相同結果,但效能可能差很多;非單一策略恆優 |
Related Notes
- 19-Parallel-Programming-And-Computational-Thinking/02-Algorithm-Selection-and-Tradeoffs
- 19-Parallel-Programming-And-Computational-Thinking/04-Computational-Thinking-and-Parallelization-Approaches
- 18-Electrostatic-Potential-Map/01-DCS-Scatter-vs-Gather
- 17-Iterative-MRI-Reconstruction/02-FHD-Kernel-Parallelism-Structure
- 09-Parallel-Histogram/01-Atomic-Operations-and-Basic-Histogram
- 15-Graph-Traversal/02-Vertex-Centric-and-Edge-Centric-BFS