【问题标题】:why does GCC __builtin_prefetch not improve performance?为什么 GCC __builtin_prefetch 不能提高性能?
【发布时间】:2015-05-26 00:46:47
【问题描述】:

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

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 上运行。

谢谢, 大

【问题讨论】:

  • get_neighbor 在做什么? neigh_vals 的类型是什么? vertex_id_t 是什么? v 是什么?请编辑您的问题以改进它。
  • 另见this answer 相关问题
  • 看起来您在一个循环中预取并希望数据在下一个循环中仍然可用。这不太可能有帮助,而且可能会造成伤害。
  • gcc 的哪个版本,哪个处理器?您需要编辑您的问题以改进它。
  • 旁白:v.get_num_edges() 在整个for 循环中是不变的吗?看起来您可以将它分配给一个变量,而不是每次通过循环顶部调用它。

标签: c performance gcc prefetch


【解决方案1】:

是的,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 是没用的(因为编译器可能能够自己添加它)并且它可能会损害(甚至使程序崩溃,当计算它的参数时给出未定义的行为,例如如果@987654337 @ 是未定义的;但是在地址空间之外预取地址不会造成损害——但可能会减慢程序的速度)。 请进行基准测试。

有关相关问题,请参阅this answer

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

在某些情况下,无论您添加什么 __builtin_prefetch,硬件都会限制性能(添加它们可能会损害性能)

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

如果你有时间浪费在优化上,你可以考虑明确的multithreadingOpenMPOpenCL。请记住,过早的优化是邪恶的。您是否进行了基准测试,您是否分析了您的整个应用程序?

【讨论】:

  • 要明确一点,“甚至使程序崩溃”并不意味着如果地址无效,预取就会产生错误——但地址表达式本身必须是有效的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-07-16
  • 1970-01-01
  • 2010-12-09
  • 2017-11-19
  • 1970-01-01
  • 2012-10-03
  • 1970-01-01
相关资源
最近更新 更多