使用System.arraycopy(...)比复制数组的for循环更好吗?

Dar*_*ren 86 java

我想创建一个新的对象数组,将两个较小的数组放在一起.

它们不能为空,但大小可能为0.

我无法在这两种方式之间进行选择:它们是等效还是更高效(例如system.arraycopy()复制整个块)?

MyObject[] things = new MyObject[publicThings.length+privateThings.length];
System.arraycopy(publicThings, 0, things, 0, publicThings.length);
System.arraycopy(privateThings, 0, things,  publicThings.length, privateThings.length);
Run Code Online (Sandbox Code Playgroud)

要么

MyObject[] things = new MyObject[publicThings.length+privateThings.length];
for (int i = 0; i < things.length; i++) {
    if (i<publicThings.length){
        things[i] = publicThings[i]
    } else {
        things[i] = privateThings[i-publicThings.length]        
    }
}
Run Code Online (Sandbox Code Playgroud)

唯一的区别是代码的外观?

编辑:感谢链接的问题,但他们似乎有一个未解决的讨论:

如果it is not for native types:byte [],Object [],char [] 真的更快吗?在所有其他情况下,执行类型检查,这将是我的情况,因此将是等效的......不是吗?

在另一个相关问题上,他们说the size matters a lot,对于size> 24,system.arraycopy()获胜,小于10,手动for循环更好......

现在我真的很困惑.

Tre*_*all 85

public void testHardCopyBytes()
{
    byte[] bytes = new byte[0x5000000]; /*~83mb buffer*/
    byte[] out = new byte[bytes.length];
    for(int i = 0; i < out.length; i++)
    {
        out[i] = bytes[i];
    }
}

public void testArrayCopyBytes()
{
    byte[] bytes = new byte[0x5000000]; /*~83mb buffer*/
    byte[] out = new byte[bytes.length];
    System.arraycopy(bytes, 0, out, 0, out.length);
}
Run Code Online (Sandbox Code Playgroud)

我知道JUnit测试并不是最适合基准测试的,但
testHardCopyBytes需要0.157秒才能完成
,
testArrayCopyBytes需要0.086秒才能完成.

我认为这取决于虚拟机,但它看起来好像是复制内存块而不是复制单个数组元素.这绝对会提高性能.

编辑:
看起来System.arraycopy的表现已经到处都是.当使用字符串而不是字节,并且数组很小(大小为10)时,我得到以下结果:

    String HC:  60306 ns
    String AC:  4812 ns
    byte HC:    4490 ns
    byte AC:    9945 ns
Run Code Online (Sandbox Code Playgroud)

这是数组大小为0x1000000时的样子.看起来System.arraycopy肯定会赢得更大的数组.

    Strs HC:  51730575 ns
    Strs AC:  24033154 ns
    Bytes HC: 28521827 ns
    Bytes AC: 5264961 ns
Run Code Online (Sandbox Code Playgroud)

多奇怪啊!

谢谢,达人,指出参考文献的复制方式不同.这使得这个问题变得更加有趣!

  • 感谢您的努力,但您错过了看似关键点:非本机类型(使用anythign创建随机类,因此数组包含引用)和大小...似乎更小的数组大小,手动for循环更快.小心纠正这个? (2认同)
  • 哦哇,你说得对!这是串联的.将字符串放在那些数组而不是字节中会产生巨大的差异:<<< testHardCopyStrs:0.161s >>> <<< testArrayCopyStrs:0.170s >>> (2认同)

Phi*_*der 36

Arrays.copyOf(T[], int)更容易阅读.Internaly它使用的System.arraycopy()是本机调用.

你不能更快!

  • `copyOf` 不能总是替换 `arraycopy`,但它适用于这个用例。 (2认同)

s.t*_*.ts 15

它取决于虚拟机,但System.arraycopy应该为您提供最接近本机性能的功能.

我作为嵌入式系统的Java开发人员已经工作了两年(性能是一个非常重要的优先事项),并且可以使用System.arraycopy,我主要使用它/看到它在现有代码中使用.当性能成为问题时,它始终优先于循环.如果性能不是一个大问题,我会选择循环.更容易阅读.

  • 是的,这本身并不是"原生表现",这就是为什么我说我"大部分时间"都尽可能地使用它(你会发现它主要胜过循环复制).我想原因是:当它是一个原始类型的小数组时,"成本调用"大于性能提升.使用JNI会因同样的原因降低性能 - 本机代码本身很快,但是从java进程调用它 - 而不是那么多. (2认同)

200*_*ess 9

我没有依赖猜测和可能过时的信息,而是使用运行了一些基准测试.实际上,Caliper附带了一些例子,其中包括一个CopyArrayBenchmark测量这个问题的例子!你所要做的就是跑步

mvn exec:java -Dexec.mainClass=com.google.caliper.runner.CaliperMain -Dexec.args=examples.CopyArrayBenchmark
Run Code Online (Sandbox Code Playgroud)

我的结果基于Oracle的Java HotSpot(TM)64位服务器VM,1.8.0_31-b13,运行于2010年中期的MacBook Pro(带有Intel Arrandale i7的macOS 10.11.6,8 GiB RAM).我不相信发布原始时序数据是有用的.相反,我将用支持的可视化来总结结论.

综上所述:

  • for无论是对于短数组还是长数组,编写手动循环以将每个元素复制到新实例化的数组中都是不利的.
  • Arrays.copyOf(array, array.length)并且array.clone()都一直很快.这两种技术的性能几乎相同; 你选择哪一个是品味问题.
  • System.arraycopy(src, 0, dest, 0, src.length)几乎一样快和,但并不完全一致等等.(看看50000 秒的情况.)因为这个,以及电话的详细程度,如果您需要精确控制哪些元素被复制到哪里,我会建议.Arrays.copyOf(array, array.length)array.clone()intSystem.arraycopy()

以下是时间图:

用于复制长度为5的数组的计时 用于复制长度为500的阵列的计时 用于复制长度为50000的阵列的计时

  • int 复制有什么奇怪的吗?奇怪的是 arraycopy 在大规模整数上会很慢。 (5认同)

Rah*_*thi 6

执行本机方法Arrays.copyOf(T[], int)确实有一些开销,但并不意味着它使用JNI执行它并不快.

最简单的方法是编写基准测试和测试.

你可以检查它Arrays.copyOf(T[], int)比你的正常for循环更快.

来自这里的基准代码: -

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;
}
Run Code Online (Sandbox Code Playgroud)

System.arraycopy()使用JNI(Java Native Interface)来复制数组(或部分数组),所以它非常快,因为你可以在这里确认


小智 5

不可能Arrays.copyOfSystem.arraycopy于此,因为这是实现copyOf

public static int[] copyOf(int[] original, int newLength) {
    int[] copy = new int[newLength];
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}
Run Code Online (Sandbox Code Playgroud)