Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/304.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
是Java';s System.arraycopy()对于小型阵列是否有效?_Java_Performance_Caliper - Fatal编程技术网

是Java';s System.arraycopy()对于小型阵列是否有效?

是Java';s System.arraycopy()对于小型阵列是否有效?,java,performance,caliper,Java,Performance,Caliper,Java的System.arraycopy()对小型数组有效吗?还是因为它是本机方法,所以它的效率可能比简单的循环和函数调用低很多 本机方法是否会因跨越某种Java系统桥而产生额外的性能开销?字节码以本机方式执行,因此性能可能比循环更好 因此,在循环的情况下,它必须执行字节码,这将导致开销。而数组拷贝应该是直接的memcopy。本机函数应该比JVM函数快,因为没有VM开销。然而,对于许多(>1000)非常小(len在Sid编写的内容上稍微扩展一下)的人来说,System.arraycopy很可能

Java的
System.arraycopy()
对小型数组有效吗?还是因为它是本机方法,所以它的效率可能比简单的循环和函数调用低很多


本机方法是否会因跨越某种Java系统桥而产生额外的性能开销?

字节码以本机方式执行,因此性能可能比循环更好


因此,在循环的情况下,它必须执行字节码,这将导致开销。而数组拷贝应该是直接的memcopy。

本机函数应该比JVM函数快,因为没有VM开销。然而,对于许多(>1000)非常小(len在Sid编写的内容上稍微扩展一下)的人来说,
System.arraycopy
很可能只是一个JIT内在特性;这意味着当代码调用
System.arraycopy
时,它很可能会调用特定于JIT的实现(一旦JIT标记
System.arraycopy
)不是通过JNI接口执行的,因此不会产生本机方法的正常开销

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


最简单的检查方法是,正如所建议的那样,编写一个小的基准测试,小心Java微基准测试的正常警告(首先预热代码,避免代码没有副作用,因为JIT只是将其优化为无操作等)。

这是一个有效的问题。例如,在
Java.nio.DirectByteBuffer.put(byte[])
,作者试图避免对少量元素进行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()
,无论长度如何(即使是0),都不会“逐个元素复制”。因为
ArrayList
非常注重性能,我们可以推导出
System.arraycopy()
是最有效的数组复制方法,无论长度如何。

以下是我的基准代码:

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;
}
在我的测试中,使用
copyCount
=10000000(1e7)或更大的值调用
test()
,可以在第一次
copy/loop
调用期间实现预热,因此使用
testRep
=5就足够了;使用
copyCount
=1000000(1e6)预热至少需要2或3次迭代,因此应增加
testRep
,以获得可用的结果

对于我的配置(CPU Intel Core 2 Duo E8500@3.16GHz、Java SE 1.6.0_35-b10和Eclipse 3.7.2),从基准测试中可以看出:

  • copySize
    =24时,
    System.arraycopy()
    和手动循环所用的时间几乎相同(有时一个比另一个稍微快一点,有时则相反)
  • copySize
    <24时,手动循环比
    System.arraycopy()
    快(当
    copySize
    =23时稍快,当
    copySize
    <5时确实快)
  • copySize
    >24时,
    System.arraycopy()
    比手动循环快(当
    copySize
    =25时,循环时间/arraycopy时间的比率随着
    copySize
    的增加而增加)

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

系统。arraycopy
使用
memmove
操作来移动单词,并在幕后移动C中的其他基元类型。因此,它会尽最大努力以尽可能高效的方式移动。

推测和可能过时的信息,我使用运行了一些基准测试。事实上,Caliper附带了一些示例,包括一个测量这个问题的工具!您所要做的就是运行

mvn exec:java-Dexec.mainClass=com.google.caliper.runner.CaliperMain-Dexec.args=examples.copyraraybenchmark
我的结果是基于Oracle的Java HotSpot(TM)64位服务器VM,1.8.0_31-b13,运行在2010年年中的MacBook Pro(macOS 10.11.6,采用Intel Arrandale i7,8 GiB RAM)上。我不相信发布原始计时数据是有用的。相反,我将用支持的可视化来总结结论

总之:

  • 为循环编写手册
    ,将每个元素复制到新实例化的数组中从来都不是一件好事,即使数组只有5个元素。
  • 这两种技术在性能上几乎是相同的,你选择哪一种取决于你的品味
  • 几乎与
    array.copyOf(array,array.length)
    array.clone()
    一样快,但速度并不一致(请参见50000
    int
    s的例子)。因此,鉴于调用的冗长性,如果您需要精确控制哪些元素被复制到哪里,我建议您使用
    System.arraycopy()
以下是计时图:


你试过并运行过基准测试吗?我很想在上面看到一个微基准。我认为内置的本机代码不受JNI的影响latencies@glowcoder,我想看看你的一个微型基准测试,这是一个很难正确回答的问题:有一个特定的基准测试可以处理大量的小数据(小于缓存线)复制,所有JDK实现者都知道这一点,并希望以此为目标。从Java到本机代码的转换会带来额外的成本。@AndyThomas Cramer,在上下文切换到JNI方面根本没有本机代码。旁注:JIT足够聪明,可以通过循环将数组复制优化为与
System.arraycopy
相同的代码JIT足够聪明,可以