哪个代码运行得更快?

时间:2013-09-27 07:23:46

标签: java performance

我有两段代码,我想知道哪些代码运行速度更快,为什么速度更快。我对JVM和CPU的了解较少,但我很努力。每个提示都会有所帮助。

int[] a=new int[1000];
int[] b=new int[10000000];
long start = System.currentTimeMillis();
//method 1
for(int i=0;i<1000;i++){
    for(int j=0;j<10000000;j++){
        a[i]++;
    }
}
long end = System.currentTimeMillis();
System.out.println(end-start);

start=System.currentTimeMillis();
//method 2
for(int i=0 ;i<10000000;i++){
    for(int j=0;j<1000;j++){
        b[i]++;
    }
}
end = System.currentTimeMillis();
System.out.println(end-start);

8 个答案:

答案 0 :(得分:5)

<强>复杂性

就渐近复杂度(例如big-O表示法)而言,它们具有相同的运行时间。

数据本地化

暂时忽略任何优化......

b更大,因此更有可能分割为多个(或更多)pages。因此,第一个可能更快。

这里的差异可能相当小,除非并非所有这些页面都适合RAM并需要写入磁盘(由于b只有10000000 * 4 = 40000000字节= 38,因此不大可能MB)。

<强>优化

第一种方法涉及“执行a[i]++ 10000000次”(对于固定的i),理论上可以很容易地被优化器转换为a[i] += 10000000

b可以进行类似的优化,但仅限b[i] += 1000,仍需运行10000000次。

优化器可以自由地执行此操作或不执行此操作。据我所知,Java语言规范没有说明应该和不应该优化的内容,只要它不会改变最终结果。

作为一个极端的结果,理论上,优化器可以看到你在循环之后没有对ab做任何事情,从而摆脱了两个循环。

答案 1 :(得分:5)

我会把答案放在那里,理论上它们将完全相同,但在实践中会有一个小的,但可以忽略不计的差异。实际上,实际上太小了。

基本思想是数组b如何存储在内存中。因为它要大得多,取决于你的平台/实现,它可能存储在块中,也就是非连续的。这可能是因为1000万英寸的数组是4000万字节= 40 MB!

编辑:我分别得到572和593。

答案 2 :(得分:1)

第一个循环在我的系统上运行得更快(中位数:333毫秒对596毫秒)
(编辑:我在第一次回复中对数组访问次数做了错误的假设,请参阅注释)

后续增量(index ++)对同一数组的访问似乎比随机访问或减量(index--)访问更快。我假设 Java Hotspot编译器可以优化数组绑定检查,如果它识别出将逐步遍历该数组。

反转循环时,实际上运行速度较慢:

//incremental array index
for (int i = 0; i < 1000; i++) {
    for (int j = 0; j < 10000000; j++) {
        a[i]++;
    }
}

//decremental array index
for (int i = 1000 - 1; i >= 0; i--) {
    for (int j = 10000000 - 1; j >= 0; j--) {
        a[i]++;
    }
}

增量:349ms,减量:485ms。 没有边界检查,递减循环通常更快,特别是在旧处理器上(比较为零)。

如果我的假设是正确的,那么就会进行1000次优化边界检查而不是10000000次检查,因此第一种方法更快。

顺便说一句,在进行基准测试时:

  • 多轮比较平均值/介质而不是第一个样本
  • 在Java中:为您的基准测试提供预热阶段(在测量之前执行几次)。在第一次运行时,必须加载类,并且可以在 HotSpot VM功能启动并执行本机编译
  • 之前解释代码
  • 使用System.nanoTime()衡量时间增量。这样可以提供更准确的时间戳System.currentTimeMillis()不是那么精确(取决于VM),并且通常在十几毫秒或更多毫秒的桶中“跳”,使得结果时间比实际更加不稳定。顺便说一下:1毫秒= 1'000'000纳秒。

答案 3 :(得分:0)

我的猜测是它们几乎都是一样的。其中一个有一个较小的数组可以处理,但除了内存的初始分配之外没有太大的区别,这无论如何都在你的测量范围之外。

执行每次迭代的时间应该相同(将值写入数组)。递增较大的数字不应该使JVM比递增较小的数字更长,也不应该处理更小或更大的数组索引。

但为什么问题,如果你已经知道如何衡量自己?

答案 4 :(得分:0)

查看大符号

嵌套的for循环是O(n ^ 2) - 它们在理论上运行相同。

数字1000或100000是常数k O(n ^ 2 + k)

它们在实践中并不完全相同,因为其他各种各样的东西在起作用,但它会很接近。

答案 5 :(得分:0)

时间应该相等,结果显然会有所不同,因为a将包含1000个值为10000的条目,b将包含10000000个值为1000的条目。我真的不明白你的问题。最终开始的结果是什么? 可能是JVM将优化forloops,如果它理解最终结果将在数组中比最小数组更容易计算,因为它只需要1000个赋值,而另一个需要10 000倍以上

答案 6 :(得分:0)

首先是fuster。由于第一个ai单元格的初始化次数要少得多。

答案 7 :(得分:0)

现代架构很复杂,所以回答这类问题绝非易事。

运行时间可能相同,或者第一次可能更快。

在这种情况下要考虑的事情主要是内存访问和优化。

一个好的优化器会意识到永远不会读取这些值,因此可以完全跳过循环,这两种情况下的运行时间都为0。此类优化可以在compile timerun-time进行。

如果没有优化,那么就需要考虑内存访问。 a[]远小于b[],因此它更容易适应更快的缓存内存,因此cache misses更少。

另一件需要考虑的事情是memory interleaving