我正在编写一个程序来分析社交网络的图。这意味着程序需要大量的随机内存访问。在我看来,预取应该会有所帮助。下面是一小段从顶点的邻居读取值的代码。
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版本,该程序在英特尔至强E5-4620上运行。
谢谢,Da
是的,GCC的一些最新版本(例如2015年3月的4.9)能够在使用-O3
(即使没有任何明确的__builtin_prefetch
)进行优化时发出一些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
产生的代码稍慢。
如果有时间浪费在优化上,可以考虑显式多线程、OpenMP、OpenCL。请记住,过早优化是邪恶的。你对整个应用程序进行了基准测试吗?