[java] 배열 복사를 위해 for 루프보다 System.arraycopy (…)를 사용하는 것이 더 낫습니까?

두 개의 작은 배열을 모아 새로운 객체 배열을 만들고 싶습니다.

null 일 수 없지만 크기는 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);

또는

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

유일한 차이점은 코드의 모양입니까?

편집 : 연결된 질문에 감사하지만 해결되지 않은 토론이있는 것 같습니다.

it is not for native typesbyte [], Object [], char [] 이면 정말 더 빠릅니까? 다른 모든 경우에는 유형 검사가 실행됩니다. 이것은 내 경우이므로 동등합니다.

또 다른 연결된 질문에서 그들은 the size matters a lot크기가> 24 인 경우 system.arraycopy ()가 이기고 10보다 작은 경우 수동 for 루프가 더 좋다고 말합니다 .

이제 정말 혼란 스러워요.



답변

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

나는 JUnit 테스트 정말 벤치마킹을위한 최상의하지 알고 있지만
testHardCopyBytes 완료에 0.157s했다

testArrayCopyBytes 완료에 0.086s했다.

가상 머신에 의존한다고 생각하지만 단일 배열 요소를 복사하는 대신 메모리 블록을 복사하는 것처럼 보입니다. 이것은 절대적으로 성능을 향상시킬 것입니다.

편집 :
System.arraycopy의 성능이 온통있는 것 같습니다. 바이트 대신 문자열이 사용되고 배열이 작 으면 (크기 10) 다음과 같은 결과가 나타납니다.

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

배열의 크기가 0x1000000 일 때의 모습은 다음과 같습니다. System.arraycopy가 더 큰 배열에서 확실히 승리하는 것처럼 보입니다.

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

얼마나 특이한가!

참고 문헌이 다르게 복사된다는 점을 지적 해 주신 Daren에게 감사드립니다. 이것은 이것을 훨씬 더 흥미로운 문제로 만들었습니다!


답변

Arrays.copyOf(T[], int)읽기 쉽습니다. 내부적으로는 System.arraycopy()네이티브 호출을 사용합니다.

더 빨리 얻을 수 없습니다!


답변

가상 머신에 따라 다르지만 System.arraycopy는 기본 성능에 가장 근접한 성능을 제공해야합니다.

저는 2 년 동안 임베디드 시스템 (성능이 가장 중요한 곳)을위한 자바 개발자로 일했으며 System.arraycopy를 사용할 수있는 모든 곳에서 주로 사용했거나 기존 코드에서 사용하는 것을 보았습니다. 성능이 문제가 될 때 항상 루프보다 선호됩니다. 성능이 큰 문제가 아니라면 루프를 사용합니다. 훨씬 읽기 쉽습니다.


답변

추측과 구식 정보에 의존하는 대신 다음을 사용하여 몇 가지 벤치 마크를 실행했습니다. . 실제로 Caliper에는 CopyArrayBenchmark이 질문을 정확하게 측정하는를 포함하여 몇 가지 예제가 있습니다 ! 당신이해야 할 일은 달리기

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

내 결과는 2010 년 중반 MacBook Pro (Intel Arrandale i7, 8GiB RAM이있는 macOS 10.11.6)에서 실행되는 Oracle의 Java HotSpot (TM) 64 비트 서버 VM, 1.8.0_31-b13을 기반으로합니다. 원시 타이밍 데이터를 게시하는 것이 유용하다고 생각하지 않습니다. 오히려 지원 시각화를 통해 결론을 요약하겠습니다.

요약해서 말하자면:

  • for각 요소를 새로 인스턴스화 된 배열로 복사 하는 수동 루프를 작성하는 것은 짧은 배열이든 긴 배열이든간에 결코 유리하지 않습니다.
  • Arrays.copyOf(array, array.length)그리고 array.clone()둘 다 지속적으로 빠릅니다. 이 두 기술은 성능면에서 거의 동일합니다. 당신이 선택하는 것은 취향의 문제입니다.
  • System.arraycopy(src, 0, dest, 0, src.length)는 및 만큼 빠르지 만 일관성이 없습니다. (50000 s 의 경우를 참조하십시오 .) 그것과 호출의 장황함 때문에 어떤 요소가 어디에서 복사되는지에 대한 미세 제어가 필요한 경우 권장 합니다.Arrays.copyOf(array, array.length)array.clone()intSystem.arraycopy()

타이밍 플롯은 다음과 같습니다.

길이 5 배열 복사 타이밍
길이가 500 인 배열을 복사하기위한 타이밍
길이가 50000 인 배열 복사 타이밍


답변

같은 네이티브 메서드를 실행 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;
}

System.arraycopy()JNI (Java Native Interface)를 사용하여 배열 (또는 그 일부)을 복사하므로 여기에서 확인할 수 있듯이 매우 빠릅니다.


답변

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


답변

System.arraycopy()메모리에서 직접 복사 작업을 수행하는 기본 호출입니다. 단일 메모리 복사본은 항상 for 루프보다 빠릅니다.