V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Distributions
Ubuntu
Fedora
CentOS
中文资源站
网易开源镜像站
kgdb00
V2EX  ›  Linux

Linux c 程序性能分析问题,有大佬能解释一下为何这段代码在 3700x 上很慢,而在 5600x 上很快吗?

  •  
  •   kgdb00 · 2022-04-06 15:39:46 +08:00 · 5665 次点击
    这是一个创建于 964 天前的主题,其中的信息可能已经有所发展或是发生改变。
    // cpu-test.c
    #include <math.h>
    
    int main(int argc, char *argv[])
    {
    	unsigned long long c;
    	unsigned long long l;
    	double t;
    
    	for (c = 3; c < 1000000; c++) {
    		t = sqrt((double) c);
    		for (l = 2; l <= t; l++)
    			if (c % l == 0)
    				break;
    	}
    
    	return 0;
    }
    
    

    这段代码取自 sysbench ,略有删减,原函数是:

    https://github.com/akopytov/sysbench/blob/master/src/tests/cpu/sb_cpu.c 的 cpu_execute_event 函数。

    使用"perf stat ./cpu-test"在 3700x 上得到的 IPC 是 0.83 ,而在 5600X 上得到的是 2.23 。

    33 条回复    2022-04-07 09:34:28 +08:00
    kgdb00
        1
    kgdb00  
    OP
       2022-04-06 15:51:24 +08:00
    使用"sysbench cpu run"得到的是同样的结果,5600x 上的性能要远高于 3700x 。
    booboo
        2
    booboo  
       2022-04-06 15:55:39 +08:00   ❤️ 1
    是否确保编译出来的程序是一样(汇编代码比较),再就是你用了 sqrt 是动态链接的话库是否一致,操作系统版本和编译项是否一样。
    一般性能差这么大,大概率是缓存引起的。
    Huelse
        3
    Huelse  
       2022-04-06 16:00:26 +08:00
    据我所知,锐龙 5000 系列是新架构,三缓利用效率对比前几代提升很大,也就是单核性能很强

    你都是 long long 类型的,占用比较大,此时三缓越快越明显
    askonly
        4
    askonly  
       2022-04-06 16:02:44 +08:00
    @booboo 感谢回复,编译出的程序是一样的,都是在 fedora 35 系统上,而且用 fedora 35 的 sysbench 也是这样的差距。
    kgdb00
        5
    kgdb00  
    OP
       2022-04-06 16:03:59 +08:00
    @booboo 感谢回复,编译出的程序是一样的,都是在 fedora 35 系统上,而且用 fedora 35 的 sysbench 也是这样的差距。刚才不小心用小号回复了。
    icyalala
        6
    icyalala  
       2022-04-06 16:15:30 +08:00
    先把 branches, branch-misses, L1-dcache-loads, L1-dcache-load-misse 都打出来看看。
    这段代码没什么访存压力,无非是 sqrt 里面可能有些查表操作,warmup 后应该都在 L1 内。
    排除编译差别,我感觉更可能是分支预测器表现不同,但还是要看 perf 输出结果。
    jdjingdian
        7
    jdjingdian  
       2022-04-06 16:25:29 +08:00
    不懂性能分析,但是 3700x 是 zen2 ,5600x 是 zen3 ,5600x 单核性能高出 3700x 一大截
    kgdb00
        8
    kgdb00  
    OP
       2022-04-06 16:25:41 +08:00
    @icyalala
    在 3700x 上运行"perf stat ./cpu-test"结果如下:

    ```
    root@develop:~/test# perf stat ./cpu-test

    Performance counter stats for './cpu-test':

    313.31 msec task-clock # 0.999 CPUs utilized
    0 context-switches # 0.000 /sec
    0 cpu-migrations # 0.000 /sec
    62 page-faults # 197.886 /sec
    1,334,724,216 cycles # 4.260 GHz
    4,152,400 stalled-cycles-frontend # 0.31% frontend cycles idle
    1,084,407,060 stalled-cycles-backend # 81.25% backend cycles idle
    1,112,064,247 instructions # 0.83 insn per cycle
    # 0.98 stalled cycles per insn
    280,732,961 branches # 896.020 M/sec
    479,493 branch-misses # 0.17% of all branches

    0.313649907 seconds time elapsed

    0.313436000 seconds user
    0.000000000 seconds sys
    ```

    5600x 上结果如下:

    ```
    d@desktop:~/test$ perf stat ./cpu-test

    Performance counter stats for './cpu-test':

    109.50 msec task-clock:u # 0.997 CPUs utilized
    0 context-switches:u # 0.000 /sec
    0 cpu-migrations:u # 0.000 /sec
    57 page-faults:u # 520.552 /sec
    497,192,700 cycles:u # 4.541 GHz
    1,236,868 stalled-cycles-frontend:u # 0.25% frontend cycles idle
    3,047 stalled-cycles-backend:u # 0.00% backend cycles idle
    1,109,781,743 instructions:u # 2.23 insn per cycle
    # 0.00 stalled cycles per insn
    280,305,908 branches:u # 2.560 G/sec
    420,708 branch-misses:u # 0.15% of all branches

    0.109816486 seconds time elapsed

    0.108714000 seconds user
    0.000997000 seconds sys
    ```
    kgdb00
        9
    kgdb00  
    OP
       2022-04-06 16:27:28 +08:00
    @kgdb00 #8 v 站的回复不能用 markdown 格式化
    choury
        10
    choury  
       2022-04-06 16:38:26 +08:00
    从 stalled-cycles-backend 上看,是浮点数性能差异比较大
    icyalala
        11
    icyalala  
       2022-04-06 17:13:32 +08:00
    分支预测没什么太大差距。
    如果同楼上浮点数性能差异的话,那就把其他都去掉,只测一下 sqrt() 看看,还有 fp 转 int 也可以单独测一下。
    看了下 agner.org 的描述,Zen3 对 FP 单元提升挺大。
    kgdb00
        12
    kgdb00  
    OP
       2022-04-06 17:44:52 +08:00
    @choury @icyalala 我觉得应该跟浮点数性能没关系,我把 if (c % l == 0) 这一行去掉,3700x 的 ipc 还反超了 5600x 。

    而且我用 perf annotate 分析 ,if (c % l == 0) 这一行的汇编指令有一条 mov %rdx,%rax 在 3700X 上占了 81%的时间,在 5600x 上也占了 54%的时间。

    有没有什么办法让 perf record 的输出能在另一台机上分析?我发现拷贝 perf.data 到另一台机就不能 annotate 了,所以也没法共享给各位分析。
    BrettD
        13
    BrettD  
       2022-04-06 17:49:41 +08:00 via iPad
    把完整的 annotated assembly 贴出来?
    kgdb00
        14
    kgdb00  
    OP
       2022-04-06 17:58:12 +08:00
    kgdb00
        15
    kgdb00  
    OP
       2022-04-06 18:00:20 +08:00
    3700X:
    [Imgur]( )

    5600X:

    [Imgur]( )
    secondwtq
        16
    secondwtq  
       2022-04-06 18:37:44 +08:00
    这问题在这扯过了 https://v2ex.com/t/828141
    BrettD
        17
    BrettD  
       2022-04-06 18:38:57 +08:00
    > 而且我用 perf annotate 分析 ,if (c % l == 0) 这一行的汇编指令有一条 mov %rdx,%rax 在 3700X 上占了 81%的时间,在 5600x 上也占了 54%的时间。

    我觉得热点显示的 mov %rdx,%rax 这一行是在等上一条 divq 指令的 rdx 余数结果出来,然后 3700X 的整数除法运算可能比 5600X 慢,所以

    > 把 if (c % l == 0) 这一行去掉,3700x 的 ipc 还反超了 5600x 。
    secondwtq
        18
    secondwtq  
       2022-04-06 18:54:23 +08:00
    @BrettD
    > 我觉得热点显示的 mov %rdx,%rax 这一行是在等上一条 divq 指令的 rdx 余数结果出来

    这是数据采集机制导致的误差,关键词 "skid"
    choury
        19
    choury  
       2022-04-06 22:17:41 +08:00 via Android
    为什么不是编绎好的同一份二进制在两台机器上测呢,你这逻辑都不一样,测鬼呢
    kgdb00
        20
    kgdb00  
    OP
       2022-04-06 22:38:38 +08:00
    @choury 你为什么会认为我在两台机器上测的不是同一个二进制文件?
    mayli
        21
    mayli  
       2022-04-06 23:19:20 +08:00 via Android
    3700x 的 cycle 明显比 5600x 多,大概 3x 的样子
    我感觉是某个指令上 3700x 需要的 cycle 多,可以把这里面的指令拆开分别做 microbenchmark 看看具体是哪个指令慢多少。
    kgdb00
        22
    kgdb00  
    OP
       2022-04-06 23:45:25 +08:00
    @mayli 我觉得不是某个指令慢导致的,你可以看一下我在 12 楼的回复。
    c0xt30a
        23
    c0xt30a  
       2022-04-06 23:54:49 +08:00
    OP 的系统环境硬件配置是什么样子的?还有编译选项能否分享一下?
    BrettD
        24
    BrettD  
       2022-04-07 01:02:49 +08:00   ❤️ 1
    @kgdb00 上面说了呀,建议排查 divq 指令在 Zen2 和 Zen3 之间的延迟区别
    mayli
        25
    mayli  
       2022-04-07 01:12:43 +08:00 via Android
    @kgdb00 如果是 if 这个引发的话 那就是两个 cpu 分支预测成功率不一样 不过这种情况比较罕见,现代 cpu 预测一般都差不多,而且从 perf 看 也是的确差不多
    owwlo
        26
    owwlo  
       2022-04-07 02:07:49 +08:00 via iPhone
    有可能是 vulnerability mitigation 导致的么(但是差别不应该这么大)…楼主查过 lscpu 有没有 mitigation 吗?
    nlzy
        27
    nlzy  
       2022-04-07 02:14:22 +08:00   ❤️ 17
    看了眼源码,是用试除法计算素数。那汇编都不用细看了,只要编译器不作妖,程序的瓶颈肯定是 l <= t 和 if (c % l == 0) 这两句。和缓存、访存关系都不大了。

    虽然有一个 if ,但是这个分支比较好预测:即使是静态预测,每个不同的 c 也只会预测失败一次,所以和分支也没什么关系。循环条件同理。

    同一个 c 的每一轮试除,l 是循环变量,而 c 和 t 是不变的,所以上述良好的分支预测再加上投机执行,是可以掩盖掉他们的延迟的,所以程序的瓶颈大概就是 l <= t 和 c % l 这两句的吞吐了。前者是整型转浮点然后比较,后者是一个除法。

    上网查资料,zen2 的 div 吞吐率倒数是 13-44 ,zen3 是 7-12 。而 cvtsi2sd 和 comisd 在 zen2 和 zen3 上都是 1 。瓶颈在前者,后者可以忽略掉,而前者在 zen2 和 zen3 的性能差距正好大约是三倍。(浮点和整数除法的执行单元应该不冲突吧)

    这大概就是楼主想要的答案了。

    至于 mov %rdx, %rax ,寄存器重命名阶段就已经处理掉了,不会是瓶颈。

    (唉,我为什么要大半夜不睡觉去分析这种编译器优化都没开,代码也没有仔细写的程序呢)
    mayli
        28
    mayli  
       2022-04-07 02:19:45 +08:00 via Android
    @nlzy 破案了?
    kgdb00
        29
    kgdb00  
    OP
       2022-04-07 03:03:32 +08:00
    @nlzy 感谢回复,我也是大半夜不睡觉,编译器优化是故意关掉的,开了 O3 优化差距一样大。你这一大堆解释我得等有空了再验证吸收一下,另外这代码是取自 sysbench ,我也不管它有啥意义,只是想搞明白性能为啥有差别。
    LeeReamond
        30
    LeeReamond  
       2022-04-07 03:04:14 +08:00
    @nlzy 大佬怎么判断瓶颈的,为什么上文 for 中的<不是瓶颈,而下文 for 中的<=是瓶颈呢
    weyou
        31
    weyou  
       2022-04-07 09:04:25 +08:00 via Android
    @LeeReamond 前面的 for 是整型比较,后面的 for 需要是转浮点数比较,涉及浮点运算
    secondwtq
        32
    secondwtq  
       2022-04-07 09:13:51 +08:00
    @weyou 是因为大多数数都不是素数,所以外层循环的几乎每个 iteration ,内层循环都要跑大约 sqrt(c) 次,外层循环占得可能不到 1%。
    nlzy
        33
    nlzy  
       2022-04-07 09:34:28 +08:00 via Android
    @LeeReamond 外层循环的运行次数肯定少于内层循环。这也是没考虑 sqrt() 对性能的影响的原因,它们的运行次数都远远少于那两个表达式。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2412 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 32ms · UTC 16:03 · PVG 00:03 · LAX 08:03 · JFK 11:03
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.