作者shane87123 (阳光大肥宅)
看板CompilerDev
标题[问题] 为何这两份程式码的效能差异如此奇特
时间Tue May 3 22:12:16 2022
由於我是在 LLVM IR 最佳化阶段发现这个问题,
跟编译器最佳化有关,所以我发在这个版上。
这个问题困扰我很久了,想和版上的大大讨论一番。
由於 LLVM IR 比较难读,所以我把程式逆推成 C code 来增加可读性。
先附上两份程式码的线上 diff:
https://www.diffchecker.com/Thbx9sDk
然後进行这样编译:
opt -S -passes=mem2reg more.ll -o more.ll
opt -S -passes=mem2reg less.ll -o less.ll
llc more.ll
llc less.ll
( llc 预设 O2)
得到两份组合语言。线上 diff 如下:
https://www.diffchecker.com/NV4uuopa
(可以直接拉到左方组语 85 行 if.end 那里)
其实可以发现,左边那份程式码(姑且称之 less.c)先将 foo(rem % 5) 存起来,
只计算了 rem % 5 一次、call foo 一次;
右边的程式码(more.c) foo(rem % 5) 计算两次,也就是说 rem % 5 两次、call foo 两次,
比左边的程式码多一次。
理论上,应该要比较慢才对,但我用 Linux Perf 跑过一万次发现,
多计算 rem % 5 和 call foo 的反而比较快。
perf 中,我可以得知 instruction 数量、cycles 数量等等
instruction 数量中,less.c 比 more.c 还要少,不过这是可以预料的,
毕竟人家就是比他少做运算跟呼叫函数;
然而在 "insn per cycle" 则直接输给了 more.c,导致实际 cycles 数量 less.c 比 more.c
还要多,
也当然执行时间比较长,但我实在是不明白原因为何。
目前的实验做到以下:
1. llc 用 O0 最佳化 -> less.c 比 more.c 更快
-> 表示 llc 的 O2 对 more.c 那份程式码有更好的最佳化?
但很没道理,明明多 call 了 function 也多计算了取余数运算,怎麽会比较快?
2. 观察 foo(rem % 5) 的参数 "rem % 5" 值为多少,发现都是 0
-> 也就是说,多 call 的 function 都只是进入 function 直接回传 1
-> 把该参数改成非 0 则 less.c 比 more.c 更快。
但不管如何,还是多呼叫 function 了呀,没道理参数影响这麽多,
program counter 跳来跳去,一定会比较慢吧?
这问题虽然很实务,但真的很玄,而且困扰我很久。
我的老板(现为硕士生)要我把原因找出来,但我找了一整天,实在是想不到原因。
希望有高手能够帮帮我,拜托了...
--
※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 1.160.189.98 (台湾)
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/CompilerDev/M.1651587139.A.DF3.html
※ 编辑: shane87123 (1.160.189.98 台湾), 05/03/2022 22:13:26
※ 编辑: shane87123 (1.160.189.98 台湾), 05/03/2022 22:19:29
1F:推 VF84: 是说 LLVM 有个东西叫 MCA,我没用过,但看描述,感觉应该可05/04 08:42
2F:→ VF84: 以帮到你?05/04 08:42
谢谢,我都忘记还有这个东西了。
用 llvm-mca 分析一下,并用 bottleneck-analysis 分析,发现 O3 的 data-dependency
比较重,研判可能跟 cse 有关:资料在程式码初期计算好,并存放在 register 内,
之後的运算需要使用到它,就需要去存取这个 register 等等...
为了验证这个部分,我把 cse 後的程式码还原回去,再用 llvm-mca 跑一次分析,
确实 register-dependency 下降了、时间也降下来了。
我自己得到的结论是:O3 可以有效降低指令的执行数量,但有可能因此增加 data-depende
ncy,
会变差。
※ 编辑: shane87123 (1.160.189.98 台湾), 05/04/2022 11:59:19
3F:推 VF84: 那为什麽有 data dependency 就会跑比较慢呢?因为05/04 12:54
4F:→ VF84: pipeline 会 stall 吗?05/04 12:54
谢谢你抽空跟我一起讨论。看过你的提问後,我在近一步分析。
我猜测 data dependency 应该是出在 C code 中的 a = a * call
(另一份的是 a = a * foo( rem % 5 )
我去做了 time-line 的分析
llvm-mca -timeline more.s
llvm-mca -timeline less.s
根据 llvm-mca 文件提到的:当指令在排班器的 ready 状态的时间与在排班器的总时间相比越少的话,data dependency 越高,Latency也比较高
(
https://llvm.org/docs/CommandGuide/llvm-mca.html#timeline-view)
所以我去看了那个乘法指令的状态,大致上如下:
[1]: 在排班器里面的时间
[2]: 在排班器且为 ready 的时间
[3]: 总共耗费的时间
less.s (我认为应该比较快但没有比较快的)
[1] [2] [3]
90.4 0.5 36.2 imull %ebp, %eax
more.s
[1] [2] [3]
112.1 1.0 31.8 imull %r14d, %eax
我去算了一下比例:
less.s 的比例:0.5 / 90.4*100% = 0.56%
more.s 的比例:1 / 112.1 * 100% = 0.89%
表示 less.s 的乘法因 data dependency 而 latency 比较严重
至於 more.s 的乘法运算之前应该还有 callq foo 这个指令,应该会很慢才对,
但 llvm-mca 有提到,面对 call function 准确度不高,加上我原文提及,more.c 比较快
的原因是因为传入 foo 的参数为0,会
直接 return 1,所以我觉得 call foo 实际执行时间应该很短
换成传入2以上的话应该就会比较慢了
※ 编辑: shane87123 (49.216.29.37 台湾), 05/04/2022 14:43:08
※ 编辑: shane87123 (49.216.29.37 台湾), 05/04/2022 14:44:32
※ 编辑: shane87123 (49.216.29.37 台湾), 05/04/2022 14:45:54
※ 编辑: shane87123 (49.216.29.37 台湾), 05/04/2022 14:46:48
※ 编辑: shane87123 (49.216.29.37 台湾), 05/04/2022 14:49:12
5F:推 VF84: 感谢分享 05/04 17:47
6F:推 VF84: 其实你後面那一串分析我就跟不上了XD,所以没办法给出更多的 05/04 23:06
7F:→ VF84: 想法,这里就留给更厉害的大大吧05/04 23:06
※ 编辑: shane87123 (1.160.189.98 台湾), 05/05/2022 01:22:46
8F:→ Lipraxde: 文章应该是说 long data dependencies 会使 ILP 变糟,05/05 06:06
9F:→ Lipraxde: 跟你转换过的说法有些不太一样?05/05 06:06
谢谢你提醒我ILP,我昨天看文章没看到这块
所以我後来有做另一个实验
就是我把两份程式码用单一核心去跑
发现less比more快了
10F:→ Lipraxde: 「然而在 "insn per cycle" 则直接输给了 more.c,导致05/05 06:13
11F:→ Lipraxde: 实际 cycles 数量 less.c 比 more.c 还要多」<- 因果关05/05 06:13
12F:→ Lipraxde: 系怪怪的,而且因为 more 有更多 cycle 短的指令去摊平05/05 06:13
13F:→ Lipraxde: insn per cycle,比较 insn per cycle 不合适。05/05 06:13
我认为的因果关系是这样:
1. less比more多data dependency导致ILP更差
2. ILP变差,导致在多核心运算时,less的IPC比more还低
3. 多核运算中,速度上 less 比more 慢
4. 单一核心运算中,more失去了ILP比较好的优势,而less指令比较少的优势还在,所以单
核运算less比more快
虽然比较IPC好像真的不合适,因为他应该是从cycles和指令数量去逆推IPC
但我想不到更好的因果关系去说明这件事情OTZ
※ 编辑: shane87123 (101.12.17.166 台湾), 05/05/2022 12:36:56
※ 编辑: shane87123 (101.12.17.166 台湾), 05/05/2022 12:40:10
14F:推 xam: 专题生吗? 05/15 05:38
15F:→ Lipraxde: 单核跟多核比能造成影响的因素蛮多的...通常可以先考虑 05/15 16:20
16F:→ Lipraxde: 差异的量级再针对可能的原因找 05/15 16:20
17F:→ Lipraxde: 还有个方式是换旧旧的 CPU,比较不用考虑特殊的因素XD 05/15 16:22