【问题标题】:strlen() compile time optimizationstrlen() 编译时优化
【发布时间】:2016-07-23 05:51:21
【问题描述】:

几天前我发现你可以使用类似这样的方式找到编译时strlen

template<size_t N>
constexpr size_t strlen_(const char (&data)[N]) noexcept{
    return N - 1;
}

如果它被编译,那么一切都很好。您可以这样添加重载:

size_t strlen_(const char *s) noexcept{
    return strlen(s);
}

然后它会一直编译。

我的问题是 - C++ &lt;cstring&gt; 是否使用类似的东西,如果不使用 - 为什么?

【问题讨论】:

  • 如果我使用char x[3] = {'a', 'b', 'c'};,那会产生意想不到的结果...
  • @KerrekSB,这不是 C 风格的字符串。
  • char x[10] = "abc"; 虽然是一个 c 字符串,但会产生意想不到的结果。
  • 它没有。没有编译时优化 - 它占用了很多空间。
  • “如果我让我的代码更复杂和晦涩,也许编译器会更好地优化它”——怀疑。

标签: c++ arrays string c++11


【解决方案1】:

你的代码有问题。

strlen() 上进行编译时优化的正确方法令人惊讶的是,只需调用strlen()。当它在编译时知道长度时,像 clang 这样的现代编译将优化掉不必要的strlen()

此外,在大多数情况下,sizeof 会在您(程序员)与保存文字字符串的变量一起正确使用时派上用场。喜欢:

const char foo[] = "Hello World";
size_t len = sizeof(foo)-1; // 11

请注意,这是有一些假设的,如果你这样做会给自己带来麻烦,但不会让你的生活变得简单:

const char foo[] = "Hello World\01234";
size_t len = sizeof(foo)-1; // 16

已编辑:使显而易见的看起来更像是一个答案。

【讨论】:

    【解决方案2】:

    不,它没有。因为它给出了错误的答案。

    char x[10] = "abc";
    int correct_length   = std::strlen(x);  // 3
    int incorrect_length = strlen_(x);      // 9
    

    此外,使用您的 2 个重载,模板将永远不会被调用。接受const char* 的非模板将始终是首选。

    这并不意味着 strlen 不能在编译时用一些编译器魔法来计算。但不能以这种方式完成,使用适当的语言。

    【讨论】:

    • char x[10] = "abc"; 不是常量字符串
    • @Calvin:你的意思是什么? strlen 需要同时处理常量和非常量字符串,即使我将其设为常量,结果也不会有所不同。
    • 我认为关键是非常量字符串与模板中的 const arg 不匹配——所以只有 strlen_("abc") 这样的东西会匹配并且它会给出正确的结果
    • @Soren:两个帐户都错了。
    • 我们只能对编译时已知长度的常量文字字符串进行编译时优化。传递非 const char 数组意味着内容可能会改变编译器的视线,这应该回退到传统的strlen(),而不是 OP 关心的问题。
    【解决方案3】:

    您的strlen_ 不起作用:http://ideone.com/PNNeaX

    另外:我知道,Visual Studio 2015 优化了各种 strlen 调用,即使在我有疑问的情况下,当我检查程序集输出时这些都消失了,它没有生成 runtime @ 987654325@ 电话。

    正如@Calvin 所说,Clang 似乎也在做同样的事情。所以真的没有理由这样做。

    【讨论】:

    • 不明白这个问题的答案?
    • 其实它确实回答了这个问题
    【解决方案4】:

    您的 strlen_返回数组data 的大小,而不是空终止字符串的大小。正确的实现是:

    constexpr size_t ct_strlen( const char* s ) noexcept
    {
      return *s ? 1 + ct_strlen(s + 1) : 0;
    }
    

    【讨论】:

    • 1 起来。我故意没有对这个解决方案说什么。但是,如果它在运行时执行,这会减慢很多。 C++17 允许“正常”for 循环,所以我认为这将是一个解决方案。
    • @Nick 会吗?我在这里看到了一个尾递归优化机会,可以使它在性能上与实际的strlen 相当。诚然,这需要一些代码重新排列,我不知道编译器是否会这样做。
    • 我忘记了 C 尾递归已经优化多年了。我会试试的。
    【解决方案5】:

    ZDF 回答的跟进。

    我做了测试,这似乎是过早的优化。

    代码:

    #include <cstdio>
    #include <cstring>
    
    constexpr unsigned MAX = 1000 * 1000 * 250;
    
    
    constexpr size_t strlen_c(const char* s) noexcept{
        return *s ? 1 + strlen_c(s + 1) : 0;
    }
    
    
    int main(){
        constexpr const char *s = "Hi, my name is Malcolm and I am very good boy...";
    
        unsigned sum = 0;
    
        for(unsigned i = 0; i < MAX; ++i){
            sum += strlen(s) + i;
        }
    
        return sum;
    }
    

    结果:

    代码编译时使用:

    gcc   x.cc -std=c++11 -Wall -Wpedantic
    gcc   x.cc -std=c++11 -Wall -Wpedantic -O3
    clang x.cc -std=c++11 -Wall -Wpedantic
    clang x.cc -std=c++11 -Wall -Wpedantic -O3
    

    这是花了多少时间:

    ------------------------------------
    |         | GCC       | CLANG      |
    ------------------------------------
    | len     | 0m1.506s  | 0m0.743s   |
    | len O3  | 0m0.001s  | 0m0.002s   |
    | lenc    | 1m5.476s  | 0m56.871s  |
    | lenc O3 | 0m12.267s | 0m0.060s   |
    ------------------------------------
    

    Clang 似乎要快一些,但经过全面优化,constexpr strlen_c() 没有得到回报。

    如果我在代码中做错了什么,请告诉我。

    【讨论】:

    • clang 和 gcc 至少能够检测字符串是否被声明为常量表达式(看起来这是可选的,不是标准的),并且它将对 strlen 的调用替换为字符串的长度。因此,担心速度优化是没有意义的。请参阅 here 程序集输出。
    猜你喜欢
    • 2010-11-26
    • 2011-08-11
    • 2012-03-04
    • 1970-01-01
    • 2011-11-07
    • 1970-01-01
    • 1970-01-01
    • 2012-03-21
    • 1970-01-01
    相关资源
    最近更新 更多