为什么GCC __builtin_prefetch不能提高性能?

我正在编写一个程序来分析社交网络图。 这意味着程序需要大量的随机内存访问。 在我看来,预取应该有所帮助。 这是从顶点的邻居读取值的一小段代码。

for (size_t i = 0; i < v.get_num_edges(); i++) { unsigned int id = v.neighbors[i]; res += neigh_vals[id]; } 

我将上面的代码转换为下面的代码,并预取顶点的邻居值。

 int *neigh_vals = new int[num_vertices]; for (size_t i = 0; i < v.get_num_edges(); i += 128) { size_t this_end = std::min(v.get_num_edges(), i + 128); for (size_t j = i; j < this_end; j++) { unsigned int id = v.neighbors[j]; __builtin_prefetch(&neigh_vals[id], 0, 2); } for (size_t j = i; j < this_end; j++) { unsigned int id = v.neighbors[j]; res += neigh_vals[id]; } } 

在这个C ++代码中,我没有覆盖任何运算符。

不幸的是,代码并没有真正提高性能。 我想知道为什么。 显然,硬件预取在这种情况下不起作用,因为硬件无法预测内存位置。

我想知道它是否是由GCC优化引起的。 当我编译代码时,我启用-O3。 我真的希望即使启用-O3,预取也可以进一步提高性能。 在这种情况下,-O3优化是否融合了两个循环? 在这种情况下,-O3能否默认启用预取?

我使用gcc 4.6.3版,程序在Intel Xeon E5-4620上运行。

谢谢,Da

是的,一些最新版本的GCC (例如2015年3月的4.9)能够在使用-O3进行优化时发出一些PREFETCH指令(即​​使没有任何明确的__builtin_prefetch

我们不知道get_neighbor正在做什么,以及vneigh_val的类型是什么。

预取并不总是有利可图。 添加显式__builtin_prefetch降低代码速度你需要衡量。

正如Retired Ninja评论的那样,在一个循环中预取并希望数据将在下一个循环中缓存(在源代码中更进一步)是错误的。

你也许可以尝试一下

 for (size_t i = 0; i < v.get_num_edges(); i++) { fg::vertex_id_t id = v.get_neighbor(i); __builtin_prefetch (neigh_val[v.get_neighbor(i+4)]); res += neigh_vals[id]; } 

您可以凭经验将4更换为最合适的常数。

但我猜上面的__builtin_prefetch是没用的(因为编译器可能会自己添加它)并且它可能会损害(甚至崩溃程序,当计算它的参数给出未定义的行为时,例如如果v.get_neighbor(i+4)是未定义的;但是在地址空间之外预取地址不会造成伤害 - 但可能会降低程序速度。 请基准。

查看相关问题的答案 。

请注意,在C ++中所有的[]get_neighbor都可能会重载并变得非常复杂,因此我们无法猜测!

并且有些情况下硬件会限制性能,无论你添加什么__builtin_prefetch (添加它们都会损害性能)

顺便说一句,您可以传递-O3 -mtune=native -fdump-tree-ssa -S -fverbose-asm以了解编译器正在做什么(并查看生成的转储文件和汇编程序文件); 而且,确实发生-O3产生的代码比-O2给出的稍慢。

如果你有时间浪费在优化上,你可以考虑显式multithreading , OpenMP , OpenCL 。 请记住, 过早优化是邪恶的 。 您是否进行了基准测试,是否对整个应用程序进