【问题标题】:Why is the compiler not optimizing this simple idiom?为什么编译器不优化这个简单的习语?
【发布时间】:2020-02-25 09:58:17
【问题描述】:

我有这段代码,但我想知道为什么编译器不将两个字符与 16 位比较进行比较。

inline bool beginswith (char c0, char c1, const char* s) {
    return (s[0] == c0) & (s[1] == c1);
}

bool test(const char* s) {
    return beginswith('0', 'x', s);
}

我希望我的Godbolt 链接(test 和 test2)中的两个版本能够编译为等效指令。有什么我遗漏的,因为这似乎是一个非常微不足道的优化。

编辑:我希望编译器进行这种优化的原因是我可以编写没有未定义行为的可移植代码,并且仍然可以获得完整的性能。

【问题讨论】:

  • 我已经设法使 GCC 输出与 char const b[]{c0, c1}; return !std::memcmp(b, s, sizeof b); 相同的程序集。 MSVC 未分阶段。
  • 我的猜测是,编译器不会做这个优化,因为这里*(const short*)s == (c0 | (c1 << 8)); 可能会发生未对齐的访问,与字节访问版本相比,这可能会产生巨大的性能损失
  • @Ayxan:没关系。允许编译器定义并因此利用 UB;只是程序员不被允许。
  • beginswith2 是未定义的行为。根据机器的字节序,您可以将c0/c1 与 s...的 msb/lsb 进行比较...
  • const char* s = "_x"; beginswith('x', 'X', s+1); char 访问s+1short 访问更快(在硬件级别,在某些拱门上)。输出组装的差异可能反映了这种差异。

标签: c++ performance optimization


【解决方案1】:

似乎编译器不够聪明,无法将内联视为此处的特殊情况。它们生成与函数具有外部链接时完全相同的代码,在这种情况下,参数 c0 和 c1 之间没有假定连接。

在函数内部,c0c1 根据 ABI 调用约定驻留在本地副本中,这不一定将它们彼此相邻放置以进行对齐访问 - 即使您的情况确实如此特定的 x86 示例。

将调用者代码更改为alignas(int) char str[2] = {'0', 'x'}; return beginswith(str[0],str[1],s); 并没有什么不同,因此对齐似乎也不是(唯一)原因。

但是,如果您将功能更改为:

inline bool beginswith (char c0, char c1, const char* s)
{
    char tmp[2] = {c0, c1};
    return memcmp(tmp, s, 2);
}

那么您在两种情况下都会得到相同的机器代码 (godbolt]。

test(char const*):
  cmp WORD PTR [rdi], 30768
  setne al
  ret

需要注意的是,*(const short*)s 中的test2 是未定义的行为,是严格的别名违规。当程序开始扩展或更改优化器设置时,该行的机器代码可能无法保持确定性。这主要是实施质量问题,标准不做任何保证,test2 可能随时中断。

【讨论】:

  • 注意:在 C 中,您可能已经能够使用联合类型双关语来躲避这种未定义的行为,但在 C++ 中这是不可能的。
  • 参数传递是一个红鲱鱼。您可以在 C++ 中将参数更改为 char const (&)[2],但编译器仍然不会合并比较。优化器中似乎没有任何规则可以合并此类比较。
  • 在您的对齐示例中,s 指向的数据仍然未对齐。
  • @MaximEgorushkin 又不是这个...不,他们不能。您可以从指针类型转到指针到字符,然后作为字符类型取消引用但不能反过来What is the strict aliasing rule?.
  • 是的,但这仅在原始缓冲区确实是一个短数组时才有效。如果您有一个 double 数组,并将其转换为 char*,那么在其他地方将其作为 short 访问作为优化可能会有问题。诚然,除非它在通过double* 进行变异的上下文中被内联,否则它可能不会破坏任何东西。
猜你喜欢
  • 2015-05-13
  • 1970-01-01
  • 2011-04-17
  • 1970-01-01
  • 2016-05-24
  • 2014-09-28
  • 2018-11-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多