c-为什么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版本,该程序在英特尔至强E5-4620上运行。

谢谢,Da

是的,GCC的一些最新版本(例如2015年3月的4.9)能够在使用-O3(即使没有任何明确的__builtin_prefetch)进行优化时发出一些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产生的代码稍慢。

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

相关内容

  • 没有找到相关文章

最新更新