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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18638743/
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-08-12 09:45:35  来源:igfitidea点击:

Is it better to use System.arraycopy(...) than a for loop for copying arrays?

java

提问by Daren

I want to create a new array of objects putting together two smaller arrays.

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

They can't be null, but size may be 0.

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

I can't chose between these two ways: are they equivalent or is one more efficient (for example system.arraycopy() copies whole chunks)?

我无法在这两种方式之间进行选择:它们是等效的还是一种更有效(例如 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);

or

或者

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]        
    }
}

Is the only difference the look of the code?

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

EDIT:thanks for linked question, but they seem to have an unsolved discussion:

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

Is it truly faster if it is not for native types: byte[], Object[], char[]? in all other cases, a type check is executed, which would be my case and so would be equivalent... no?

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

On another linked question, they say that the size matters a lot, for size >24 system.arraycopy() wins, for smaller than 10, manual for loop is better...

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

Now I'm really confused.

现在我真的很困惑。

采纳答案by Trent Small

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);
}

I know JUnit tests aren't really the best for benchmarking, but
testHardCopyBytes took 0.157s to complete
and
testArrayCopyBytes took 0.086s to complete.

I think it depends on the virtual machine, but it looks as if it copies blocks of memory instead of copying single array elements. This would absolutely increase performance.

我知道 JUnit 测试对于基准测试来说并不是最好的,但是
testHardCopyBytes 需要 0.157 秒才能完成,

testArrayCopyBytes 需要 0.086 秒才能完成。

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

EDIT:
It looks like System.arraycopy 's performance is all over the place. When Strings are used instead of bytes, and arrays are small (size 10), I get these results:

编辑:
看起来 System.arraycopy 的性能无处不在。当使用字符串代替字节并且数组很小(大小为 10)时,我得到以下结果:

    String HC:  60306 ns
    String AC:  4812 ns
    byte HC:    4490 ns
    byte AC:    9945 ns

Here is what it looks like when arrays are at size 0x1000000. It looks like System.arraycopy definitely wins with larger arrays.

这是数组大小为 0x1000000 时的样子。看起来 System.arraycopy 肯定会在更大的数组中获胜。

    Strs HC:  51730575 ns
    Strs AC:  24033154 ns
    Bytes HC: 28521827 ns
    Bytes AC: 5264961 ns

How peculiar!

Thanks, Daren, for pointing out that references copy differently. It made this a much more interesting problem!

多么奇特!

谢谢达伦指出引用的复制方式不同。它使这成为一个更有趣的问题!

回答by Daren

It depends on the virtual machine, but System.arraycopy should give you the closest you can get to native performance.

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

I've worked for 2 years as a java developer for embedded systems (where performance is a huge priority) and everywhere System.arraycopy could be used, I've mostly used it / seen it used in existing code. It's always preferred over loops when performance is an issue. If performance isn't a big issue, I'd go with the loop, though. Much easier to read.

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

回答by Philipp Sander

Arrays.copyOf(T[], int)is easier to read. Internaly it uses System.arraycopy()which is a native call.

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

You can't get it faster!

你不能更快地得到它!

回答by Nandkumar Tekale

System.arraycopy()is a native call which does copy operation directly at memory. Single memory copy would be always faster than your for loop

System.arraycopy()是一个本地调用,它直接在内存中进行复制操作。单个内存复制总是比你的 for 循环快

回答by Rahul Tripathi

Executing native methods like Arrays.copyOf(T[], int)does have some overhead but it doesnot mean that it is not fast as you are executing it using JNI.

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

The easiest way is to write a benchmark and test.

最简单的方法是编写一个基准和测试。

You can check that Arrays.copyOf(T[], int)is faster than your normal forloop.

您可以检查它Arrays.copyOf(T[], int)是否比正常for循环更快。

The benchmark code from here:-

来自这里的基准代码:-

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;
}

System.arraycopy()uses JNI (Java Native Interface) to copy an array (or parts of it), so it is blazingly fast, as you can confirm here

System.arraycopy()使用 JNI(Java 本机接口)复制数组(或其中的一部分),因此速度非常快,您可以在此处确认

回答by 200_success

Instead of relying on speculation and possibly outdated information, I ran some benchmarks using caliper. In fact, Caliper comes with some examples, including a CopyArrayBenchmarkthat measures exactly this question! All you have to do is run

我没有依赖推测和可能过时的信息,而是使用caliper运行了一些基准测试。事实上,Caliper 附带了一些示例,其中包括一个CopyArrayBenchmark可以准确衡量这个问题的示例!你所要做的就是跑

mvn exec:java -Dexec.mainClass=com.google.caliper.runner.CaliperMain -Dexec.args=examples.CopyArrayBenchmark

My results are based on Oracle's Java HotSpot(TM) 64-Bit Server VM, 1.8.0_31-b13, running on a mid-2010 MacBook Pro (macOS 10.11.6 with an Intel Arrandale i7, 8 GiB RAM). I don't believe that it's useful to post the raw timing data. Rather, I'll summarize the conclusions with the supporting visualizations.

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

In summary:

总之:

  • Writing a manual forloop to copy each element into a newly instantiated array is never advantageous, whether for short arrays or long arrays.
  • Arrays.copyOf(array, array.length)and array.clone()are both consistently fast. These two techniques are nearly identical in performance; which one you choose is a matter of taste.
  • System.arraycopy(src, 0, dest, 0, src.length)is almost as fast as Arrays.copyOf(array, array.length)and array.clone(), but not quite consistently so. (See the case for 50000 ints.) Because of that, and the verbosity of the call, I would recommend System.arraycopy()if you need fine control over which elements get copied where.
  • 编写手动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()

Here are the timing plots:

以下是时序图:

Timings for copying arrays of length 5Timings for copying arrays of length 500Timings for copying arrays of length 50000

复制长度为 5 的数组的时间复制长度为 500 的数组的时间复制长度为 50000 的数组的时间

回答by DimaD

It is not possible that Arrays.copyOfis faster than System.arraycopysince this is the implementation of copyOf:

这是不可能的Arrays.copyOf比速度更快System.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;
}