【问题标题】:Duplicate literals and hard-coding重复文字和硬编码
【发布时间】:2011-03-09 01:42:26
【问题描述】:

我经常看到以下模式:

 b->last = ngx_cpymem(b->last, "</pre><hr>", sizeof("</pre><hr>") - 1);

请注意,文字字符串被使用了两次。提取物来自 nginx 源代码库。

当在编译单元中遇到这些文字时,编译器应该能够合并这些文字。

我的问题是:

  1. 商业级编译器(VC++、GCC、LLVM/Clang)在编译单元中遇到这种冗余时是否会删除?
  2. (静态)链接器在链接目标文件时是否会删除此类冗余。
  3. 如果应用 2,这种优化会在动态链接期间发生吗?
  4. 如果 1 和 2 适用,它们是否适用于所有文字。

这些问题很重要,因为它允许程序员在不损失效率的情况下变得冗长——例如,考虑将大量静态数据模型硬连接到程序中(例如,在某些低端中使用的决策支持系统的规则)水平场景)。

编辑

2 点/说明

  1. 上面的代码是由公认的“大师”程序员编写的。那家伙单枪匹马写了nginx。

  2. 我没有问过哪种可能的文字硬编码机制更好。所以不要跑题。

编辑 2

我最初的例子是相当做作和限制性的。下面的 sn-p 显示了嵌入到内部硬编码知识中的字符串文字的用法。第一个 sn-p 用于配置解析器,告诉它要为哪个字符串设置什么枚举值,第二个更普遍地用作程序中的字符串。只要编译器使用字符串文字的一份副本,我个人对此感到满意,并且由于元素是静态的,它们不会进入全局符号表。

static ngx_conf_bitmask_t  ngx_http_gzip_proxied_mask[] = {
   { ngx_string("off"), NGX_HTTP_GZIP_PROXIED_OFF },
   { ngx_string("expired"), NGX_HTTP_GZIP_PROXIED_EXPIRED },
   { ngx_string("no-cache"), NGX_HTTP_GZIP_PROXIED_NO_CACHE },
   { ngx_string("no-store"), NGX_HTTP_GZIP_PROXIED_NO_STORE },
   { ngx_string("private"), NGX_HTTP_GZIP_PROXIED_PRIVATE },
   { ngx_string("no_last_modified"), NGX_HTTP_GZIP_PROXIED_NO_LM },
   { ngx_string("no_etag"), NGX_HTTP_GZIP_PROXIED_NO_ETAG },
   { ngx_string("auth"), NGX_HTTP_GZIP_PROXIED_AUTH },
   { ngx_string("any"), NGX_HTTP_GZIP_PROXIED_ANY },
   { ngx_null_string, 0 }
};

紧随其后:

static ngx_str_t  ngx_http_gzip_no_cache = ngx_string("no-cache");
static ngx_str_t  ngx_http_gzip_no_store = ngx_string("no-store");
static ngx_str_t  ngx_http_gzip_private = ngx_string("private");

致那些一直关注主题的人,好样的!

【问题讨论】:

  • 抱歉,但您担心高辛烷值燃油会使车开得更快,但不考虑道路上到处都是坑洞。通过将文字放在常量中来选择更好的道路。 (好吧,这不是最好的比喻,但它在一定程度上有效)
  • 你为什么想要在你的代码中重复你自己?一个更好的问题是“我可以避免冗长而又不降低效率吗?”
  • 虽然我必须同意 jalf (“主”程序员与否),但您的问题的答案是肯定的,所有体面的编译器不仅会在同一个翻译单元中合并重复的字符串文字,但链接器还将跨翻译单元合并重复项。即使对于像 CW 8 这样的过时编译器也是如此,它提供了这个作为链接器选项。当然,这显然不会在共享库/DLL 中发生。但是,如果您的代码依赖于重复的字符串文字来共享相同的地址,那么代码就是一场等待发生的灾难。
  • 你不会通过某人喜欢的软件成为“大师”程序员。你可以通过编写好的代码来做到这一点。

标签: c++ c micro-optimization compiler-theory string-interning


【解决方案1】:

请注意,对于 sizeof("&lt;/pre&gt;&lt;hr&gt;") 的特定情况,几乎可以肯定字符串文字永远不会出现在输出文件中 - 整个 sizeof 表达式可以在编译时计算为整数常量 11。

尽管如此,对于编译器来说,合并相同的字符串文字仍然是一种非常常见的优化。

【讨论】:

  • @Pmod:我不这么认为,但我还没有玩过 GCC 的新“链接时间优化”功能。
  • sizeof 表达式是编译时常量(根据 C++ 标准)。所以“几乎确定”似乎是轻描淡写。
  • @MSalters:并非总是如此,至少在 ISO C99 中是这样。想想 VLA。不会有人请考虑 VLA 吗?
【解决方案2】:

我无法回答您的问题,但在这种情况下始终尝试使用 const 字符串(甚至 #define 会更好)。当您重构代码并更改一个文字的值而忘记另一个文字时,问题就出现了(在您的示例中不太可能,因为它们彼此相邻,但我以前见过)。

无论编译器能做什么优化,人类仍然可以把它搞砸:)

【讨论】:

  • 最后一行一百万次,其余的也是!冗余尖叫“在此处插入错误”。
  • 其实我只是想帮忙,但是嘿。
  • 啊,我希望我有足够的成熟度来放手,但我没有:)。我敢肯定,搜索这种复杂代码内部的人知道常量上的 sizeof() 将被优化,但这是您选择的答案;也许提供基本信息有时甚至对您这样的专家也有用,我向您保证,当我提供我的专业知识时,我并不是要质疑您的专业知识。
  • 最后,我宁愿冒着激怒专家的风险,提出他们不需要的建议,而不是不去帮助那些需要的人
  • 没关系,我很乐意为大多数事情争论,不会轻易被冒犯。我认为你混淆了你的定义,狭隘的视野将纯粹专注于这个问题,而不考虑潜在的更广泛的问题;至于长期有效性,仅仅因为你没有找到有用的东西并不意味着没有其他人会。
【解决方案3】:
  1. 对于 GCC 是这样,对于其他人来说也应该如此
  2. 对于 GNU 链接器可能是的(请参阅 -fmerge-constants、-fmerge-all-constants)
  3. 没有
  4. 不确定

【讨论】:

    【解决方案4】:

    我会很不高兴看到这种模式 - 如果有人改变了一个文字而不改变另一个怎么办?应将其拔出;制作一个非常小的命名常量。

    假设你不能出于某种原因,或者只是为了实际回答这个问题:(至少,轶事。)

    我用 C 语言编写了一个类似的程序并用 GCC 4.4.3 编译它,常量字符串在生成的可执行文件中只出现一次。

    编辑:因为它可能作为一个简单的测试有用,这里是我测试它的代码......

    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    
    main(){
        char *n = (char*)malloc(sizeof("teststring"));
        memcpy((void*)n, "teststring", sizeof("teststring"));
        printf("%s\n", n);
    }
    

    这是我用来检查字符串出现次数的命令...

    strings a.out|grep teststring
    

    但请考虑尽可能使用不易出错的编码实践。

    【讨论】:

    • 编译器会为你做这个优化。 +1 用于验证的命令行
    【解决方案5】:

    我写了一个小示例代码并编译:

    void func (void)
    {
        char ps1[128];
        char ps2[128];
    
        strcpy(ps1, "string_is_the_same");
        strcpy(ps2, "string_is_the_same");
    
        printf("", ps1, ps2);
    }
    

    因此,即使没有优化,汇编文件中也只有一个文字“string_is_the_same”实例。但是,不确定这些字符串是否没有被重复放入不同的文件 -> 不同的目标文件中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-11-07
      • 2021-09-04
      • 2023-01-07
      • 2010-09-17
      • 2013-07-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多