作者FRAXIS (喔喔)
看板C_and_CPP
标题[心得] Loop unrolling, Duff's device
时间Sat Jun 27 10:50:01 2009
这些技巧是在书上面看到的,跟大家分享。
假设现在要做一个整数阵列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
1F:→ MOONRAKER:不是在写asm又不是在没最佳化能力的compiler上实作的 06/27 10:54
2F:→ MOONRAKER:前提下,何时需要这种手动最佳化? 06/27 10:55
3F:→ tinlans:compiler 会自己做,还会帮你选该 unroll 或做 SWP。 06/27 11:35
4F:→ FRAXIS:我只是在研究Duff's device的语法 没有想那麽多.. 06/27 12:02
5F:→ MOONRAKER:那是叫logic,或flow,或pattern,不叫语法(syntax) 06/27 13:22
6F:→ MOONRAKER:if後面要有小括号这种事情才叫语法 06/27 13:23
7F:→ FRAXIS:其实我要表达的是 Duff's device使用到的语法 06/27 13:37
8F:→ FRAXIS:用switch跳到回圈内部 是否还有其他什麽用途? 06/27 13:38
9F:推 Song6Lin:可以请问一下是什麽书吗? 06/27 14:17
10F:→ Song6Lin:我觉得duff's device的用法好神奇喔。 06/27 14:17
11F:推 VictorTom:乍看之下是很有趣的用法, compiler的实作上或许也会用到 06/27 14:56
12F:→ VictorTom:类似的概念, 但老实说小弟觉得这东西拿来研究或特定领域 06/27 14:56
13F:→ VictorTom:与用途实作还行, 一般自己或工作上我一点都不想看到这种 06/27 14:57
14F:→ VictorTom:写法....Orz 06/27 14:57
15F:→ VictorTom:考虑CPU/pipeline最佳化, 其实这种简单回圈判断现在应该 06/27 14:59
16F:→ VictorTom:predict都有不错的效果; 最佳化应该考虑CPU与平台的特性 06/27 15:01
17F:→ VictorTom:来作, 不一定换个方向用i>=0, 用--i取代i--等还比较易懂 06/27 15:02
18F:→ VictorTom:也易除错吧我想@_@" 06/27 15:02
19F:→ VictorTom:看到加总另外说一下, 以前老师还说, 要安全的加总, 在已 06/27 15:03
20F:→ VictorTom:知结果不会爆的情况, 可能也要先对资料排序好, 再抓头抓 06/27 15:04
21F:→ VictorTom:尾来加, 避免中间过程可能加爆; 比如特有钱人记收入支出 06/27 15:04
22F:→ VictorTom:, 不过这已经无关效率最佳化就是了:) 06/27 15:05
23F:推 ckclark:楼上说的加总例子可以举一个会结果不同的吗 06/29 00:48
※ 编辑: FRAXIS 来自: 140.119.162.51 (06/29 07:59)
24F:推 VictorTom:忽然发现随便想了几个例子好像真的让它爆了也没差Orz 06/29 08:57
25F:→ VictorTom:特地试了一下好像爆了也不会写坏其他变数空间.... 06/29 08:58
26F:→ VictorTom:大概现在这东西比较不用care了吧....@_@" 06/29 08:58
27F:→ FRAXIS:或许是浮点数相加? 按照某种顺序加比较可以避免误差? 06/29 09:04
28F:推 VictorTom:Hmm~~浮点数处理难度又更大了说....Orz 06/29 09:08
29F:→ FRAXIS:要不然就是有正负数.. 让正负相消避免溢位.. 06/29 09:38
30F:推 mabus:这个还蛮有趣的。 07/29 14:01