看板 C_and_CPP 關於我們 聯絡資訊
這些技巧是在書上面看到的,跟大家分享。 假設現在要做一個整數陣列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
MOONRAKER:不是在寫asm又不是在沒最佳化能力的compiler上實作的 06/27 10:54
MOONRAKER:前提下,何時需要這種手動最佳化? 06/27 10:55
tinlans:compiler 會自己做,還會幫你選該 unroll 或做 SWP。 06/27 11:35
FRAXIS:我只是在研究Duff's device的語法 沒有想那麼多.. 06/27 12:02
MOONRAKER:那是叫logic,或flow,或pattern,不叫語法(syntax) 06/27 13:22
MOONRAKER:if後面要有小括號這種事情才叫語法 06/27 13:23
FRAXIS:其實我要表達的是 Duff's device使用到的語法 06/27 13:37
FRAXIS:用switch跳到迴圈內部 是否還有其他什麼用途? 06/27 13:38
Song6Lin:可以請問一下是什麼書嗎? 06/27 14:17
Song6Lin:我覺得duff's device的用法好神奇喔。 06/27 14:17
VictorTom:乍看之下是很有趣的用法, compiler的實作上或許也會用到 06/27 14:56
VictorTom:類似的概念, 但老實說小弟覺得這東西拿來研究或特定領域 06/27 14:56
VictorTom:與用途實作還行, 一般自己或工作上我一點都不想看到這種 06/27 14:57
VictorTom:寫法....Orz 06/27 14:57
VictorTom:考慮CPU/pipeline最佳化, 其實這種簡單迴圈判斷現在應該 06/27 14:59
VictorTom:predict都有不錯的效果; 最佳化應該考慮CPU與平台的特性 06/27 15:01
VictorTom:來作, 不一定換個方向用i>=0, 用--i取代i--等還比較易懂 06/27 15:02
VictorTom:也易除錯吧我想@_@" 06/27 15:02
VictorTom:看到加總另外說一下, 以前老師還說, 要安全的加總, 在已 06/27 15:03
VictorTom:知結果不會爆的情況, 可能也要先對資料排序好, 再抓頭抓 06/27 15:04
VictorTom:尾來加, 避免中間過程可能加爆; 比如特有錢人記收入支出 06/27 15:04
VictorTom:, 不過這已經無關效率最佳化就是了:) 06/27 15:05
ckclark:樓上說的加總例子可以舉一個會結果不同的嗎 06/29 00:48
※ 編輯: FRAXIS 來自: 140.119.162.51 (06/29 07:59)
VictorTom:忽然發現隨便想了幾個例子好像真的讓它爆了也沒差Orz 06/29 08:57
VictorTom:特地試了一下好像爆了也不會寫壞其他變數空間.... 06/29 08:58
VictorTom:大概現在這東西比較不用care了吧....@_@" 06/29 08:58
FRAXIS:或許是浮點數相加? 按照某種順序加比較可以避免誤差? 06/29 09:04
VictorTom:Hmm~~浮點數處理難度又更大了說....Orz 06/29 09:08
FRAXIS:要不然就是有正負數.. 讓正負相消避免溢位.. 06/29 09:38
mabus:這個還蠻有趣的。 07/29 14:01