平行演算法考量與數值穩定性 (Algorithm Considerations and Numerical Stability)
重點總覽 (Overview)
| 主題 | 核心訊息 | 對平行化的影響 | 對策 |
|---|---|---|---|
| 加總順序 (summation order) | 浮點加法不滿足結合律 (not associative) | 平行 reduction tree 與循序加總結果可能不同 | presort 升冪後分組;Kahan 補償加總 |
| 小數消失 (swamping) | 大數 + 極小數時,小數對齊右移後整個消失 | 把大小相近的數放同組可減少誤差 | 同 magnitude 分組 / 排序 |
| 數值穩定性 (numerical stability) | 演算法能對任意可解輸入找到解 → stable | 線性解算器對運算順序更敏感 | pivoting 交換 row |
| Gaussian elimination | 每 thread 負責一個 row,逐欄消元 + barrier | 簡單可平行,但 pivoting 需跨 thread 檢查 | shared memory 內做 parallel max reduction |
| Pivoting 成本 | 找 lead 變數係數絕對值最大的 row 來交換 | 跨 block/node 的全域檢查極昂貴 | partial pivoting / randomization (communication-avoiding) |
Precision (精度) 由 mantissa 位數決定(見 24-Numerical-Considerations/01-Floating-Point-Data-Representation);Accuracy (準確度) 由運算與其順序決定。本筆記聚焦後者:相同數值、不同順序,平行與循序可能得到不同答案。
加總順序與平行 Reduction 的準確度 (Summation Order in Parallel Reduction)
矩陣乘法的 dot product、reduction 等都需要把大量數值加總。理想上順序無關(加法符合結合律),但在有限精度下,順序會改變最終準確度。
範例:5-bit 格式 (1-bit S, 2-bit E, 2-bit M)
加總 1.00B×2⁰ + 1.00B×2⁰ + 1.00B×2⁻² + 1.00B×2⁻²(即 1 + 1 + 0.25 + 0.25,精確值 = 2.5 = 1.01B×2¹)。
循序 (sequential, 左到右):
1.00×2⁰ + 1.00×2⁰ = 1.00×2¹ (= 2)
1.00×2¹ + 1.00×2⁻² → 1.00×2¹ (0.25 對齊右移後消失)
1.00×2¹ + 1.00×2⁻² → 1.00×2¹ = 2 (再次消失,誤差 0.5)
平行 (parallel reduction tree, 見第 10 章):
1.00×2⁰ + 1.00×2⁰ = 1.00×2¹ (= 2)
1.00×2⁻² + 1.00×2⁻² = 1.00×2⁻¹ (= 0.5)
1.00×2¹ + 1.00×2⁻¹ = 1.01×2¹ = 2.5 (精確!)
ASCII:兩種加總路徑
循序 (left-to-right) 平行 (reduction tree)
1 1 0.25 0.25 1 1 0.25 0.25
\ / \ / \ /
2 +0.25 → 2 (0.25 消失) 2 0.5
\ \ /
2 +0.25 → 2 (0.25 消失) 2 + 0.5
= 2 (誤差 0.5) = 2.5 (精確)
此例平行更準,但只是巧合。結合律對浮點不成立,所以也能構造平行更不準的情境。不要假設「平行 = 更準」或「循序 = 更準」。第二、三步的小運算元 (0.25) 因 alignment shifting 右移到超出 mantissa 範圍而整個被吞掉 (swamping)。
提升準確度的技巧
| 技巧 | 做法 | 為何有效 |
|---|---|---|
| Presort (升冪排序) | reduction 前依數值升冪排序 (含正負號) | 分組時大小相近的數同組,且循序加總由小到大累積誤差最小 |
| 分組對齊 magnitude | 把絕對值接近的數放同一 group / thread | 避免大數吞掉小數 |
| Kahan / compensated summation | 額外追蹤捨入餘量並補回 | 更穩健的高準確度加總 (Kahan, 1965) |
這是為何排序在大規模平行數值演算法中如此常見的原因之一:排序不只為了輸出,也能讓後續 reduction 更準確。若每個 thread 循序 reduce 自己 group 內的值,升冪排列能讓單 thread 的循序加總獲得更高準確度。
反例(課本習題 4):若陣列已由小到大排序,而你用「相鄰配對」的 reduction 把最小與最大放在同一棵子樹底端配對,反而會讓小值被大值吞掉、降低準確度。排序方向要配合分組策略。
線性解算器與數值穩定性 (Linear Solvers and Numerical Stability)
數值穩定 (numerically stable):只要解存在,演算法對任意輸入都能找到合適的運算順序並求得解。
數值不穩定 (numerically unstable):對某些輸入會找不到解,即使解存在。
線性方程組解算器中,不同的輸入數值可能需要不同的運算順序才能求解 — 比 reduction 的順序問題更嚴重。
Gaussian Elimination 流程
以下系統(解為 X=1, Y=2, Z=3):
3X + 5Y + 2Z = 19
2X + 3Y + Z = 11
X + 2Y + 2Z = 11
矩陣 (augmented) 視角的消元 + 回代:
[ 3 5 2 | 19 ] step1: 各 row 除以 lead 係數,使 X 係數一致
[ 2 3 1 | 11 ] step2: 減去 top row → 消去 X
[ 1 2 2 | 11 ] 重複對 Y ... → upper-triangular(上三角)
→ 對角線化為 identity matrix
→ backward substitution: 由最後一式往回解 Z→Y→X
- 消元後成為 triangular matrix;最終對角線全為 1 即 identity matrix(任意矩陣乘 identity 等於自身)。
- 對矩陣做 Gaussian elimination 等價於乘上其反矩陣 (inverse)。
- 第二階段 backward substitution:由最後一式 (
Z=3) 往前代回求Y=2、X=1。 - 複雜度約 O(n³)。
平行化策略 (CUDA)
每個 thread 負責矩陣的一個 row;若系統可放進 shared memory,用一個 thread block 處理。所有 thread 同步迭代各 step:
// 一 thread 一 row;augmented matrix A 為 n×(n+1),放在 shared memory
__global__ void gaussianElim(float* A, int n) {
int row = threadIdx.x; // 每個 thread 擁有一個 row
for (int k = 0; k < n; ++k) { // 逐欄 k 消元
// ── (pivoting 步驟會插在這裡:找 |A[i][k]| 最大者並 swap row) ──
if (row == k) { // pivot row 正規化:除以 lead 係數
float piv = A[k*(n+1) + k];
for (int j = k; j <= n; ++j) A[k*(n+1)+j] /= piv;
}
__syncthreads(); // barrier:pivot row 已就緒
if (row > k) { // 下方各 row 減去 pivot row → 消去變數
float f = A[row*(n+1) + k];
for (int j = k; j <= n; ++j)
A[row*(n+1)+j] -= f * A[k*(n+1)+j];
}
__syncthreads(); // barrier:更新完成才進下一欄
}
}
- division step 後 barrier,再做 subtraction step,再 barrier。
- 每完成一欄,負責該 row 的 thread 停止參與(其 row 在回代前已無工作),其餘繼續。
- 變數越多,平行化加速越顯著。
數值不穩定:lead 係數為 0
把第一式換成 5Y + 2Z = 16(X 係數 = 0):
0X + 5Y + 2Z = 16 ← step1 需除以 X 係數 0 → 失敗!
2X + 3Y + Z = 11
X + 2Y + 2Z = 11
此系統仍可解 (X=1, Y=2, Z=3),但 naive Gaussian elimination 除以零而失敗 → numerically unstable。
Pivoting:交換 row 求穩定
Pivoting:在剩餘方程式中找出 lead 變數係數不為 0(實務上選絕對值最大)的一式,與目前最上方的式子交換 (swap row),同時對應交換變數 (column)。
[ 0 5 2 | 16 ] lead 係數 = 0 → 無法 divide
[ 2 3 1 | 11 ] 找 |lead| 最大的 row → swap row0 ↔ row1
[ 1 2 2 | 11 ]
↓
[ 2 3 1 | 11 ] lead 係數 = 2 ≠ 0 → 正常進行 Gaussian elimination
[ 0 5 2 | 16 ]
[ 1 2 2 | 11 ]
額外洞見:有些 row 本來就不含當前要消去的變數(如 swap 後的 0X+5Y+2Z),負責該 row 的 thread 不需做 division。kernel 中要對「係數已為 0」做判斷。
Pivoting 的平行成本
| 範圍 | 成本 | 對策 |
|---|---|---|
| 單一 block / shared memory | 係數都在 shared memory,可跑 parallel max reduction | 控制 warp 內 control divergence 即可 |
| 多 block / 多 cluster node | 係數分散,全域檢查極昂貴 | communication-avoiding algorithms (Ballard et al., 2011) |
兩種降低全域檢查成本的途徑:
| 方法 | 概念 | 代價 |
|---|---|---|
| Partial pivoting | 候選 swap 限制在局部方程式集合 | 可能略降數值準確度 |
| Randomization | 隨機化處理 | 研究顯示仍能維持高準確度 |
附錄總結 (Appendix Summary)
| 概念 | 重點 |
|---|---|
| Floating-point format / representable numbers | precision 的基礎(見 sibling 筆記 01 / 02) |
| Denormalized numbers | 對許多數值應用很重要;早期 CUDA 不支援,近代硬體已支援 |
| Arithmetic accuracy | 快速算術(special function units)可能較低準確度,需留意 |
| 平行演算法準確度 | 平行常改變計算準確度;排序等技巧可改善 |
考試/面試重點 (Exam / Test Patterns)
| 情境 / 關鍵字 | 答案 / 技巧 |
|---|---|
| 「平行 reduction 與循序加總結果不同」 | 浮點加法不符結合律;(a+b)+c ≠ a+(b+c),屬正常現象 |
| 「大數加極小數,小數不見了」 | alignment shifting / swamping:小運算元右移超出 mantissa 範圍被吞掉 |
| 「如何提升 reduction 準確度」 | presort 升冪 + 大小相近分組;進階用 Kahan compensated summation |
| 「已排序陣列用相鄰配對 reduction 反而變差」 | 分組把最小最大配對 → 小值被吞;排序方向要配合分組(課本習題 4) |
| 「numerically stable 定義」 | 解存在時對任意輸入都能找到運算順序求解 |
| 「Gaussian elimination 為何失敗 / 不穩定」 | lead 係數為 0 無法 divide,即使解存在仍失敗 |
| 「如何修正 → pivoting」 | 找 lead 係數絕對值最大的 row,swap 到最上方 |
| 「pivoting 在多 block/cluster 為何慢」 | 需全域檢查分散的係數 → 用 partial pivoting / randomization (communication-avoiding) |
| 「CUDA Gaussian elimination 結構」 | 一 thread 一 row;division → barrier → subtraction → barrier;消完的 thread 退出 |
| 「max ULP error of round-to-zero add」(習題 3) | round-to-zero(truncation)最大誤差約 1 ULP(非 0.5 ULP) |
Related Notes
- 24-Numerical-Considerations/01-Floating-Point-Data-Representation
- 24-Numerical-Considerations/02-Representable-Numbers-Precision-and-Accuracy
- 10-Reduction/01-Reduction-Fundamentals-and-Simple-Kernel
- 13-Sorting/01-Sorting-Foundations-and-Parallel-Radix-Sort
- 03-Multidimensional-Grids-And-Data/04-Matrix-Multiplication-Kernel
- 19-Parallel-Programming-And-Computational-Thinking/02-Algorithm-Selection-and-Tradeoffs