java 调用 array.length 的成本是多少

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1208320/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 15:38:15  来源:igfitidea点击:

What is the Cost of Calling array.length

javaarrayspremature-optimization

提问by Stroboskop

While updating for loops to for-each loops in our application, I came across a lot of these "patterns":

在我们的应用程序中将 for 循环更新为 for-each 循环时,我遇到了很多这样的“模式”:

for (int i = 0, n = a.length; i < n; i++) {
    ...
}

instead of

代替

for (int i = 0; i < a.length; i++) {
    ...
}

I can see that you gain performance for collections because you don't need to call the size()method with each loop. But with arrays??

我可以看到您获得了集合的性能,因为您不需要在每个循环中调用size()方法。但是用数组??

So the question arose: is array.lengthmore expensive than a regular variable?

所以问题出现了:array.length比常规变量更昂贵吗?

回答by jjnguy

No, a call to array.lengthis O(1)or constant time operation.

不,调用array.lengthisO(1)或恒定时间操作。

Since the .lengthis(acts like) a publicfinalmember of array, it is no slower to access than a local variable. (It is very different from a call to a method like size())

由于.lengthis( 行为类似于) 的publicfinal成员array,因此访问速度并不比局部变量慢。(这与调用类似的方法非常不同size()

A modern JIT compiler is likely to optimize the call to .lengthright out anyway.

.length无论如何,现代 JIT 编译器很可能会优化对right的调用。

You can confirm this by either looking at the source code of the JIT compiler in OpenJDK, or by getting the JVM to dump out the JIT compiled native code and examining the code.

您可以通过查看 OpenJDK 中 JIT 编译器的源代码或让 JVM 转储出 JIT 编译的本机代码并检查代码来确认这一点。

Note that there may be cases where the JIT compiler can't do this; e.g.

请注意,可能存在 JIT 编译器无法执行此操作的情况;例如

  1. if you are debugging the enclosing method, or
  2. if the loop body has enough local variables to force register spilling.
  1. 如果您正在调试封闭方法,或者
  2. 如果循环体有足够的局部变量来强制寄存器溢出。

回答by Grant Wagner

I had a bit of time over lunch:

我有一点时间吃午饭:

public static void main(String[] args) {
    final int[] a = new int[250000000];
    long t;

    for (int j = 0; j < 10; j++) {
        t = System.currentTimeMillis();
        for (int i = 0, n = a.length; i < n; i++) { int x = a[i]; }
        System.out.println("n = a.length: " + (System.currentTimeMillis() - t));

        t = System.currentTimeMillis();
        for (int i = 0; i < a.length; i++) { int x = a[i]; }
        System.out.println("i < a.length: " + (System.currentTimeMillis() - t));
    }
}

The results:

结果:

n = a.length: 672
i < a.length: 516
n = a.length: 640
i < a.length: 516
n = a.length: 656
i < a.length: 516
n = a.length: 656
i < a.length: 516
n = a.length: 640
i < a.length: 532
n = a.length: 640
i < a.length: 531
n = a.length: 641
i < a.length: 516
n = a.length: 656
i < a.length: 531
n = a.length: 656
i < a.length: 516
n = a.length: 656
i < a.length: 516

Notes:

笔记:

  1. If you reverse the tests, then n = a.lengthshows as being faster than i < a.lengthby about half, probably due to garbage collection(?).
  2. I couldn't make 250000000much larger because I got OutOfMemoryErrorat 270000000.
  1. 如果您反转测试,则n = a.length显示比i < a.length大约一半快,可能是由于垃圾收集(?)。
  2. 我不能做得250000000更大,因为我OutOfMemoryError270000000.

The point is, and it is the one everyone else has been making, you have to run Java out of memory and you still don't see a significant difference in speed between the two alternatives. Spend your development time on things that actually matter.

关键是,这是其他人一直在做的事情,您必须在内存不足的情况下运行 Java,而且您仍然没有看到两种替代方案之间的速度有显着差异。把你的开发时间花在真正重要的事情上。

回答by IRBMe

I doubt there is any significant difference whatsoever, and even if there was, I would bet it's probably optimized away during compilation. You're wasting your time when you try to micro-optimize things like that. Make the code readable and correct first, then if you have a performance problem, use a profiler, then worry about choosing better data structures/algorithms if appropriate, thenworry about optimizing the parts your profiler highlights.

我怀疑是否有任何显着差异,即使有,我敢打赌它可能在编译过程中被优化掉了。当您尝试对此类事物进行微观优化时,您是在浪费时间。首先使代码可读和正确,然后如果您有性能问题,请使用分析器,然后担心选择更好的数据结构/算法(如果合适),然后担心优化分析器突出显示的部分。

回答by Bill the Lizard

The length of an array is stored as a member variable of the array (not the same as an element) in Java, so fetching that length is a constant time operation, the same as reading a member variable from a regular class. Many older languages like C and C++ don't store the length as part of the array, so you'd want to store the length before the loop starts. You don't have to do that in Java.

在Java中,数组的长度是作为数组的成员变量(与元素不同)存储的,因此获取该长度是一个常数时间操作,与从常规类中读取成员变量相同。许多较旧的语言(如 C 和 C++)不将长度存储为数组的一部分,因此您希望在循环开始之前存储长度。您不必在 Java 中这样做。

回答by instcode

In that case, why don't you make an inverse loop:

在这种情况下,你为什么不做一个反向循环:

for (int i = a.length - 1; i >= 0; --i) {
    ...
}

There are 2 micro optimizations here:

这里有 2 个微优化:

  • Loop reversal
  • Postfix decrement
  • 循环反转
  • 后缀递减

回答by Michael Myers

It might be ever-so-slightly faster to store it in a variable. But I would be extremely surprised if a profiler pointed to it as being a problem.

将它存储在变量中可能会稍微快一点。但是如果分析器指出它是一个问题,我会非常惊讶。

At the bytecode level, getting the length of an array is done with the arraylengthbytecode. I don't know if it is slower than an iloadbytecode, but there shouldn't be enough difference to notice.

在字节码级别,获取数组的长度是通过arraylength字节码完成的。我不知道它是否比iload字节码慢,但应该没有足够的差异需要注意。

回答by Michael Burr

This answer is from a C# point of view, but I would think the same applies to Java.

这个答案是从 C# 的角度来看的,但我认为这同样适用于 Java。

In C#, the idiom

在 C# 中,习语

for (int i = 0; i < a.length; i++) {    ...}

is recognized as iterating over the array, so the bounds check is avoided when accessing the array in the loop instead of with each array access.

被识别为对数组进行迭代,因此在循环中访问数组而不是每次访问数组时都避免了边界检查。

This may or may not be recognized with code such as:

这可能会或可能不会被代码识别,例如:

for (int i = 0, n = a.length; i < n; i++) {    ...}

or

或者

n = a.length;

for (int i = 0; i < n; i++) {   ...}

How much of this optimization is performed by the compiler vs. the JITter I don't know, and in particular if it's performed by the JITter I'd expect all 3 to generate the same native code.

这种优化中有多少是由编译器与 JITter 执行的,我不知道,特别是如果它是由 JITter 执行的,我希望所有 3 个都生成相同的本机代码。

However, the first form is also arguably more readable by people, so I'd say go with that.

然而,第一种形式也可以说更容易被人们阅读,所以我会说去吧。

回答by Chris Vest

Array.length is a constant and the JIT compiler should see through that in both instances. I would expect the resulting machine code to be the same in both cases. At least for the server compiler.

Array.length 是一个常量,JIT 编译器应该在这两种情况下都看穿它。我希望生成的机器代码在两种情况下都相同。至少对于服务器编译器。