【问题标题】:What is strlen elision?什么是strlen省略?
【发布时间】:2018-04-08 03:58:30
【问题描述】:

我可以将see it listed 作为现代 C++ 习语之一,但它到底是什么?

它只是copy elision的一种吗?

【问题讨论】:

标签: c++ c++17 strlen


【解决方案1】:

您可能知道std::strlen 循环遍历整个字符串。在某些情况下,这可能效率低下,因为这意味着 CPU 必须开始计算字符数,这会降低其他内容的缓存局部性。

但在大多数情况下,编译器能够优化std::strlen 并计算字符串本身的字符数,而不是让生成的程序执行此操作。这有时称为 strlen 省略(因为它省略了对 strlen 的调用)。

std::strlen 中的simple example 将完全得到优化。

#include <cstring>

int main() {
    return std::strlen("hi");
}

-O3 下生成的程序集是

main: # @main
  mov eax, 2
  ret

即使under -O0 也不会生成循环!

现在这是一个相当简单的示例,但它甚至适用于更复杂的 std::strlen 用法。

#include <cstring>

int main(int argc, char**) {
    const char *string;
    if (argc == 1)
        string = "hello";
    else if (argc == 2)
        string = "world";
    else if (argc == 3)
        string = "!";

    return std::strlen(string); 
}

这里的字符串完全是optimized away,以及std::strlen 调用。

【讨论】:

  • 非常感谢。你认为这样的优化是否可能在不涉及字符串文字的情况下实现?
  • @DusanJovanovic 不是。如果不涉及字符串文字,则编译器必须知道字符串的实际长度。但是编译器总是会让你大吃一惊:)
  • 使用“完整程序优化”(由微软的链接器执行),那么很多事情都是可能的。例如,如果一个字符串字面量被传递给一个函数,该函数又调用 strlen,并且该函数被链接器内联,那么对 strlen 的调用就可以省略了。如果链接器和编译器一样聪明,那就是。
【解决方案2】:

恕我直言,如果编译器理解 strlen 的语义,并且如果有问题的字符串在编译时已知,那么编译器可以只发出一个常量而不是评估运行时字符串的长度(通过任何方式)。这就是这里发生的一切。相信微软的营销部门会大肆宣传这样一个简单的功能。

对这一切感兴趣的是(优化)编译器将常用的运行时库函数视为内在函数并在可能的情况下为它们发出更好的代码的增长趋势。这已经持续了一段时间 - memcpy 和 memset 是明显的例子 - 但我最近在 Godbolt 的 Compiler Explorer 中玩耍,很惊讶地看到 gcc 替换了它:

printf (" ");

用这个:

putchar (' ');

震惊?我也是,我不确定我是否喜欢它。我可能有一个定制的 printf 可以做一些特别的事情,而这种诡计会让人大吃一惊。在我看来,如此小的收益(编译器编写者)是一个很大的风险。

【讨论】:

  • 如果你有一个自定义的printf,那么你不是在编写标准的 C 或 C++,而是你自己。
  • 你觉得呢?它在标准中的什么地方说“您不应替换标准库函数”(通过编写替换并将其链接)?当然,对于像 strlen 这样可能被视为内在的东西,这是最不明智的,但是对于 printf?我真的认为编译器应该只调用一个像这样复杂的函数。这就是我试图说明的一点——通过调用 printf 来胡闹远远超出了大多数人期望编译器做的事情——并不是我一定想要这样做。
  • 标准库函数的名称是带有外部链接的名称。因此,它们是保留的,定义任何这些名称的程序的行为是未定义的。 [reserved.names]/2.
  • 换句话说,欢迎你写myprintf,但你不应该尝试写自己的printf
  • 好吧,这很公平,我也没有打算这样做,但我确实认为编译器作者在这里走得太远了。没有任何收获,何必呢?
猜你喜欢
  • 2014-01-19
  • 2015-08-08
  • 2017-03-12
  • 2013-06-14
  • 2021-10-29
相关资源
最近更新 更多