Java 在循环之前或循环中声明变量之间的区别?
我一直想知道,一般来说,在循环之前声明一个丢弃变量,而不是在循环内部重复声明,是否会产生任何(性能)差异? Java中的一个(毫无意义的)示例: a)循环前声明:Java 在循环之前或循环中声明变量之间的区别?,java,performance,loops,variables,initialization,Java,Performance,Loops,Variables,Initialization,我一直想知道,一般来说,在循环之前声明一个丢弃变量,而不是在循环内部重复声明,是否会产生任何(性能)差异? Java中的一个(毫无意义的)示例: a)循环前声明: double intermediateResult; for(int i=0; i < 1000; i++){ intermediateResult = i; System.out.println(intermediateResult); } 双中间结果; 对于(int i=0;i
double intermediateResult;
for(int i=0; i < 1000; i++){
intermediateResult = i;
System.out.println(intermediateResult);
}
双中间结果;
对于(int i=0;i<1000;i++){
中间结果=i;
系统输出打印项次(中间结果);
}
b)循环内的声明(重复):
for(int i=0; i < 1000; i++){
double intermediateResult = i;
System.out.println(intermediateResult);
}
for(int i=0;i<1000;i++){
双中间结果=i;
系统输出打印项次(中间结果);
}
哪个更好,a还是b
我怀疑重复的变量声明(例如b)在理论上会产生更多的开销,但编译器足够聪明,所以这并不重要。示例b的优点是更紧凑,并且将变量的范围限制在使用它的地方。尽管如此,我还是倾向于根据示例a编写代码
编辑:我对Java案例特别感兴趣。我认为这取决于编译器,很难给出一般性的答案。它依赖于语言-IIRC C优化了这一点,因此没有任何区别,但JavaScript(例如)每次都会进行整个内存分配。即使我知道我的编译器足够聪明,我也不想依赖它,而是会使用a)变量 b)只有当您迫切需要在循环体之后使中间结果不可用时,变体才对我有意义。但无论如何,我无法想象如此绝望的局面
编辑:Jon Skeet提出了一个非常好的观点,表明循环中的变量声明可以产生实际的语义差异。我怀疑一些编译器可以将两者优化为相同的代码,但肯定不是全部。所以我想说你和前者相处得更好。使用后者的唯一原因是,如果您希望确保所声明的变量仅在循环中使用。一般来说,我在最可能的内部范围内声明变量。因此,如果您没有在循环之外使用intermediateResult,那么我会选择B。这取决于语言和确切用法。例如,在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();
}
}
}
不同之处在于,所有动作都捕获相同的
外部变量,但每个动作都有自己独立的内部变量。在我看来,b是更好的结构。在a中,intermediateResult的最后一个值在循环完成后保持不变
编辑:
这与值类型没有太大区别,但引用类型可能有点重要。就我个人而言,我喜欢尽快取消对变量的引用以进行清理,b会为您这样做,哪个更好,a还是b
从性能的角度来看,您必须对其进行测量。(在我看来,如果你能测量一个差异,编译器就不是很好)
从维护角度来看,b更好。在尽可能狭窄的范围内,在同一位置声明和初始化变量。不要在声明和初始化之间留下漏洞,也不要污染您不需要的名称空间。我将始终使用(而不是依赖编译器)并可能重写为:
for(int i=0, double intermediateResult=0; i<1000; i++){
intermediateResult = i;
System.out.println(intermediateResult);
}
for(int i=0,double intermediateResult=0;i这是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...
这将在第一次打印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...
我分别运行了20次A和B示例,循环了1亿次。(JVM-1.5.0)
A:平均执行时间:.074秒
B:平均执行时间:.067秒
令我惊讶的是,B的速度稍微快一点。
像现在的计算机一样快,很难说你是否能准确地测量它。
我也会用这种方式编写代码,但我想说这并不重要。一位同事更喜欢第一种形式,告诉他这是一种优化,更喜欢重复使用声明
我更喜欢第二个(并试着说服我的同事!;-)读过:
- 它将变量的范围缩小到需要它们的地方,这是一件好事
- Java进行了充分的优化,在性能上没有显著差异。IIRC,也许第二种形式更快
无论如何,它属于过早优化的范畴,依赖于编译器和/或JVM的质量。以下是我在.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);
}
doubler0;
对于(int i=0;i<1000;i++){
r0=i*i;
控制台写入线(r0);
}
对于(int j=0;j<1000;j++){
双r1=j*j;
控制台写入线(r1);
}
这就是我从渲染回代码时得到的结果
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);
}
for(int i=0;i<0x3e8;i++)
{
双r0=i*i;
控制台写入线(r0);
}
对于(int j=0;j<0x3e8;j++)
{
双r1=j*j;
控制台写入线(r1);
}
因此,在编译之后,两者看起来完全相同。在托管语言中,代码转换为CL/字节代码,在执行时转换为机器语言。因此,在机器语言中,甚至可能不会在堆栈上创建double。它可能只是一个寄存器,因为代码反映它是WriteLine
函数的临时变量。对于循环,有一整套优化规则。所以普通人不应该担心,尤其是在托管语言中。在某些情况下,您可以优化管理代码,例如,如果您
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);
}
for(;;) {
Object o = new Object();
}
Object o;
for(;;) {
o = new Object();
}
typedef struct loop_example{
JXTZ hi; // where JXTZ could be another type...say closed source lib
// you include in Makefile
}loop_example_struct;
//then....
int j = 0; // declare here or face c99 error if in loop - depends on compiler setting
for ( ;j++; )
{
loop_example loop_object; // guess the result in memory heap?
}
{ //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);
}
}
public static void outside() {
double intermediateResult;
for(int i=0; i < Integer.MAX_VALUE; i++){
intermediateResult = i;
}
}
public static void inside() {
for(int i=0; i < Integer.MAX_VALUE; i++){
double intermediateResult = i;
}
}
int b;
for (int i = 0; i < 10; i++) {
b = i;
}
for (int i = 0; i < 10; i++) {
int b = i;
}
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
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
var now = require("../node_modules/performance-now")
// declare vars inside loop
function varInside(){
for(var i = 0; i < 100000000; i++){
var temp = i;
var temp2 = i + 1;
var temp3 = i + 2;
}
}
// declare vars outside loop
function varOutside(){
var temp;
var temp2;
var temp3;
for(var i = 0; i < 100000000; i++){
temp = i
temp2 = i + 1
temp3 = i + 2
}
}
// for computing average execution times
var insideAvg = 0;
var outsideAvg = 0;
// run varInside a million times and average execution times
for(var i = 0; i < 1000; i++){
var start = now()
varInside()
var end = now()
insideAvg = (insideAvg + (end-start)) / 2
}
// run varOutside a million times and average execution times
for(var i = 0; i < 1000; i++){
var start = now()
varOutside()
var end = now()
outsideAvg = (outsideAvg + (end-start)) / 2
}
console.log('declared inside loop', insideAvg)
console.log('declared outside loop', outsideAvg)
double intermediateResult;
int i = byte.MinValue;
for(; i < 1000; i++)
{
intermediateResult = i;
System.out.println(intermediateResult);
}
int intermediateResult;
for(int i=0; i < 1000; i++){
intermediateResult = i+2;
System.out.println(intermediateResult);
}
for(int i=0; i < 1000; i++){
int intermediateResult = i+2;
System.out.println(intermediateResult);
}
for(int i = 0; i < 1000; ++i) {
int intermediateResult = i + 2;
System.out.println(intermediateResult);
}
Code:
stack=2, locals=3, args_size=1
0: iconst_0
1: istore_2
2: iload_2
3: sipush 1000
6: if_icmpge 26
9: iload_2
10: iconst_2
11: iadd
12: istore_1
13: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
16: iload_1
17: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
20: iinc 2, 1
23: goto 2
26: return
LocalVariableTable:
Start Length Slot Name Signature
13 13 1 intermediateResult I
2 24 2 i I
0 27 0 args [Ljava/lang/String;
Code:
stack=2, locals=3, args_size=1
0: iconst_0
1: istore_1
2: iload_1
3: sipush 1000
6: if_icmpge 26
9: iload_1
10: iconst_2
11: iadd
12: istore_2
13: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
16: iload_2
17: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
20: iinc 1, 1
23: goto 2
26: return
LocalVariableTable:
Start Length Slot Name Signature
13 7 2 intermediateResult I
2 24 1 i I
0 27 0 args [Ljava/lang/String;
for(int i=0; i < 1000; i++){
int intermediateResult = i;
System.out.println(intermediateResult);
}
for(int i = 0; i < 1000; ++i) {
System.out.println(i);
}