是Java的System.arraycopy()有效的小arrays?

对于小型数组,Java的System.arraycopy()效率如何,或者它是一个本地方法,这使得它可能比一个简单的循环和一个函数调用效率低得多?

本地方法是否会导致跨越某种Java系统桥的额外性能开销?

Sid写的东西有点扩大了, System.arraycopy很可能只是一个JIT内在的东西; 这意味着当代码调用System.arraycopy ,它很可能会调用特定于JIT的实现(一旦JIT标记System.arraycopy为“热”),而不是通过JNI接口执行,所以它不会产生本地方法的正常开销。

通常,执行本地方法确实会有一些开销(通过JNI接口,当执行本地方法时也不会发生一些内部的JVM操作)。 但是这并不是因为一个方法被标记为“native”,而是使用JNI实际执行它。 JIT可以做一些疯狂的事情。

最简单的检查方法是,如前所述,编写一个小基准,小心Java的基本标记(首先预热代码,避免代码没有副作用,因为JIT只是将其优化为no-op等等)。

这是我的基准代码:

 public void test(int copySize, int copyCount, int testRep) { System.out.println("Copy size = " + copySize); System.out.println("Copy count = " + copyCount); System.out.println(); for (int i = testRep; i > 0; --i) { copy(copySize, copyCount); loop(copySize, copyCount); } System.out.println(); } public void copy(int copySize, int copyCount) { int[] src = newSrc(copySize + 1); int[] dst = new int[copySize + 1]; long begin = System.nanoTime(); for (int count = copyCount; count > 0; --count) { System.arraycopy(src, 1, dst, 0, copySize); dst[copySize] = src[copySize] + 1; System.arraycopy(dst, 0, src, 0, copySize); src[copySize] = dst[copySize]; } long end = System.nanoTime(); System.out.println("Arraycopy: " + (end - begin) / 1e9 + " s"); } public void loop(int copySize, int copyCount) { int[] src = newSrc(copySize + 1); int[] dst = new int[copySize + 1]; long begin = System.nanoTime(); for (int count = copyCount; count > 0; --count) { for (int i = copySize - 1; i >= 0; --i) { dst[i] = src[i + 1]; } dst[copySize] = src[copySize] + 1; for (int i = copySize - 1; i >= 0; --i) { src[i] = dst[i]; } src[copySize] = dst[copySize]; } long end = System.nanoTime(); System.out.println("Man. loop: " + (end - begin) / 1e9 + " s"); } public int[] newSrc(int arraySize) { int[] src = new int[arraySize]; for (int i = arraySize - 1; i >= 0; --i) { src[i] = i; } return src; } 

在我的testing中,使用copyCount = 10000000(1e7)或更高的值调用test()允许在第一次copy/loop调用期间实现预热,所以使用testRep = 5就足够了; 对于copyCount = 1000000(1e6),预热需要至less2或3次迭代,因此应该增加testRep以获得可用的结果。

通过我的configuration(CPU Intel Core 2 Duo E8500 @ 3.16GHz,Java SE 1.6.0_35-b10和Eclipse 3.7.2),从基准testing中可以看出:

  • copySize = 24时, System.arraycopy()和手动循环几乎是一样的时间(有时一个比另一个快一些,其他时间则相反),
  • copySize <24时,手动循环比System.arraycopy()快( copySize = 23时稍快, copySize <5时更快),
  • copySize > 24时, System.arraycopy()比手动循环快( copySize = 25稍快copySize ,比率循环时间/ copySize时间随copySize增加而增加)。

注意:我不是英语母语的人,请原谅我所有的语法/词汇错误。

这是一个有效的关注。 例如,在java.nio.DirectByteBuffer.put(byte[]) ,作者试图避免less量元素的JNI副本

 // These numbers represent the point at which we have empirically // determined that the average cost of a JNI call exceeds the expense // of an element by element copy. These numbers may change over time. static final int JNI_COPY_TO_ARRAY_THRESHOLD = 6; static final int JNI_COPY_FROM_ARRAY_THRESHOLD = 6; 

对于System.arraycopy() ,我们可以检查JDK如何使用它。 例如,在ArrayList ,总是使用System.arraycopy() ,而不pipe“长度是多less”(即使是0),也不要使用“逐个元素拷贝”。 由于ArrayList非常注重性能,所以我们可以推导出System.arraycopy()是数组复制最有效的方法,不pipe长度如何。

System.arraycopy使用memmove操作来移动单词,并使用汇编来移动场景后面的其他基本types。 所以它尽最大努力提高效率。

字节代码无论如何都是本地执行的,所以性能可能比循环更好。

所以在循环的情况下,它将不得不执行字节代码,这将导致开销。 而arrays副本应该是直的memcopy。

原生函数应该比JVM函数更快,因为没有VM开销。 然而对于很多(> 1000)非常小(len <10)的数组,它可能会变慢。