【问题标题】:Performance of 2D array allocation二维数组分配的性能
【发布时间】:2014-08-10 11:05:35
【问题描述】:

我想知道为什么一次分配一个 2D int 数组 (new int[50][2]) 性能比单独分配差,即先执行new int[50][],然后一个接一个地执行new int[2]。这是一个非专业的基准代码:

public class AllocationSpeed {

    private static final int ITERATION_COUNT = 1000000;

    public static void main(String[] args) {
        new AllocationSpeed().run();
    }

    private void run() {
        measureSeparateAllocation();
        measureAllocationAtOnce();
    }

    private void measureAllocationAtOnce() {
        Stopwatch stopwatch = Stopwatch.createStarted();
        for (int i = 0; i < ITERATION_COUNT; i++) {
            allocateAtOnce();
        }
        stopwatch.stop();
        System.out.println("Allocate at once: " + stopwatch);
    }

    private int allocateAtOnce() {
        int[][] array = new int[50][2];
        return array[10][1];
    }

    private void measureSeparateAllocation() {
        Stopwatch stopwatch = Stopwatch.createStarted();
        for (int i = 0; i < ITERATION_COUNT; i++) {
            allocateSeparately();
        }
        stopwatch.stop();
        System.out.println("Separate allocation: " + stopwatch);
    }

    private int allocateSeparately() {
        int[][] array = new int[50][];
        for (int i = 0; i < array.length; i++) {
            array[i] = new int[2];
        }
        return array[10][1];
    }
}

我在 64 位 linux 上测试,这些是不同 64 位 oracle java 版本的结果:

1.6.0_45-b06:

Separate allocation: 401.0 ms
Allocate at once: 1.673 s

1.7.0_45-b18

Separate allocation: 408.7 ms
Allocate at once: 1.448 s

1.8.0-ea-b115

Separate allocation: 380.0 ms
Allocate at once: 1.251 s

出于好奇,我也尝试使用 OpenJDK 7(差异较小):

Separate allocation: 424.3 ms
Allocate at once: 1.072 s

对我来说这很违反直觉,我希望立即分配更快。

【问题讨论】:

  • 我已经尝试了不同的尺寸和Object[][],但没有任何改变。要么我真的瞎了,要么你发现了一些有趣的东西。或者 JIT 比我们聪明并消除了部分工作。

标签: java arrays performance


【解决方案1】:

绝对令人难以置信。基准源可能会受到优化、gc 和 JIT 的影响,但是这个呢?

看着java byte code instruction set

  • anewarray(+ 2 字节间接类索引)用于对象类数组(a = 地址)
  • newarray(+1 字节用于原始类)用于原始类型数组
  • multianewarray(+ 2 字节间接类索引)用于多维数组

这导致人们怀疑 multianewarray 对于原始类型不是最优的。

在进一步研究之前,我希望有人知道我们在哪里被误导了。

【讨论】:

  • 字节码几乎不会比源代码告诉我们更多关于速度的信息,因为是 JITC 决定了速度。
  • 您介意扩展一下为什么您认为multianewarray 对于基元来说不是最理想的吗?我不太听从你的推论。作为旁注,我个人认为您的答案更适合作为评论,但这可能没什么大不了的......
  • 似乎multianew 已经失去了关于分配是关于原语的即时知识,而不是newarray。它可能会为每个int[] 做类似anewarray 的事情。但我并不声称这绝对是这样。事实上,这将是一个糟糕的实现。
  • 为什么那个实现会不好?它本质上不等同于OP的代码吗?关于为什么会丢失有关原语的信息,首先想到的是,这可能是为了处理任意嵌套的数组,但我怀疑这是正确的......
  • 但我只是复制了它,结果相同
【解决方案2】:

后一个代码的内循环(带有newarray)比前一个代码的multianewarray 被命中的次数更多,因此它可能会命中C2 并更快地进行转义分析。 (一旦发生这种情况,后面的代码创建的行会被分配到堆栈上,这比堆更快并且减少了垃圾收集器的工作量。)

也有可能这些 JDK 版本实际上并没有对来自 multianewarray 的行进行转义分析,因为多维数组更有可能超过堆栈数组的大小限制。

【讨论】:

    猜你喜欢
    • 2020-09-05
    • 2010-11-17
    • 2013-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多