Java 阵列和阵列列表位置访问性能
我刚刚阅读了以下代码示例: 什么原因 j=INT_数组[i]; 比……快三倍 j=数组\列表。获取(i) 我知道ArrayList在内部使用数组。所以我想知道 详细地说,这次添加了哪些额外操作(调用方法、强制转换、其他JVM注意事项等) 提前感谢。性能将在很大程度上取决于所涉及的虚拟机以及各种其他考虑因素。这篇文章开头的笼统陈述让我怀疑,作者对JVM上的性能如何变化几乎一无所知,测试代码的其余部分证实了这一点。它没有进行足够长的测试,也没有使用任何JVM预热期或任何类似的时间。哦,当测试Java 阵列和阵列列表位置访问性能,java,arrays,performance,arraylist,Java,Arrays,Performance,Arraylist,我刚刚阅读了以下代码示例: 什么原因 j=INT_数组[i]; 比……快三倍 j=数组\列表。获取(i) 我知道ArrayList在内部使用数组。所以我想知道 详细地说,这次添加了哪些额外操作(调用方法、强制转换、其他JVM注意事项等) 提前感谢。性能将在很大程度上取决于所涉及的虚拟机以及各种其他考虑因素。这篇文章开头的笼统陈述让我怀疑,作者对JVM上的性能如何变化几乎一无所知,测试代码的其余部分证实了这一点。它没有进行足够长的测试,也没有使用任何JVM预热期或任何类似的时间。哦,当测试Arr
ArrayList
版本时,它使用INT\u ARRAY.length
,这意味着JIT优化的一个潜在来源被删除。真的不是一篇好文章
但是,很容易考虑ARRAYList.GET()超出正常数组访问的事情:
- 空性检查(查看ArrayList引用是否为非空)。这是对数组本身的空性检查的补充,这对于数组和ArrayList都是必需的
- 可能是虚拟方法间接寻址,这取决于JIT是否已成功内联调用
- 边界检查-与数组访问不同,因为列表的大小通常小于数组的长度
微基准标记很有趣,但在为您提供有用信息时,您需要意识到它的局限性。我的理解是JVM有特定的操作码用于处理阵列。性能差异很可能是方法调用的开销等。为什么不编写一个简单的测试用例,并使用
javad
来查看代码到底编译成了什么。这应该会给你一个想法。如果不检查发布的链接,并且假设数组列表的速度比你所说的慢3倍,那么速度差可能会随着JVM的不同而变化,有几件事可能会影响返回值的速度。根据执行测试时有效的控制变量,该文章的结果可能会有所不同。考虑到在完成操作之前已经完成了各种检查,一个集合的速度应该不会令人惊讶地慢一点。添加到arraylist,例如调用
public void ensureCapacity(int minCapacity) {
modCount++;
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
Object oldData[] = elementData;
int newCapacity = (oldCapacity * 3)/2 + 1;
if (newCapacity < minCapacity)
newCapacity = minCapacity;
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
}
public-capacity(int-minCapacity){
modCount++;
int oldCapacity=elementData.length;
如果(最小容量>旧容量){
对象oldData[]=elementData;
int newCapacity=(旧容量*3)/2+1;
if(新容量<最小容量)
新容量=最小容量;
//minCapacity通常接近大小,因此这是一个胜利:
elementData=Arrays.copyOf(elementData,newCapacity);
}
}
这清楚地表明,存在检查,数据被复制和替换。
因此,简单地说,没有一个答案,测试条件可能会影响结果。它可能有助于找出导致差异的原因(在他特定的处理器、JVM、操作系统等上)来查看生成的字节码 对于readFromArrayList:
6: goto 25
9: getstatic #47; //Field ARRAY_LIST:Ljava/util/List;
12: iload_3
13: invokeinterface #116, 2; //InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
18: checkcast #17; //class java/lang/Integer
21: astore_0
22: iinc 3, 1
25: iload_3
26: getstatic #25; //Field INT_ARRAY:[Ljava/lang/Integer;
29: arraylength
30: if_icmplt 9
对于readFromArray:
6: goto 18
9: getstatic #25; //Field INT_ARRAY:[Ljava/lang/Integer;
12: iload_3
13: aaload
14: astore_0
15: iinc 3, 1
18: iload_3
19: getstatic #25; //Field INT_ARRAY:[Ljava/lang/Integer;
22: arraylength
23: if_icmplt 9
我不知道我是否购买了“三次”差异,但无论存在什么差异,都可以追溯到op#13:aaload(用于数组)与invokeinterface和checkcast(用于数组列表)之间的差异。测试写得很糟糕。你不能从中学到很多东西 确实,像
get
这样的访问器需要一些时间,但例如sunjvm可以将其中的许多访问器优化到几乎为零。特别是,ArrayList
get实际上不需要花费任何额外的时间
下面是一个基准测试(用Scala编写,但使用Java的数组和ArrayList
),演示了当您实际使用数组中的(所有)值时,差异有多小:
object ArraySpeed {
def ptime[A](f: => A) = {
val t0 = System.nanoTime
val ans = f
printf("Elapsed: %.3f seconds\n",(System.nanoTime-t0)*1e-9)
ans
}
val a = Array.range(0,1000000).map(x => new java.lang.Integer(x))
val b = new java.util.ArrayList[java.lang.Integer]
a.foreach(x => b.add(x))
var j = 0
def jfroma = {
var i=0
while (i<1000000) {
j += a(i).intValue
i += 1
}
j
}
def jfromb = {
var i=0
while (i<1000000) {
j += b.get(i).intValue
i += 1
}
j
}
def main(args: Array[String]) {
for (i <- 1 to 5) {
ptime(for (j <- 1 to 100) yield jfroma)
ptime(for (j <- 1 to 100) yield jfromb)
println
}
}
}
Scala字节码与Java字节码非常相似,所以这是一个相当公平的比较。(scala命令只是一个包装器,可以在类路径中使用正确的库调用
java
。没有最好的方法。根据不同的网络情况,我有3种不同的方法:
1.当我必须在低成本操作的情况下进行大量的循环时-是的,改进数据访问可以为您提供良好的优化百分比。
2.如果情况与1相同。但由于内部操作繁重,优化访问只是一个非常小的优化,更好的方法是优化对象中的字段和操作。
3.大量的周期和繁重的计算,没有更多的优化-使一些'坏习惯'编程。
例如:不是每次都返回新值,而是将工作变量传递给函数并返回其中的结果。这听起来很愚蠢,但减少了变量创建和内存碎片。在我的情况下,这给了我15-25%的时间。原因?减少GC调用。没有时间调用构造函数。00036的区别,你是认真的吗?它是关于查找时间而不是插入时间。+1表示世界“空”。。。今天我完全要找个借口用这个。我问这个问题是从理论的角度,而不是从实践的角度。我要问的是这两者之间的一个主要区别:它们都是堆上的对象,并且都在末尾使用数组。@Yaron:但是您要开始使用fr
$ scalac ArraySpeed.scala
$ scala ArraySpeed
Elapsed: 0.324 seconds // This is direct array access
Elapsed: 0.378 seconds // This is ArrayList
Elapsed: 0.326 seconds
Elapsed: 0.389 seconds
Elapsed: 0.355 seconds
Elapsed: 0.349 seconds
Elapsed: 0.323 seconds
Elapsed: 0.333 seconds
Elapsed: 0.318 seconds
Elapsed: 0.331 seconds