C#在函数开头声明循环变量和在每个for循环内部声明哪个更快?

4

我一直在想,是这样做更快:

可能会重复:
在循环之前或之中声明变量的区别?

还是像这样:

int i;

for (i=0;i<...)
for (i=0;i<...)
for (i=0;i<...)
for (i=0;i<...)

或者

for (int i=0;i<...)
for (int i=0;i<...)
for (int i=0;i<...)
for (int i=0;i<...)

意思是,如果我在一个函数中有多个for循环,如果我只声明一次循环迭代变量并多次使用它,还是在每个for循环内部声明它,它会更快吗?

10
作为程序员,重要的考虑因素比如何“优化”更重要。这不是关于“哪个更快”,而是将变量的范围缩小到最小必要范围的问题。 - spender
你测试过了吗?在优化像这样微小的东西之前,一定要先进行测量。我敢打赌,在循环内部你所做的事情可以比 i 更加优化。 - Emond
是的,这些都是有效的顾虑,但由于我是一种自学的方式,我只想知道是否有一种常识的方法来做到这一点,也许我做错了。 - Istrebitel
你正在将过早优化提升到一个新的水平。如果在循环中有任何有意义的工作,这两种方法都不会对程序执行时间产生实质性影响。先进行分析再进行优化。 - SolutionYogi
4个回答

4
生成的IL(发布版)对于这两种方法来说基本相同。
考虑以下代码:
static int test1()
{
    int result = 0;
    int i;

    for (i = 0; i < 10; ++i)
        ++result;

    for (i = 0; i < 10; ++i)
        ++result;

    return result;
}

static int test2()
{
    int result = 0;

    for (int i = 0; i < 10; ++i)
        ++result;

    for (int i = 0; i < 10; ++i)
        ++result;

    return result;
}

下面是生成的发布版本IL代码,我将其并排放置以便于比较:

test1():                        test2()
{                               {
    .maxstack 2                     .maxstack 2
    .locals init (                  .locals init (
        [0] int32 result,               [0] int32 result,
        [1] int32 i)                    [1] int32 i,
                                        [2] int32 V_2)
    L_0000: ldc.i4.0                L_0000: ldc.i4.0 
    L_0001: stloc.0                 L_0001: stloc.0 
    L_0002: ldc.i4.0                L_0002: ldc.i4.0 
    L_0003: stloc.1                 L_0003: stloc.1 
    L_0004: br.s L_000e             L_0004: br.s L_000e
    L_0006: ldloc.0                 L_0006: ldloc.0 
    L_0007: ldc.i4.1                L_0007: ldc.i4.1 
    L_0008: add                     L_0008: add 
    L_0009: stloc.0                 L_0009: stloc.0 
    L_000a: ldloc.1                 L_000a: ldloc.1 
    L_000b: ldc.i4.1                L_000b: ldc.i4.1 
    L_000c: add                     L_000c: add 
    L_000d: stloc.1                 L_000d: stloc.1 
    L_000e: ldloc.1                 L_000e: ldloc.1 
    L_000f: ldc.i4.s 10             L_000f: ldc.i4.s 10
    L_0011: blt.s L_0006            L_0011: blt.s L_0006
    L_0013: ldc.i4.0                L_0013: ldc.i4.0 
    L_0014: stloc.1                 L_0014: stloc.2 
    L_0015: br.s L_001f             L_0015: br.s L_001f
    L_0017: ldloc.0                 L_0017: ldloc.0 
    L_0018: ldc.i4.1                L_0018: ldc.i4.1 
    L_0019: add                     L_0019: add 
    L_001a: stloc.0                 L_001a: stloc.0 
    L_001b: ldloc.1                 L_001b: ldloc.2 
    L_001c: ldc.i4.1                L_001c: ldc.i4.1 
    L_001d: add                     L_001d: add 
    L_001e: stloc.1                 L_001e: stloc.2 
    L_001f: ldloc.1                 L_001f: ldloc.2 
    L_0020: ldc.i4.s 10             L_0020: ldc.i4.s 10
    L_0022: blt.s L_0017            L_0022: blt.s L_0017
    L_0024: ldloc.0                 L_0024: ldloc.0 
    L_0025: ret                     L_0025: ret 
}                               }

这很明显表明你应该选择 'i' 局部于循环的版本,因为这是更好的实践方法。然而,在循环外声明循环计数器的版本将比初始化一个 int 为零所需的时间更快 - 几乎可以忽略不计。

很棒的回答。不仅仅是说它是这样的,而且还有IL证明/解释。+1。 - Erik van Brakel
2
IL与“哪个更快”的问题有什么关系? IL并不是执行代码的东西! - Eric Lippert
不,但如果它们完全相同,则它们就是相同的,对吧? - Matthew Watson

3

理论上来说,前者应该更快,因为只有一个内存位置被重复使用。


不是真的。对于这两种方法,IL几乎是相同的。 - Matthew Watson
好的,事实并非如此 - 我使用了dotPeek来反编译这两个版本,它们是不同的。唯一相同的方式是如果存在编译器优化,但在这种情况下并非如此。 - Petar Ivanov
请查看我下面发布的IL代码。两种方法之间基本相同。将“i”在循环中声明为局部变量的唯一开销是函数开始时的单个.init。您可能正在查看调试版本? - Matthew Watson
正如您在发布的IL中所看到的那样,几乎没有任何区别。我还检查了调试版本,对于它来说根本没有任何区别。 - Matthew Watson
你怎么看待IL呢?顺便问一下。 - Petar Ivanov

1
如Spender所提到的,这里没有涉及速度问题,更多的是与您需要变量可用的范围有关。如果它仅需要在循环中可用,则在循环中声明它而不是在循环外部声明。但是,如果您需要使其在所有循环中都可用,则第一种方法更快。

嗯...在for循环外声明变量会更快吗,还是不会呢? - Istrebitel
在您提供的例子中,是的! - ranjez

0
这两个代码生成相同的JIT代码。因此,它实际上没有任何性能提升。

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