【问题标题】:Is it more efficient to declare variables late?延迟声明变量是否更有效?
【发布时间】:2011-12-09 14:31:23
【问题描述】:

延迟声明变量是更多的内存还是计算效率更高?

例子:

int x;
code
..
.
.
. x is able to be used in all this code
.
actually used here
.
end

code
..
.
.
.
int x;
actually used here
.
end

谢谢。

【问题讨论】:

  • 最好将x 保留在使用它的位置,因为程序员会保持头脑清醒,查看代码并专注于算法而不是这些细节。
  • 我想象-First 声明,-Then 代码;来自帕斯卡和相关语言的古老时代,并且一直存在。
  • 它实际上是 C89 的遗留物,当时编译器需要预先查看所有变量以确定堆栈大小。现代编译器在变量初始化之前不会分配堆栈空间,即使它之前声明过也是如此。

标签: c++ performance variables


【解决方案1】:

写任何逻辑上最有意义的东西(通常更接近使用)。编译器可以并且将会发现这样的事情并生成对您的目标架构最有意义的代码。

您的时间比试图猜测编译器和处理器缓存的交互更有价值。


例如在 x86 上这个程序:

#include <iostream>

int main() {
  for (int j = 0; j < 1000; ++j) {
    std::cout << j << std::endl;
  }
  int i = 999;
  std::cout << i << std::endl;
}

相比:

#include <iostream>

int main() {
  int i = 999;
  for (int j = 0; j < 1000; ++j) {
    std::cout << j << std::endl;
  }
  std::cout << i << std::endl;
}

编译:

g++ -Wall -Wextra -O4 -S measure.c
g++ -Wall -Wextra -O4 -S measure2.c

当使用diff measure*.s 检查输出时:

<       .file   "measure2.cc"
---
>       .file   "measure.cc"

即使是:

#include <iostream>

namespace {
  struct foo {
    foo() { }
    ~foo() { }
  };
}

std::ostream& operator<<(std::ostream& out, const foo&) {
  return out << "foo";
}

int main() {
  for (int j = 0; j < 1000; ++j) {
    std::cout << j << std::endl;
  }
  foo i;
  std::cout << i << std::endl;
}

#include <iostream>

namespace {
  struct foo {
    foo() { }
    ~foo() { }
  };
}

std::ostream& operator<<(std::ostream& out, const foo&) {
  return out << "foo";
}

int main() {
  foo i;
  for (int j = 0; j < 1000; ++j) {
    std::cout << j << std::endl;
  }
  std::cout << i << std::endl;
}

g++ -S 生成的程序集的差异结果除了文件名之外仍然相同,因为没有副作用。如果有副作用,那么这将决定您在哪里构建对象 - 您希望在什么时候出现副作用?

【讨论】:

  • +1 for Your time is far more valuable than trying to second guess the interactions of the compiler and the cache on the processor. 我希望更多的人能够让技术继续优化 99% 的代码并继续进行更高效的工作!
  • @AAT - 甚至可能还有第三件事要避免第二次猜测 - 未来十年处理器的行为!
【解决方案2】:

对于int 等基本类型,从性能的角度来看并不重要。对于class 类型,变量定义还包括构造函数调用,如果控制流跳过该变量,则可以省略该调用。此外,对于基本类型和class 类型,定义应至少延迟到有足够信息使此类变量有意义的程度。对于非默认可构造类类型,这是强制性的;对于其他类型,它可能不是,但它会强制您使用未初始化的状态(如 -1 或其他无效值)。您应该在尽可能小的范围内尽可能晚地定义变量;有时从性能的角度来看可能无关紧要,但在设计方面总是很重要。

【讨论】:

    【解决方案3】:

    一般来说,您应该声明使用变量的位置和时间。它提高了可重复性、可维护性,并且出于纯粹的实际原因,还提高了内存局部性。

    即使你有一个大对象并且你在循环体外部或内部声明它,唯一的区别将是构造和赋值之间;实际的内存分配几乎是相同的,因为当代分配器非常擅长短期分配。

    如果您有一小部分代码之后不需要其变量,您甚至可以考虑创建新的匿名作用域(尽管这通常表明您最好使用单独的函数)。

    所以基本上,以最合乎逻辑的方式编写,您通常也会得到最高效的代码;或者至少你不会比在顶部声明所有内容做得更糟。

    【讨论】:

      【解决方案4】:

      对于简单类型来说,无论是内存还是计算效率都不高。对于更复杂的类型,将内容热在缓存中(从被构造的地方)靠近它们使用的地方可能会更有效。它还可以最大限度地减少内存保持分配的时间。

      【讨论】:

      • 另一方面,如果构造需要昂贵的操作,例如从主内存中读取,那么在非依赖操作同时完成的同时更早地进行流水线操作可能会更有效。
      • 是的。如果施工有副作用,您可能无法选择在哪里完成。最接近一般规则的是,通常最好将它们放在靠近使用它们的地方,但如果您有任何理由不这样做,通常就足够了。
      猜你喜欢
      • 2019-10-02
      • 1970-01-01
      • 1970-01-01
      • 2021-05-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-03
      相关资源
      最近更新 更多