[心得] Loop unrolling, Duff's device

看板C_and_CPP (C/C++)作者 (喔喔)時間16年前 (2009/06/27 10:50), 編輯推噓6(6024)
留言30則, 7人參與, 最新討論串1/2 (看更多)
這些技巧是在書上面看到的,跟大家分享。 假設現在要做一個整數陣列a的加總,如果已知陣列長度為100的話, 最簡單的寫法是。 for ( i = 0; i < 100; ++i ) sum += a[ i ]; 但是這樣會做100次的 i < 100 的判斷,增加branch降低效率,所 以loop unrolling的技巧就是在迴圈內部多做幾次,減少判斷的次 數。 for ( i = 0; i < 100; i += 5 ) { sum += a[ i ]; sum += a[ i + 1 ]; sum += a[ i + 2 ]; sum += a[ i + 3 ]; sum += a[ i + 4 ]; } (當然可以乾脆展開100次,所有的判斷都省了,但是這樣做會加大 程式碼的長度,對效率也會有影響,而且完全沒彈性) 這是在已知長度是100的情況下才能做的,因為我們知道100是5的倍 數,但是如果情況變成要對任意的陣列長度n都可以適用,就得寫成。 for ( i = 0; i < n % 5; ++i ) // 先把n % 5的餘數做完 sum += a[ i ]; for ( ; i < n; i += 5 ) { // 剩下的迴圈數一定是5的倍數了 sum += a[ i ]; sum += a[ i + 1 ]; sum += a[ i + 2 ]; sum += a[ i + 3 ]; sum += a[ i + 4 ]; } 不過Duff's device的技巧就是可以把這兩個迴圈融合在一起。 i = 0; switch ( n % 5 ) { case 0: do {sum += a[ i++ ]; case 4: sum += a[ i++ ]; case 3: sum += a[ i++ ]; case 2: sum += a[ i++ ]; case 1: sum += a[ i++ ]; } while ((n -= 5) > 0); } 這邊是用switch-case跳到do-while的迴圈之中。 回到一開始的loop unrolling的版本 for ( i = 0; i < 100; i += 5 ) { sum += a[ i ]; sum += a[ i + 1 ]; sum += a[ i + 2 ]; sum += a[ i + 3 ]; sum += a[ i + 4 ]; } 其實這程式等價於 for ( i = 0; i < 100;) { sum += a[ i++ ]; sum += a[ i++ ]; sum += a[ i++ ]; sum += a[ i++ ]; sum += a[ i++ ]; } 但是這樣做會讓造成前後兩個指令之間在i上的相依性,沒辦法完全 利用到CPU的pipeline。用前者會稍微比較好,不過實際上sum也是 造成相依性的來源,所以可以改寫成 for ( i = 0; i < 100; i += 5 ) { sum1 += a[ i ]; sum2 += a[ i + 1 ]; sum1 += a[ i + 2 ]; sum2 += a[ i + 3 ]; sum1 += a[ i + 4 ]; } sum = sum1 + sum2; 這技巧也能和Duff's device同時使用,只是有沒有辦法增加效率就 要試試看才知道。 上面這些程式碼都只是示意,迴圈要展開幾次,要用多少個變數來 加總才能達成最高效率,與硬體實作部份有很大的關係,只能慢慢 的作測試。 -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 140.119.162.51

06/27 10:54, , 1F
不是在寫asm又不是在沒最佳化能力的compiler上實作的
06/27 10:54, 1F

06/27 10:55, , 2F
前提下,何時需要這種手動最佳化?
06/27 10:55, 2F

06/27 11:35, , 3F
compiler 會自己做,還會幫你選該 unroll 或做 SWP。
06/27 11:35, 3F

06/27 12:02, , 4F
我只是在研究Duff's device的語法 沒有想那麼多..
06/27 12:02, 4F

06/27 13:22, , 5F
那是叫logic,或flow,或pattern,不叫語法(syntax)
06/27 13:22, 5F

06/27 13:23, , 6F
if後面要有小括號這種事情才叫語法
06/27 13:23, 6F

06/27 13:37, , 7F
其實我要表達的是 Duff's device使用到的語法
06/27 13:37, 7F

06/27 13:38, , 8F
用switch跳到迴圈內部 是否還有其他什麼用途?
06/27 13:38, 8F

06/27 14:17, , 9F
可以請問一下是什麼書嗎?
06/27 14:17, 9F

06/27 14:17, , 10F
我覺得duff's device的用法好神奇喔。
06/27 14:17, 10F

06/27 14:56, , 11F
乍看之下是很有趣的用法, compiler的實作上或許也會用到
06/27 14:56, 11F

06/27 14:56, , 12F
類似的概念, 但老實說小弟覺得這東西拿來研究或特定領域
06/27 14:56, 12F

06/27 14:57, , 13F
與用途實作還行, 一般自己或工作上我一點都不想看到這種
06/27 14:57, 13F

06/27 14:57, , 14F
寫法....Orz
06/27 14:57, 14F

06/27 14:59, , 15F
考慮CPU/pipeline最佳化, 其實這種簡單迴圈判斷現在應該
06/27 14:59, 15F

06/27 15:01, , 16F
predict都有不錯的效果; 最佳化應該考慮CPU與平台的特性
06/27 15:01, 16F

06/27 15:02, , 17F
來作, 不一定換個方向用i>=0, 用--i取代i--等還比較易懂
06/27 15:02, 17F

06/27 15:02, , 18F
也易除錯吧我想@_@"
06/27 15:02, 18F

06/27 15:03, , 19F
看到加總另外說一下, 以前老師還說, 要安全的加總, 在已
06/27 15:03, 19F

06/27 15:04, , 20F
知結果不會爆的情況, 可能也要先對資料排序好, 再抓頭抓
06/27 15:04, 20F

06/27 15:04, , 21F
尾來加, 避免中間過程可能加爆; 比如特有錢人記收入支出
06/27 15:04, 21F

06/27 15:05, , 22F
, 不過這已經無關效率最佳化就是了:)
06/27 15:05, 22F

06/29 00:48, , 23F
樓上說的加總例子可以舉一個會結果不同的嗎
06/29 00:48, 23F
※ 編輯: FRAXIS 來自: 140.119.162.51 (06/29 07:59)

06/29 08:57, , 24F
忽然發現隨便想了幾個例子好像真的讓它爆了也沒差Orz
06/29 08:57, 24F

06/29 08:58, , 25F
特地試了一下好像爆了也不會寫壞其他變數空間....
06/29 08:58, 25F

06/29 08:58, , 26F
大概現在這東西比較不用care了吧....@_@"
06/29 08:58, 26F

06/29 09:04, , 27F
或許是浮點數相加? 按照某種順序加比較可以避免誤差?
06/29 09:04, 27F

06/29 09:08, , 28F
Hmm~~浮點數處理難度又更大了說....Orz
06/29 09:08, 28F

06/29 09:38, , 29F
要不然就是有正負數.. 讓正負相消避免溢位..
06/29 09:38, 29F

07/29 14:01, , 30F
這個還蠻有趣的。
07/29 14:01, 30F
文章代碼(AID): #1AHOZQfK (C_and_CPP)
文章代碼(AID): #1AHOZQfK (C_and_CPP)