延迟声明变量是否更加高效?

3

将变量声明放在后面是否更节省内存或者更高效?

示例:

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

对比

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

谢谢。

5
最好将 x 保留在使用它的地方,这样程序员就能保持头脑清晰、专注于算法而不是这些细节。请注意,这并不改变原意。 - Johannes Schaub - litb
我想,“先声明,后编写代码”的习惯可能源自Pascal和相关语言的古老时代,并一直延续至今。 - Captain Giraffe
2
这实际上是C89遗留下来的产物,当时编译器需要提前看到所有变量以确定堆栈大小。现代编译器直到变量初始化时才分配堆栈空间,即使它早先被声明。 - MSalters
4个回答

9

请按照逻辑编写代码(通常更接近实际使用)。编译器可以并且会发现这样的问题,并生成最适合目标架构的代码。

你的时间比试图猜测编译器和处理器缓存之间的交互要宝贵得多。


例如,在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;
}

vs

#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生成的汇编代码差异仅在文件名上有所区别,因为没有副作用。如果有副作用,则会决定您构建对象的位置-您希望何时发生副作用?


@AAT - 可能还有第三件事要避免猜测 - 下一个十年处理器的行为! - Flexo

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

5
一般来说,你应该声明变量的使用位置和时间。这样可以提高代码的可读性、可维护性,并且出于纯粹实用的原因,内存局部性也会得到改善。
即使你有一个大对象,无论是在外部声明还是在循环体内声明,唯一的区别就在于构造和赋值;由于现代分配器非常擅长短期分配,实际的内存分配将几乎相同。
如果你有一小部分代码不需要后续使用其变量(尽管这通常表示你最好使用单独的函数),甚至可以考虑创建新的匿名范围。
所以,基本上就写最合逻辑的方式,通常也会得到最有效的代码;或者至少你不会比将所有东西都声明在顶部做得更糟。

4

对于简单类型来说,无论是哪种方式都没有更高的内存或计算效率。但对于更复杂的类型,将内容保持在缓存中(从构建时开始)并靠近使用它们的位置可能更加有效。这也可以最小化内存保留的时间。


2
另一方面,如果构建需要昂贵的操作,比如从主存中读取数据,那么在非依赖操作完成的同时,更早地将其流水线化可能更加高效。 - Peter Alexander
正确。如果构造具有副作用,您可能无法选择它的位置。最接近于一般规则的是,通常最好将它们放在使用它们的地方附近,但如果您有任何理由不这样做,那通常就足够了。 - David Schwartz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接