在循环中创建一个对象

4
 std::vector<double> C(4);
 for(int i = 0; i < 1000;++i)
  for(int j = 0; j < 2000; ++j)
  {   
   C[0] = 1.0;
   C[1] = 1.0;
   C[2] = 1.0;
   C[3] = 1.0;
  }

比起其他选项,速度更快

 for(int i = 0; i < 1000;++i)
  for(int j = 0; j < 2000; ++j)
  {
   std::vector<double> C(4);
   C[0] = 1.0;
   C[1] = 1.0;
   C[2] = 1.0;
   C[3] = 1.0;
  }

我意识到这是因为在循环中重复创建和实例化了std::vector,但我认为这应该被优化掉。

尽可能在循环中保留变量局部是否完全错误?我曾经有这样(也许是错误的)的印象,这能够为编译器提供优化机会。

或者这只适用于POD类型,而不适用于像std::vector这样的东西。

编辑:我使用 VC++2005(发布模式)和 Windows XP 上的全优化 (/Ox)。


1
由于对象实例化可能会产生副作用,但当然,这是一个STL容器,所以您的编译器可能猜测不会发生。顺便问一下,您是否使用了正确的优化标志进行编译? - Pieter
1
你使用了哪个编译器、操作系统和编译器选项? - David Thornley
我敢打赌,double C[4]; 会更快,因为你正在支付动态内存分配的费用,但实际上并不需要它。 - Brian
1
优化器在需要时确实很好用,但优化真正是程序员的工作。写一些愚蠢的东西只是为了看看优化器有多聪明,这有什么意义呢?这就像走在汽车前面看司机是否注意一样危险。 - Mike Dunlavey
@Mike:没想到最小化对象的范围是愚蠢的。现在我知道了。 - Jacob
@Jacob:如果我在选择用词方面的知识只有编程的十分之一,那我就不会这么生硬了。而且,我早已忘记了我初学时的问题。你做得很好。 - Mike Dunlavey
7个回答

3
在循环中尽可能让变量保持局部性完全没有问题,这是一个好的经验法则。最小化变量的作用域可以为编译器提供更多的寄存器分配和其他优化自由度,并且至少同样重要的是,通常会产生更易读的代码。但这也取决于重复创建/销毁操作是否廉价或被完全优化掉,这通常是可行的......但并不总是。因此,正如你发现的那样,有时这是个坏主意。

谢谢jalf,我想这只是一个经验法则,我不能指望全能的编译器在没有进行性能分析之前就优化std::vector等东西。 - Jacob
有些编译器在某些情况下可能能够做到这一点。当然,在许多其他情况下,性能并不重要。但这次不一样。 :) - jalf

2
问题出在堆活动上。将 std::vector<double> C(4); 替换为 std::array<double, 4> C;,这样无论您将变量放在哪里都不会有任何区别。

1
谷歌表示,std::array是C++0x的一部分。 - Dummy00001
然后使用 boost,或者 C 风格的数组。 - Pete Kirkham
或者如果您使用的是“半现代”的编译器,则可以使用std::tr1::array - fredoverflow

2
我曾经认为这会为编译器提供优化机会,但这可能是错误的印象。对于像int或double这样的内置类型,这可能是正确的。
问题在于您正在使用vector,它需要在进入循环体时运行构造函数,在离开时运行析构函数。由于这两种方法都是非平凡的,因此编译器无法将其优化掉,否则您的程序将不再正确。
作为对此的反例,请想象一下如果您使用文件对象而不是向量,这样的优化会发生什么。

1

vector 的创建是昂贵的,因为它可能在堆上分配一个大小为 4 的数组。

如果您事先知道“本地”向量的大小,那么您可以使用自动 数组

for( int i = 0; i != 2000; ++i ) {
   int C[4]; // no initialization
   C[0] = 1;
   // ...
}

这样你就可以避免分配空闲内存的成本。


1

第二种方法是分配新内存(在您的情况下为1000 * 2000次)。每个都是堆中完全新的内存位置(尽管不总是新的,可以在同一位置)。内存分配比仅修改已分配内存中包含的值需要更长的时间。

第一种方法是分配一个内存位置数组,并只修改其中的值。 如果编译器针对此进行优化(这并不总是情况),最好不要将其留给编译器,如果您作为程序员可以选择分配较少的内存(或较少地分配)。


0
`

obj 的作用域将在循环内部,因此一旦循环结束,您将无法再使用它。 此外,对象在循环通过时被实例化然后销毁多次。 最终,除了构造和销毁对象会浪费时间外,没有任何成果。

`

0

首先,你应该确保设计是OK的,这意味着:

  • 代码易于理解
  • 代码能够防止自身出现错误
  • 代码易于扩展

我认为在这种情况下,最好在循环中定义变量。

只有当你真正遇到性能问题时,你才可以优化你的代码(如果编译器没有为你做这件事的话),例如将变量声明放在循环外面。


然后可以自由地将变量放在循环外,但要在代码中清楚地注释为什么这样做,以便下一个开发人员不会将其放回循环中。 - Patrick

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