Java的System.arraycopy()对小型数组有效吗?

2022-08-31 17:34:59

Java对于小型数组是否有效,或者它是一种本机方法的事实是否使它可能比简单的循环和函数调用效率低得多?System.arraycopy()

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


答案 1

稍微扩展一下Sid所写的内容,很可能只是JIT的内在功能;这意味着当代码调用时,它很可能会调用一个特定于JIT的实现(一旦JIT标签为“hot”),该实现不是通过JNI接口执行的,因此它不会产生本机方法的正常开销。System.arraycopySystem.arraycopySystem.arraycopy

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

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


答案 2

这是我的基准代码:

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

根据我的测试,使用 = 10000000 (1e7) 或更大的呼叫允许在第一次调用期间实现预热,因此使用 = 5 就足够了;对于 = 1000000 (1e6),预热至少需要 2 或 3 次迭代,因此应增加以获得可用结果。test()copyCountcopy/looptestRepcopyCounttestRep

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

  • 当 = 24 时,手动循环几乎花费相同的时间(有时一个比另一个稍微快一些,其他时候则相反),copySizeSystem.arraycopy()
  • 当< 24 时,手动循环比(= 23稍快,< 5)更快),copySizeSystem.arraycopy()copySizecopySize
  • 当> 24 时,比手动循环快(= 25 时稍快,循环时间/数组复制时间之比随增加而增加)。copySizeSystem.arraycopy()copySizecopySize

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


推荐