算術強度(arithmetic intensity)
想像把雜貨搬上好幾層樓梯。累人的部分是爬樓梯(搬袋子);到了之後下廚相對快。若每趟上樓能讓你煮一整桌大餐,這趟爬就值得;若每趟只夠你煮一顆水煮蛋,你就把人生耗在樓梯上。算術強度衡量的正是程式的這個比值:你每從記憶體搬上一單位的資料,能做多少有用的算術。高強度代表每趟爬都煮很多;低強度代表爬很多卻煮很少。
精確說,算術強度是一個運算所做的算術運算次數,除以它與主記憶體之間傳輸的位元組數——每位元組的運算數。把兩個陣列相加,c[i] = a[i] + b[i],讀 8 位元組、寫 4 個,只做一個加法:約為每 12 位元組 1 個運算,強度很低。相對地,稠密矩陣乘法能在一個值留在快速暫存器或晶片上記憶體被重用時做數千次乘加,把它的強度推高。同一個演算法也能藉由重用載入的資料(快取分塊、tiling)變得更密集,讓每趟昂貴的記憶體往返餵養多得多的算術。
算術強度是決定你住在 Roofline 哪一邊的單一數字,這就是為什麼它是 GPU 與向量效能誠實的核心。低強度代表受記憶體所限:晶片的算術單元閒坐等資料,更快或更寬的算術單元買不到東西——只有更多頻寬或更多資料重用才有幫助。高強度代表受運算所限:資料來得夠快、能讓算術單元保持忙碌,此時純算術吞吐量才是值得追的限制。給初學者的實務教訓違反直覺卻至關重要:吞吐量硬體上大多數真實的加速,並非來自把算術做得更快,而是來自提高算術強度——重構運算,讓每個從記憶體取來的位元組在被丟棄前被多用許多次。
一個樸素的矩陣乘法,為每個輸出元素都從記憶體讀一列與一行,強度低、受記憶體所限。把它分塊——把每個矩陣的一小塊載入快速的晶片上記憶體,在取更多之前重用它做許多乘加——會大幅提高算術強度,把核心程式從記憶體屋頂推向運算屋頂,往往在結果相同下加速許多倍。
算術強度 = 每搬一位元組的運算數。提高它(靠重用資料)通常才是吞吐量硬體上真正的提速之路。
低算術強度封住你,無論算術單元多強:你受記憶體所限,買更多運算是冤枉錢。常見的補救是靠重用資料來提高強度,而非把數學做得更快。