Rab*_*ski 307 java variables performance loops initialization
我一直想知道,一般来说,在循环之前声明一个抛弃变量,而不是在循环内部重复,是否会产生任何(性能)差异?Java中的一个(毫无意义的)示例:
a)循环前的声明:
double intermediateResult;
for(int i=0; i < 1000; i++){
intermediateResult = i;
System.out.println(intermediateResult);
}
Run Code Online (Sandbox Code Playgroud)
b)声明(重复)内循环:
for(int i=0; i < 1000; i++){
double intermediateResult = i;
System.out.println(intermediateResult);
}
Run Code Online (Sandbox Code Playgroud)
哪一个更好,一个还是b?
我怀疑重复变量声明(例子b)在理论上会产生更多的开销,但是编译器足够聪明,所以它并不重要.实施例b具有更紧凑的优点,并且将变量的范围限制在其使用的位置.尽管如此,我倾向于根据示例来编码一个.
编辑:我对Java案件特别感兴趣.
Dan*_*ker 250
哪个更好,a还是b?
从性能角度来看,您必须对其进行衡量.(在我看来,如果你可以测量差异,编译器就不是很好).
从维护的角度来看,b更好.在尽可能最窄的范围内在同一位置声明和初始化变量.不要在声明和初始化之间留下空洞,也不要污染您不需要的命名空间.
Mar*_*ark 211
好吧,我每次运行你的A和B示例20次,循环1亿次.(JVM - 1.5.0)
答:平均执行时间:.074秒
B:平均执行时间:.067秒
令我惊讶的是B稍快一点.如果能够准确地测量计算机,现在很难说计算机.我会把它编码为A方式,但我会说它并不重要.
Jon*_*eet 66
这取决于语言和确切用途.例如,在C#1中没有任何区别.在C#2中,如果局部变量是通过匿名方法(或C#3中的lambda表达式)捕获的,那么它可以产生非常显着的差异.
例:
using System;
using System.Collections.Generic;
class Test
{
static void Main()
{
List<Action> actions = new List<Action>();
int outer;
for (int i=0; i < 10; i++)
{
outer = i;
int inner = i;
actions.Add(() => Console.WriteLine("Inner={0}, Outer={1}", inner, outer));
}
foreach (Action action in actions)
{
action();
}
}
}
Run Code Online (Sandbox Code Playgroud)
输出:
Inner=0, Outer=9
Inner=1, Outer=9
Inner=2, Outer=9
Inner=3, Outer=9
Inner=4, Outer=9
Inner=5, Outer=9
Inner=6, Outer=9
Inner=7, Outer=9
Inner=8, Outer=9
Inner=9, Outer=9
Run Code Online (Sandbox Code Playgroud)
不同之处在于所有操作都捕获相同的outer变量,但每个操作都有自己独立的inner变量.
par*_*cle 35
以下是我在.NET中编写和编译的内容.
double r0;
for (int i = 0; i < 1000; i++) {
r0 = i*i;
Console.WriteLine(r0);
}
for (int j = 0; j < 1000; j++) {
double r1 = j*j;
Console.WriteLine(r1);
}
Run Code Online (Sandbox Code Playgroud)
当CIL渲染回代码时,这就是我从.NET Reflector获得的.
for (int i = 0; i < 0x3e8; i++)
{
double r0 = i * i;
Console.WriteLine(r0);
}
for (int j = 0; j < 0x3e8; j++)
{
double r1 = j * j;
Console.WriteLine(r1);
}
Run Code Online (Sandbox Code Playgroud)
因此编译后两者看起来完全相同.在托管语言中,代码转换为CL /字节代码,并在执行时将其转换为机器语言.因此,在机器语言中,甚至可能无法在堆栈上创建double.它可能只是一个寄存器,因为代码反映它是WriteLine函数的临时变量.循环有一整套优化规则.所以一般人不应该担心它,特别是在托管语言中.在某些情况下,您可以优化管理代码,例如,如果您必须使用仅string a; a+=anotherstring[i]使用vs 来连接大量字符串StringBuilder.两者之间的表现有很大差异.有很多这样的情况,编译器无法优化您的代码,因为它无法弄清楚更大范围内的目标.但它可以为您优化基本的东西.
Mic*_*ren 24
这是VB.NET中的问题.在此示例中,Visual Basic结果不会重新初始化变量:
For i as Integer = 1 to 100
Dim j as Integer
Console.WriteLine(j)
j = i
Next
' Output: 0 1 2 3 4...
Run Code Online (Sandbox Code Playgroud)
这将在第一次打印0(Visual Basic变量在声明时具有默认值!)但i每次都在此之后.
= 0但是,如果你添加一个,你会得到你所期望的:
For i as Integer = 1 to 100
Dim j as Integer = 0
Console.WriteLine(j)
j = i
Next
'Output: 0 0 0 0 0...
Run Code Online (Sandbox Code Playgroud)
小智 15
我做了一个简单的测试:
int b;
for (int i = 0; i < 10; i++) {
b = i;
}
Run Code Online (Sandbox Code Playgroud)
VS
for (int i = 0; i < 10; i++) {
int b = i;
}
Run Code Online (Sandbox Code Playgroud)
我用gcc-5.2.0编译了这些代码.然后我反汇编了这两个代码的main(),这就是结果:
1º:
0x00000000004004b6 <+0>: push rbp
0x00000000004004b7 <+1>: mov rbp,rsp
0x00000000004004ba <+4>: mov DWORD PTR [rbp-0x4],0x0
0x00000000004004c1 <+11>: jmp 0x4004cd <main+23>
0x00000000004004c3 <+13>: mov eax,DWORD PTR [rbp-0x4]
0x00000000004004c6 <+16>: mov DWORD PTR [rbp-0x8],eax
0x00000000004004c9 <+19>: add DWORD PTR [rbp-0x4],0x1
0x00000000004004cd <+23>: cmp DWORD PTR [rbp-0x4],0x9
0x00000000004004d1 <+27>: jle 0x4004c3 <main+13>
0x00000000004004d3 <+29>: mov eax,0x0
0x00000000004004d8 <+34>: pop rbp
0x00000000004004d9 <+35>: ret
Run Code Online (Sandbox Code Playgroud)
VS
2º
0x00000000004004b6 <+0>: push rbp
0x00000000004004b7 <+1>: mov rbp,rsp
0x00000000004004ba <+4>: mov DWORD PTR [rbp-0x4],0x0
0x00000000004004c1 <+11>: jmp 0x4004cd <main+23>
0x00000000004004c3 <+13>: mov eax,DWORD PTR [rbp-0x4]
0x00000000004004c6 <+16>: mov DWORD PTR [rbp-0x8],eax
0x00000000004004c9 <+19>: add DWORD PTR [rbp-0x4],0x1
0x00000000004004cd <+23>: cmp DWORD PTR [rbp-0x4],0x9
0x00000000004004d1 <+27>: jle 0x4004c3 <main+13>
0x00000000004004d3 <+29>: mov eax,0x0
0x00000000004004d8 <+34>: pop rbp
0x00000000004004d9 <+35>: ret
Run Code Online (Sandbox Code Playgroud)
哪个是exaclty相同的结果.是不是两个代码产生相同的东西的证据?
Tri*_*ych 11
我总是使用A(而不是依赖于编译器),也可能会重写为:
for(int i=0, double intermediateResult=0; i<1000; i++){
intermediateResult = i;
System.out.println(intermediateResult);
}
Run Code Online (Sandbox Code Playgroud)
这仍然限制intermediateResult了循环的范围,但在每次迭代期间不会重新声明.
在我看来,b是更好的结构.在a中,在循环结束后,intermediateResult的最后一个值会保持不变.
编辑:这与值类型没有太大区别,但引用类型可能有点重要.就个人而言,我喜欢尽快解除引用的变量进行清理,b为你做的就是,
同事更喜欢第一种形式,告诉它是一种优化,更喜欢重复使用声明.
我更喜欢第二个(并试图说服我的同事!;-)),读过:
无论如何,它属于依赖于编译器和/或JVM质量的过早优化类别.
小智 5
好吧,你总是可以为此做一个范围:
{ //Or if(true) if the language doesn't support making scopes like this
double intermediateResult;
for (int i=0; i<1000; i++) {
intermediateResult = i;
System.out.println(intermediateResult);
}
}
Run Code Online (Sandbox Code Playgroud)
这样你只需要声明一次变量,当你离开循环时它就会死掉.
如果你在lambda等中使用变量,那么C#就有区别.但是通常编译器基本上会做同样的事情,假设变量只在循环中使用.
鉴于它们基本相同:请注意,版本b使得读者更加明显地认为变量不是,也不能在循环之后使用.此外,版本b更容易重构.在版本a中将循环体提取到自己的方法中更加困难.此外,版本b向您保证对此类重构没有任何副作用.
因此,版本a让我厌烦,因为它没有任何好处,它使得更难以推理代码......
| 归档时间: |
|
| 查看次数: |
130099 次 |
| 最近记录: |