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

2022-04-06 15:39:46 +08:00
 kgdb00
// 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 。

5443 次点击
所在节点    Linux
33 条回复
mayli
2022-04-06 23:19:20 +08:00
3700x 的 cycle 明显比 5600x 多,大概 3x 的样子
我感觉是某个指令上 3700x 需要的 cycle 多,可以把这里面的指令拆开分别做 microbenchmark 看看具体是哪个指令慢多少。
kgdb00
2022-04-06 23:45:25 +08:00
@mayli 我觉得不是某个指令慢导致的,你可以看一下我在 12 楼的回复。
c0xt30a
2022-04-06 23:54:49 +08:00
OP 的系统环境硬件配置是什么样子的?还有编译选项能否分享一下?
BrettD
2022-04-07 01:02:49 +08:00
@kgdb00 上面说了呀,建议排查 divq 指令在 Zen2 和 Zen3 之间的延迟区别
mayli
2022-04-07 01:12:43 +08:00
@kgdb00 如果是 if 这个引发的话 那就是两个 cpu 分支预测成功率不一样 不过这种情况比较罕见,现代 cpu 预测一般都差不多,而且从 perf 看 也是的确差不多
owwlo
2022-04-07 02:07:49 +08:00
有可能是 vulnerability mitigation 导致的么(但是差别不应该这么大)…楼主查过 lscpu 有没有 mitigation 吗?
nlzy
2022-04-07 02:14:22 +08:00
看了眼源码,是用试除法计算素数。那汇编都不用细看了,只要编译器不作妖,程序的瓶颈肯定是 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
2022-04-07 02:19:45 +08:00
@nlzy 破案了?
kgdb00
2022-04-07 03:03:32 +08:00
@nlzy 感谢回复,我也是大半夜不睡觉,编译器优化是故意关掉的,开了 O3 优化差距一样大。你这一大堆解释我得等有空了再验证吸收一下,另外这代码是取自 sysbench ,我也不管它有啥意义,只是想搞明白性能为啥有差别。
LeeReamond
2022-04-07 03:04:14 +08:00
@nlzy 大佬怎么判断瓶颈的,为什么上文 for 中的<不是瓶颈,而下文 for 中的<=是瓶颈呢
weyou
2022-04-07 09:04:25 +08:00
@LeeReamond 前面的 for 是整型比较,后面的 for 需要是转浮点数比较,涉及浮点运算
secondwtq
2022-04-07 09:13:51 +08:00
@weyou 是因为大多数数都不是素数,所以外层循环的几乎每个 iteration ,内层循环都要跑大约 sqrt(c) 次,外层循环占得可能不到 1%。
nlzy
2022-04-07 09:34:28 +08:00
@LeeReamond 外层循环的运行次数肯定少于内层循环。这也是没考虑 sqrt() 对性能的影响的原因,它们的运行次数都远远少于那两个表达式。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/845257

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX