2010-12-02 55 views
2

我在我的地方很新,所以我应该在表达担忧之前三思,但我已经看到一些代码...VS2010 C++/C#编译器可以优化掉在循环内声明的变量吗?

当我试图提高可读性时,我被告知并不总是那时候,效率要重要得多。

但是后来我在不同类型的循环内部看到变量重新声明,有时甚至到了两个级别。我的一部分认为 - 永远不要那样做!但另一部分则说 - 无论如何,这个复杂的功能应该分解成几个功能。那些较小的函数可以有临时变量,编译器应该能够照顾它们。

然后函数调用会增加一些额外的成本。让我试着拿出2个例子:

Class1::Do1() 
{ 
    for (int i = 0; i < 100; i++) 
    { 
     bool x = GetSomeValue(); 
     ... 
     if (x) 
     { 
      ... 
     } 
    } 
} 

VS

Class1::Do1() 
{ 
    bool x = false; 
    for (int i = 0; i < 100; i++) 
    { 
     x = GetSomeValue(); 
     ... 
     if (x) 
     { 
      ... 
     } 
    } 
} 

VS

Class1::Do1() 
{ 
    for (int i = 0; i < 100; i++) 
    { 
     Do2(); 
    } 
} 

Class1::Do2() 
{ 
    bool x = GetSomeValue(); 
    ... 
    if (x) 
    { 
     ... 
    } 
} 

第一种方法看起来我错了,我总是喜欢第二次,甚至第三次,当我自己写代码。我认为由于额外的函数调用,第三种方式可能会更慢。第一种方法有时甚至可能看起来粗略 - 如果函数很长,声明将远离使用的地方。另一件事是,我的例子太简单了 - 编译器可能会弄清楚如何简化,也许内联所有3.不幸的是,现在我不记得其他例子,我认为是不合理的,只是想提一些变量是重新宣布n * m次,因为它们是两个深度级别(2个圈内)。

魔鬼的拥护者说 - 你怎么知道100%这可能不是有效的?我的纯粹主义者(我的版本)认为,一遍又一遍地重新声明相同的变量是愚蠢的 - 至少在阅读代码时抛出一个变量。

想法?有问题吗?

+3

通常,尝试在适当的范围声明变量。如果bool x不在for循环外部使用,请不要在for循环之外声明它。 [在C#范围变量]的 – Joe 2010-12-02 02:41:31

+0

可能重复(http://stackoverflow.com/questions/3979493/scope-of-variables-in-c) – Joe 2010-12-02 02:42:52

+0

谢谢,乔。我想知道另一种语言 - C++/C++ CLI。 – 2010-12-02 02:46:54

回答

7

据我所知,所有局部变量在方法调用开始时都被分配了堆栈空间,所以在循环内还是在循环之前声明变量应该没有关系。

这种情况下,我会为可读性编写代码 - 如果您不需要循环外的变量,我会亲自在循环中声明它,以使它更接近实际使用它的代码并缩小范围尽可能多的变量。

2

生成的代码为1 & 2将是相同的。你声明变量的地方并不重要。下面是一个例子(与Console.WriteLine("Yes")if发言):

.maxstack 2 
.locals init (
    [0] bool x, 
    [1] int32 i) 
L_0000: ldc.i4.0 
L_0001: stloc.1 
L_0002: br.s L_001b 
L_0004: call bool ConsoleApplication8.Program::GetSomeValue() 
L_0009: stloc.0 
L_000a: ldloc.0 
L_000b: brfalse.s L_0017 
L_000d: ldstr "Yes" 
L_0012: call void [mscorlib]System.Console::WriteLine(string) 
L_0017: ldloc.1 
L_0018: ldc.i4.1 
L_0019: add 
L_001a: stloc.1 
L_001b: ldloc.1 
L_001c: ldc.i4.s 100 
L_001e: blt.s L_0004 
L_0020: ret 

风格上,不过,我会说尽可能接近来声明变量,你使用它们。

在性能方面,对于这些示例更重要的是GetSomeValue()调用是否可以在循环外移动(即它在整个循环中是不变的)。在某些情况下,编译器可以自己检测到它。

#3将取决于函数Do2()是否内联到循环中。如果是这样,最终可能会生成完全相同的生成代码(不是MSIL)。

2

你做什么,你是不是试图变得比你的工具更聪明,你只是使用它们!获取一个分析器,分析你的代码和MEASURE性能!然后,只有那么,你会知道是否你甚至需要关心自己。

我知道,它听起来很不直观,认为也许,也许,你不可能知道你的编译器可能会或可能不会优化掉的每一个该死的东西。诚然,对于包括有经验的开发人员来说,开发人员认为他们知道他们无法知道的东西,然后以效率的名义写出无法维护,不可优化的代码块的巨大迷宫似乎更为常见。他们应该做的是确保代码是可维护的,然后找出需要传播的瓶颈(如果有的话)。

3

是的,这是编译器可以执行的最基本的优化之一,适用于

当然,编译器只有在不改变程序的语义时才能做到这一点。

但是,还有另一个重要方面,你错过了。你假设在循环中声明变量是有成本的。

您认为需要多少时间才能声明bool类型的变量?它基本上是免费的。编译器不需要做任何事情,除了增加堆栈指针(不管在声明变量的函数的哪个位置,它必须做的事情),并为它分配一个值(在你的例子中,它发生在循环内部任何状况之下)。

所以性能差异是。这是编译器擅长的纯机械优化。它知道如何在堆栈上分配内存,并知道如何进行这种操作以及初始化或分配给变量的确切成本。

只要有可能,你应该在尽可能小的范围内声明你的变量。当你需要的时候宣布他们,并且尽快。所以你的第一个方法很糟糕,它甚至没有更快,所以它基本上是一个坏主意。

在C#中,这绝对没有任何缺点。变量是一个值类型,在这种情况下,分配它是空闲的(在整个循环中重复使用相同的堆栈空间,因此在循环内部声明变量没有任何花费),或者它是一个引用变量(在这种情况下,您只是在堆栈中添加引用,这是免费的,出于同样的原因)

在C++中,有些情况下这不起作用,或者存在实际成本的情况。该变量可能会在其构造函数中执行一些无法优化的昂贵操作,并且如果变量在循环内声明,则必须在每次迭代时执行该操作。编译器不能将循环外部的变量移动,因为它必须在循环的每次迭代中初始化:这就是程序员指定的内容,唯一的方法就是调用构造函数。

然后,它可能是值得的移动循环外的对象。 (但当然,赋值运算符,而不是构造函数,每次迭代都会执行,所以希望这是一个更便宜的操作)。

0

可维护性通常更重要,但这个特例不是死罪。

  • 我认为有趣/典型的是,你被告知效率更高。

如果性能是严重的重要,那么性能应予以处理。
Here's an example of the method I use.