【问题标题】:Optimizing away memcpy优化 memcpy
【发布时间】:2019-04-28 16:10:35
【问题描述】:

为了避免违反严格的别名规则,我在我的代码中的几个地方引入了memcpy,希望它是一个空操作。以下示例在 gcc 和 clang 上生成对 memcpy(或等效项)的调用。具体来说,fool<40> 总是这样做,而 foo 在 gcc 上运行但不是 clang,fool<2> 在 clang 但不是 gcc 上运行。何时/如何优化?

uint64_t bar(const uint16_t *buf) {
  uint64_t num[2];
  memcpy(&num, buf, 16);
  return num[0] + num[1];
}

uint64_t foo(const uint16_t *buf) {
  uint64_t num[3];
  memcpy(&num, buf, sizeof(num));
  return num[0] + num[1];
}

template <int SZ>
uint64_t fool(const uint16_t *buf) {
  uint64_t num[SZ];
  memcpy(&num, buf, sizeof(num));
  uint64_t ret = 0;
  for (int i = 0; i < SZ; ++i)
    ret += num[i];
  return ret;
}

template uint64_t fool<2>(const uint16_t*);
template uint64_t fool<40>(const uint16_t*);

还有一个link to the compiled output (godbolt).

【问题讨论】:

  • 你误解了一些东西。 &lt;2&gt; 不会用 clang 或 gcc 调用 memcpy&lt;40&gt; 调用 memcpy 仅使用 clang。如果您只为一个uint64_t 应用memcpy,则可以轻松删除它(我的意思是,将memcpy 放入for 循环中)。
  • @geza 他们正在制作一个“等效”(这无疑是不精确的......)。即有一个对应的mov
  • 这很可能意味着 memcpy 被内联并且(不幸的是)没有别的。因此,虽然没有实际调用 memcpy 函数,但仍在制作副本,而不是将给定内存的预期效果简单地解释为目标类型的对象。

标签: c++ compiler-optimization


【解决方案1】:

我无法真正告诉您为什么各个编译器无法按照您希望它们在特定情况下优化的方式优化代码。我猜每个编译器要么只是无法跟踪 memcpy 在目标数组和源内存之间建立的关系(正如我们所见,它们似乎至少在某些情况下确实认识到了这种关系),或者它们只是有一些启发式的说法他们选择不使用它。

无论如何,因为当我们依赖编译器跟踪整个数组时,编译器的行为似乎不像我们希望的那样,我们可以尝试做的是通过对每个元素执行 memcpy 来使其对编译器更加明显-元素基础。这个seems to produce the desired result on both compilers。请注意,我必须手动展开 barfoo 中的初始化,否则 clang 会再次进行复制。

除此之外,请注意在 C++ 中您应该使用 std::memcpystd::uint64_t 等,因为标准头文件 are not guaranteed 也将这些名称引入全局命名空间(尽管我不知道有任何实现那不会那样做)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-22
    • 2016-04-25
    • 2013-07-30
    相关资源
    最近更新 更多