为什么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
正在做什么,以及v
和neigh_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 。 请记住, 过早优化是邪恶的 。 您是否进行了基准测试,是否对整个应用程序进