哪个循环具有更好的性能?为什么? [英] Which loop has better performance? Why?

查看:95
本文介绍了哪个循环具有更好的性能?为什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  String s =; 
for(i = 0; i< ....){
s =一些赋值;
}



<$ p (i = 0; i< ..){
String s =一些赋值; $ p>
}

我不需要再次在循环外使用's'。
第一个选项可能更好,因为每次都没有初始化一个新的字符串。然而,第二个会导致变量的作用域被限制在循环本身中。



编辑:回应Milhous的回答。将String分配给循环内的常量是没有意义的,不是吗?不,在这里'一些分配'意味着从列表中获得的变化值被迭代。

另外,这个问题并不是因为我担心内存管理。只是想知道哪个更好。

使用第二个选项:

$ p $ for(...){
String s = ...;
}



范围不会影响效果



如果您反汇编每个编译的代码(使用JDK的 javap 工具),您将看到循环在两种情况下编译为完全相同的JVM指令。另请注意, Brian R. Bondy的选项#3与选项#1相同。在使用更紧密的作用域时,没有额外的内容被添加或从堆栈中移除,并且在这两种情况下堆栈中都使用相同的数据。

避免过早初始化



这两种情况唯一的区别在于,在第一个例子中,变量 s 被不必要地初始化。这是一个与变量声明位置不同的问题。这增加了两个浪费的指令(加载字符串常量并将其存储在堆栈帧插槽中)。一个好的静态分析工具会警告你,你从来没有读过你赋给 s 的值,而且一个好的JIT编译器可能会在运行时忽略它。

你可以简单地通过使用一个空的声明(即 String s; )来解决这个问题,但这被认为是不好的做法,并在下面讨论另一个副作用。

null 这样的伪造值通常会被分配给一个变量,只是为了避免一个编译器错误,即一个变量被读取没有被初始化。这个错误可以被看作是提示变量作用域太大,并且在需要接收有效值之前声明它。空声明迫使你考虑每条代码路径;不要忽略这个有价值的警告,通过分配一个伪造的值。

Conserve Stack Slots



如上所述,在这两种情况下,JVM指令都是相同的,但有一个微妙的副作用,使得它在JVM级别最适合使用尽可能有限的范围。这在方法的局部变量表中是可见的。考虑如果你有多个循环会发生什么,并且变量声明在不必要的大范围内:

  void x(String [] strings,整数[]整数){
String s;
for(int i = 0; i< strings.length; ++ i){
s = strings [0];
...
}
整数n;
for(int i = 0; i< integers.length; ++ i){
n =整数[i];
...
}
}

变量 s n 可以在它们各自的循环中声明,但由于它们不是,编译器使用两个槽堆栈框架。如果它们是在循环中声明的,编译器可以重复使用相同的插槽,从而缩小堆栈帧。



什么真的很重要



然而,这些问题大部分都不重要。一个好的JIT编译器会看到,你无法读取你正在浪费地分配的初始值,并优化分配。在这里或者那里保存一个插槽不会造成或破坏你的应用程序。



重要的是让你的代码易读易维护,在这方面,使用有限的范围显然更好。变量的范围越小,就越容易理解它的使用方式,以及对代码的任何更改会产生什么影响。


String s = "";
for(i=0;i<....){
    s = some Assignment;
}

or

for(i=0;i<..){
    String s = some Assignment;
}

I don't need to use 's' outside the loop ever again. The first option is perhaps better since a new String is not initialized each time. The second however would result in the scope of the variable being limited to the loop itself.

EDIT: In response to Milhous's answer. It'd be pointless to assign the String to a constant within a loop wouldn't it? No, here 'some Assignment' means a changing value got from the list being iterated through.

Also, the question isn't because I'm worried about memory management. Just want to know which is better.

解决方案

Limited Scope is Best

Use your second option:

for ( ... ) {
  String s = ...;
}

Scope Doesn't Affect Performance

If you disassemble code the compiled from each (with the JDK's javap tool), you will see that the loop compiles to the exact same JVM instructions in both cases. Note also that Brian R. Bondy's "Option #3" is identical to Option #1. Nothing extra is added or removed from the stack when using the tighter scope, and same data are used on the stack in both cases.

Avoid Premature Initialization

The only difference between the two cases is that, in the first example, the variable s is unnecessarily initialized. This is a separate issue from the location of the variable declaration. This adds two wasted instructions (to load a string constant and store it in a stack frame slot). A good static analysis tool will warn you that you are never reading the value you assign to s, and a good JIT compiler will probably elide it at runtime.

You could fix this simply by using an empty declaration (i.e., String s;), but this is considered bad practice and has another side-effect discussed below.

Often a bogus value like null is assigned to a variable simply to hush a compiler error that a variable is read without being initialized. This error can be taken as a hint that the variable scope is too large, and that it is being declared before it is needed to receive a valid value. Empty declarations force you to consider every code path; don't ignore this valuable warning by assigning a bogus value.

Conserve Stack Slots

As mentioned, while the JVM instructions are the same in both cases, there is a subtle side-effect that makes it best, at a JVM level, to use the most limited scope possible. This is visible in the "local variable table" for the method. Consider what happens if you have multiple loops, with the variables declared in unnecessarily large scope:

void x(String[] strings, Integer[] integers) {
  String s;
  for (int i = 0; i < strings.length; ++i) {
    s = strings[0];
    ...
  }
  Integer n;
  for (int i = 0; i < integers.length; ++i) {
    n = integers[i];
    ...
  }
}

The variables s and n could be declared inside their respective loops, but since they are not, the compiler uses two "slots" in the stack frame. If they were declared inside the loop, the compiler can reuse the same slot, making the stack frame smaller.

What Really Matters

However, most of these issues are immaterial. A good JIT compiler will see that it is not possible to read the initial value you are wastefully assigning, and optimize the assignment away. Saving a slot here or there isn't going to make or break your application.

The important thing is to make your code readable and easy to maintain, and in that respect, using a limited scope is clearly better. The smaller scope a variable has, the easier it is to comprehend how it is used and what impact any changes to the code will have.

这篇关于哪个循环具有更好的性能?为什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆