声明64个元素的多个数组比声明65个元素的数组快1000倍

Sip*_*pko 91 java arrays

最近我注意到声明一个包含64个元素的数组比用65个元素声明相同类型的数组快得多(> 1000倍).

这是我用来测试这个的代码:

public class Tests{
    public static void main(String args[]){
        double start = System.nanoTime();
        int job = 100000000;//100 million
        for(int i = 0; i < job; i++){
            double[] test = new double[64];
        }
        double end = System.nanoTime();
        System.out.println("Total runtime = " + (end-start)/1000000 + " ms");
    }
}
Run Code Online (Sandbox Code Playgroud)

这将运行在大约6毫秒,如果我更换new double[64]new double[65]它需要大约7秒.如果作业分布在越来越多的线程中,这个问题会变得更加严重,这是我的问题所在.

不同类型的数组(例如int[65]或)也会出现此问题String[65].大字符串不会发生此问题:String test = "many characters";但是在更改为此字符串时会发生此问题String test = i + "";

我想知道为什么会这样,如果有可能绕过这个问题.

nos*_*sid 88

您正在观察由Java VM的JIT编译器执行的优化所导致的行为.此行为可通过最多64个元素的标量数组触发,并且不会被大于64的数组触发.

在详细介绍之前,让我们仔细看看循环体:

double[] test = new double[64];
Run Code Online (Sandbox Code Playgroud)

身体没有效果(可观察到的行为).这意味着无论是否执行此语句,它在程序执行之外都没有区别.整个循环也是如此.因此可能会发生,代码优化器将循环转换为具有相同功能和不同时序行为的某些(或没有).

对于基准测试,您至少应遵循以下两个准则.如果你这样做了,差异就会明显变小.

  • 通过多次执行基准来预热JIT编译器(和优化器).
  • 使用每个表达式的结果并在基准测试结束时打印它.

现在让我们详细介绍一下.毫不奇怪,对于不超过64个元素的标量数组,会触发优化.优化是Escape分析的一部分.它将小对象和小数组放入堆栈而不是在堆上分配它们 - 甚至更好地完全优化它们.您可以在2005年撰写的Brian Goetz的以下文章中找到有关它的一些信息:

可以使用命令行选项禁用优化-XX:-DoEscapeAnalysis.标量数组的魔术值64也可以在命令行中更改.如果按如下方式执行程序,则具有64和65个元素的数组之间没有区别:

java -XX:EliminateAllocationArraySizeLimit=65 Tests
Run Code Online (Sandbox Code Playgroud)

话虽如此,我强烈反对使用这样的命令行选项.我怀疑它在现实应用中有很大的不同.我只会使用它,如果我绝对相信必要性 - 而不是基于某些伪基准测试的结果.

  • @nosid:虽然OP的代码可能不太现实,但它显然会在JVM中触发一个有趣/意外的行为,这可能会对其他情况产生影响.我认为问为什么会发生这种情况是有道理的. (10认同)
  • 但是为什么优化器检测到大小为64的数组是可移动的但不是65 (9认同)
  • @Sipko:您写的是应用程序没有按线程数进行扩展.这表明问题与您所询问的微优化无关.我建议看大图而不是小部件. (2认同)