【问题标题】:Should I use const for local variables for better code optimization?我应该将 const 用于局部变量以进行更好的代码优化吗?
【发布时间】:2012-05-31 15:40:36
【问题描述】:

我经常将const用于没有被修改的局部变量,像这样:

const float height = person.getHeight();

我认为它可以使编译的代码可能更快,允许编译器进行更多优化。还是我错了,编译器可以自己弄清楚局部变量永远不会被修改?

【问题讨论】:

  • 我不会感到惊讶。
  • AFAIK const 不做任何优化,volatile 做
  • 当值为编译时常量时,优化更实用。您可能感兴趣的更一般的讨论可以在这里找到:stackoverflow.com/questions/212237/…
  • @zadane,我很确定它确实开启了某些优化。但是,不要忘记过早优化是万恶之源。为了这个目的考虑这个是浪费时间。程序运行后,如果需要加快程序速度,请选择更好的算法或其他东西。
  • @chris: “过早的优化是万恶之源” 你所要做的就是添加一个常量。请向我解释这怎么可能是邪恶的。如果有的话,它将使代码更安全,这与邪恶完全相反。此外,整句话是“过早的优化是万恶之源。但我们不应该放弃那关键的 3% 的机会。”。来源:link

标签: c++ performance optimization constants compiler-optimization


【解决方案1】:

还是我错了,编译器可以自己弄清楚局部变量永远不会被修改?

大多数编译器都很聪明,可以自己解决这个问题。
您应该使用 const 来确保 const-correctness 而不是微优化。
const 正确性 让编译器帮助您防止犯诚实的错误,因此您应该尽可能使用const,但出于可维护性原因防止自己犯错愚蠢的错误

了解我们编写的代码对性能的影响是件好事,但应避免过度的微优化。关于性能,一个应该遵循的,

80-20 规则:

通过代表性数据集通过分析,确定您的代码的20% 使用您的资源的80% 然后才尝试优化这些瓶颈。

【讨论】:

  • +1 你写了我看到问题时想写的内容。不过,我想我会提到“约束”和“清晰”这两个词。然而。 :-)
【解决方案2】:

这种性能差异几乎可以忽略不计,但是出于代码文档的原因,您应该尽可能使用 const。通常,编译器无论如何都可以为您解决这个问题并自动进行优化。 const 更多的是关于代码的可读性和清晰度,而不是性能。

【讨论】:

    【解决方案3】:

    我认为将包括函数参数在内的局部变量默认设为常量并不是一个好习惯。

    主要原因是简洁。良好的编码实践可以让您的代码更短,而这个不行。

    同样,你可以在你的函数声明中写void foo(void),你可以通过提高清晰度、明确不打算将参数传递给函数等来证明它的合理性,但这本质上是浪费空间,并最终几乎不再使用。我认为到处使用const 的趋势也会发生同样的事情。

    使用 const 限定符标记局部变量对于大多数使用您创建的代码的协作者来说不是很有用。与类成员、全局变量或指针指向的数据不同,局部变量没有任何外部影响,没有人会受到局部变量限定符的限制或从中学到任何有用的东西(除非他将更改局部变量所在的特定函数)。

    如果他需要更改您的功能,该任务通常不应该要求他尝试从那里的变量的常量限定符中推断出有价值的信息。函数不应太大或难以理解;如果是这样,您的设计可能有更严重的问题,请考虑重构。一个例外是实现一些核心数学计算的函数,但对于那些你需要在你的 cmets 中放置一些细节或论文链接。

    您可能会问如果不花费您太多精力,为什么不仍然使用 const 限定符。不幸的是,确实如此。如果我必须放置所有 const 限定符,我很可能必须在完成后检查我的函数并将限定符放置到位 - 浪费时间。这样做的原因是您不必仔细计划局部变量的使用,这与成员或指针指向的数据不同。

    它们大多是一种便利工具,从技术上讲,可以通过将它们分解为表达式或重用变量来避免它们中的大多数。因此,由于它们是一种方便的工具,特定局部变量的存在只是一种口味问题。

    特别是,我可以写:

    • int d = foo(b) + c;
    • const int a = foo(b); int d = a + c;
    • int a = foo(b); a += c

    除了变量a 是常量或非常量,或者根本不存在之外,每个变体在各个方面都是相同的。很难尽早做出某些选择。

    【讨论】:

    • 省略 const 会使编写代码稍微快一些,但读取/理解/维护该代码的速度要慢得多。特别是,如果我在函数顶部看到const int a = foo(b),那么当我阅读函数的其余部分时,我可以放心a 在函数中的所有点都将具有相同的值。没有const 关键字,我不再有这个保证,如果我想知道a 在特定位置的值是什么,我将不得不手动遍历a 声明之间的所有代码路径和那个使用点来尝试弄清楚。
    • @JeremyFriesner:const 的另一个好处是,将其与地址传递给外部的结构一起使用可能会导致编译器假设外部代码不会更改其内容——编译器无权进行的假设。
    • @supercat 你会这么认为,但实际上编译器通常不能利用 const 的存在作为进行额外优化的机会,因为编译器通常不切实际证明没有其他函数仍然可以通过其他途径在幕后修改 const-pointed-to-object。见:stackoverflow.com/questions/27466642/…
    • @JeremyFriesner:编译器不能将此类优化应用于 const 限定的 指针,但如果为对象分配空间的声明将其限定为 const 但不是const volatile,在对象的生命周期内,除了在其初始化程序中使用的值(不调用 UB)之外,没有任何方法可以保存任何值。
    【解决方案4】:

    如果左侧有一个值类型,您可以放心地认为它的影响可以忽略不计,或者根本没有影响。它不会影响重载决议,实际上const 可以很容易地从范围中推断出来。


    引用类型完全不同:

    std::vector<int> v(1);
    const auto& a = v[0];
    auto& b = v[0];
    

    这两个赋值解析为两个完全不同的运算符,并且在 STL 之外的许多库中也可以找到类似的重载对。即使在这个简单的示例中,依赖于 v 的优化对于 b 的范围是不可变的已经不再是微不足道的并且不太可能被发现。

    尽管如此,STL 在这些方面仍然相当温和,至少行为不会因选择const_reference 重载而改变。对于大多数 STL,const_reference 重载仅与 const 本身的对象相关联。

    其他一些库(例如 Qt)大量使用了写时复制语义。在这些 const-correctness 中,引用不再是可选的,而是必要的:

    QVector<int> v1(1);
    auto v2 = v1; // Backing storage of v2 and v1 is still linked
    const auto& a = v1[0]; // Still linked
    const auto& b = v2[0]; // Still linked
    auto& c = v2[0]; // Deep copy from v1 to v2 is happening now :(
    // Even worse, &b != &c
    

    写时复制语义是大型矩阵或图像处理库中常见的东西,需要注意。

    这也是编译器不再能够拯救你的地方,重载解决方案是 C++ 标准强制要求的,没有消除代价高昂的副作用的余地。

    【讨论】:

      【解决方案5】:

      局部常量值存在一个主要问题 - 如下代码所示:

      #include <stdio.h>
      #include <stdlib.h>
      #include <stdint.h>
      
      // const uint32_t const_dummy = 0;
      
      void func1(const uint32_t *ptr);
      
      int main(void) {
          const uint32_t const_dummy = 0;
          func1(&const_dummy);
          printf("0x%x\n", const_dummy);
          return EXIT_SUCCESS;
      }
      
      void func1(const uint32_t *ptr) {
          uint32_t *tmp = (uint32_t *)ptr;
          *tmp = 1;
      }
      

      此代码是在 Ubuntu 18.04 上编译的。

      如您所见,const_dummy 的值在这种情况下可以修改! 但是,如果您修改代码并将 const_dummy 范围设置为全局 - 通过注释掉本地定义并从全局定义中删除注释 - 您将收到异常并且您的程序将崩溃 - 这很好,因为您可以调试它并找到问题。

      是什么原因?那么全局const 值位于程序的ro(只读)部分。操作系统 - 使用 MMU 保护该区域。 使用堆栈中定义的常量是不可能的。

      对于不使用 MMU 的系统 - 您甚至不会“感觉”有问题。

      【讨论】:

      • 这是错误的。 const 变量是局部的还是全局的都是未定义的行为。它只是碰巧在现实中产生了不同的效果。但现实的结果是,例如,您编写的程序仍然是0
      • 我刚刚提供的代码很糟糕 - 但它来介绍问题的另一种观点。通过使用 objdump - 如果将 const 变量定义为全局变量,则可以查看它的位置。我建议获取代码,并使用全局和本地定义检查它。只需注释掉正确的定义。
      • 还是没关系的。通过强制转换修改最初声明为 const 的变量是未定义的行为。编译器的具体输出是什么无关紧要。不要编写这样的程序。
      • 没有人写这样的程序。然而,有不同的方法可以使用 memcpy 将“const”局部变量的内容覆盖到局部变量的附近地址或类似的地址。我曾与设法破坏本地 const 变量内容的程序员合作过——但并未真正理解发生了什么。
      • Tehre 不是有效的 C++ 程序,其中 const 的内容发生了变化。
      猜你喜欢
      • 2014-04-04
      • 2010-12-15
      • 2019-01-17
      • 2022-01-17
      • 1970-01-01
      • 2020-09-11
      • 1970-01-01
      • 1970-01-01
      • 2022-01-16
      相关资源
      最近更新 更多