4

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

 2 years ago
source link: https://www.v2ex.com/t/845257
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

V2EX  ›  Linux

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

  kgdb00 · 11 小时 35 分钟前 · 1513 次点击
// 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 。

30 条回复    2022-04-07 03:04:14 +08:00

kgdb00

kgdb00      11 小时 23 分钟前

使用"sysbench cpu run"得到的是同样的结果,5600x 上的性能要远高于 3700x 。

booboo

booboo      11 小时 19 分钟前   ❤️ 1

是否确保编译出来的程序是一样(汇编代码比较),再就是你用了 sqrt 是动态链接的话库是否一致,操作系统版本和编译项是否一样。
一般性能差这么大,大概率是缓存引起的。

Huelse

Huelse      11 小时 14 分钟前

据我所知,锐龙 5000 系列是新架构,三缓利用效率对比前几代提升很大,也就是单核性能很强

你都是 long long 类型的,占用比较大,此时三缓越快越明显

askonly

askonly      11 小时 12 分钟前

@booboo 感谢回复,编译出的程序是一样的,都是在 fedora 35 系统上,而且用 fedora 35 的 sysbench 也是这样的差距。

kgdb00

kgdb00      11 小时 10 分钟前

@booboo 感谢回复,编译出的程序是一样的,都是在 fedora 35 系统上,而且用 fedora 35 的 sysbench 也是这样的差距。刚才不小心用小号回复了。

icyalala

icyalala      10 小时 59 分钟前

先把 branches, branch-misses, L1-dcache-loads, L1-dcache-load-misse 都打出来看看。
这段代码没什么访存压力,无非是 sqrt 里面可能有些查表操作,warmup 后应该都在 L1 内。
排除编译差别,我感觉更可能是分支预测器表现不同,但还是要看 perf 输出结果。

jdjingdian

jdjingdian      10 小时 49 分钟前

不懂性能分析,但是 3700x 是 zen2 ,5600x 是 zen3 ,5600x 单核性能高出 3700x 一大截

kgdb00

kgdb00      10 小时 49 分钟前

@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

kgdb00      10 小时 47 分钟前

@kgdb00 #8 v 站的回复不能用 markdown 格式化

choury

choury      10 小时 36 分钟前

从 stalled-cycles-backend 上看,是浮点数性能差异比较大

icyalala

icyalala      10 小时 1 分钟前

分支预测没什么太大差距。
如果同楼上浮点数性能差异的话,那就把其他都去掉,只测一下 sqrt() 看看,还有 fp 转 int 也可以单独测一下。
看了下 agner.org 的描述,Zen3 对 FP 单元提升挺大。

kgdb00

kgdb00      9 小时 29 分钟前

@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

BrettD      9 小时 25 分钟前 via iPad

把完整的 annotated assembly 贴出来?

kgdb00

kgdb00      9 小时 14 分钟前

3700X:
[Imgur]( )

5600X:

[Imgur]( )

BrettD

BrettD      8 小时 35 分钟前

> 而且我用 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

secondwtq      8 小时 20 分钟前

@BrettD
> 我觉得热点显示的 mov %rdx,%rax 这一行是在等上一条 divq 指令的 rdx 余数结果出来

这是数据采集机制导致的误差,关键词 "skid"

choury

choury      4 小时 57 分钟前 via Android

为什么不是编绎好的同一份二进制在两台机器上测呢,你这逻辑都不一样,测鬼呢

kgdb00

kgdb00      4 小时 36 分钟前

@choury 你为什么会认为我在两台机器上测的不是同一个二进制文件?

mayli

mayli      3 小时 55 分钟前 via Android

3700x 的 cycle 明显比 5600x 多,大概 3x 的样子
我感觉是某个指令上 3700x 需要的 cycle 多,可以把这里面的指令拆开分别做 microbenchmark 看看具体是哪个指令慢多少。

kgdb00

kgdb00      3 小时 29 分钟前

@mayli 我觉得不是某个指令慢导致的,你可以看一下我在 12 楼的回复。

c0xt30a

c0xt30a      3 小时 19 分钟前

OP 的系统环境硬件配置是什么样子的?还有编译选项能否分享一下?

BrettD

BrettD      2 小时 11 分钟前

@kgdb00 上面说了呀,建议排查 divq 指令在 Zen2 和 Zen3 之间的延迟区别

mayli

mayli      2 小时 2 分钟前 via Android

@kgdb00 如果是 if 这个引发的话 那就是两个 cpu 分支预测成功率不一样 不过这种情况比较罕见,现代 cpu 预测一般都差不多,而且从 perf 看 也是的确差不多

owwlo

owwlo      1 小时 6 分钟前 via iPhone

有可能是 vulnerability mitigation 导致的么(但是差别不应该这么大)…楼主查过 lscpu 有没有 mitigation 吗?

nlzy

nlzy      1 小时 0 分钟前   ❤️ 5

看了眼源码,是用试除法计算素数。那汇编都不用细看了,只要编译器不作妖,程序的瓶颈肯定是 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

mayli      55 分钟前 via Android

@nlzy 破案了?

kgdb00

kgdb00      11 分钟前

@nlzy 感谢回复,我也是大半夜不睡觉,编译器优化是故意关掉的,开了 O3 优化差距一样大。你这一大堆解释我得等有空了再验证吸收一下,另外这代码是取自 sysbench ,我也不管它有啥意义,只是想搞明白性能为啥有差别。

LeeReamond

LeeReamond      10 分钟前

@nlzy 大佬怎么判断瓶颈的,为什么上文 for 中的<不是瓶颈,而下文 for 中的<=是瓶颈呢

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK