Java 哪种代码运行得更快?
我有两段代码,我想知道它们运行时哪个更快,为什么更快。我对JVM和CPU了解较少,但我正在努力学习。每一个提示都会有帮助Java 哪种代码运行得更快?,java,performance,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]++; } }
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);
int[]a=新的int[1000];
int[]b=新int[10000000];
长启动=System.currentTimeMillis();
//方法1
对于(int i=0;i我的猜测是,它们的数组几乎相同。其中一个要处理的数组较小,但除了内存的初始分配之外,这没有多大区别,因为内存的初始分配超出了您的测量范围
执行每个迭代的时间应该相同(将值写入数组)。增加较大的数字不会比增加较小的数字花费JVM更长的时间,也不应该寻址较小或较大的数组索引
但是,如果你已经知道如何测量自己,为什么还要问这个问题呢?看看大oh符号
嵌套的for循环是O(n^2)-理论上它们将运行相同的循环
1000或100000是一个常数ko(n^2+k)
它们在实践中并不完全相同,因为其他因素在起作用,但它们会很接近。时间应该相等,结果显然会不同,因为a将包含1000个值为10000的条目,b将包含10000000个值为1000的条目。我真的不明白你的问题。结束-开始的结果是什么?
如果JVM了解数组中的最终结果,那么它可能会优化forloops,而最小的数组将更容易计算,因为它只需要1000个赋值,而另一个需要10000倍以上的复杂度。
就渐近复杂性而言(例如,大O表示法),它们具有相同的运行时间
数据本地化
暂时忽略任何优化
b
更大,因此更可能被拆分为多个(或多个)。因此,第一个可能更快
这里的差异可能相当小,除非不是所有这些页面都适合RAM,并且需要写入磁盘(这在这里不太可能,因为b
只有10000000*4=40000000字节=38MB)
优化
第一种方法涉及“执行a[i]+
10000000次”(对于固定的i
),理论上可以很容易地由优化器转换为a[i]+=10000000
类似的优化可以针对b
,但仅针对b[i]+=1000
,这仍然需要运行10000000次
优化器可以自由地做这件事,也可以不做这件事。据我所知,Java语言规范没有太多关于应该和不应该优化的内容,只要它不改变最终结果
作为一个极端的结果,优化器可以在理论上看到,在循环之后,你没有使用a
或b
做任何事情,从而消除两个循环。我将在这里给出我的答案,理论上它们完全相同,但实际上会有一个很小但可以忽略的差异。太小了,根本不重要,事实上
基本思想是数组b
如何存储在内存中。因为它要大得多,根据您的平台/实现,它可能存储在块中,也称为非连续。这可能是因为1000万整数的数组是4000万字节=40 MB
编辑:我分别得到572和593。第一个将更模糊。因为第一个a
和I
单元格的初始化次数要少得多。第一个循环在我的系统上运行得更快(中位数:333 ms vs.596 ms)
(编辑:我在第一次响应中错误地假设了数组访问的数量,请参见注释)
与随机访问或递减(索引--)访问相比,对同一数组的后续增量(索引++)访问似乎要更快。我假设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]++;
}
}
//增量数组索引
对于(int i=0;i<1000;i++){
对于(int j=0;j<10000000;j++){
a[i]++;
}
}
//递减数组索引
对于(int i=1000-1;i>=0;i--){
对于(int j=10000000-1;j>=0;j--){
a[i]++;
}
}
递增:349ms,递减:485ms。
如果没有边界检查,递减循环通常更快,尤其是在旧处理器上(与零相比)
如果我的假设是正确的,这将进行1000次优化边界检查,而不是10000000次检查,因此第一种方法更快
顺便说一下,在进行基准测试时:
- 进行多轮和比较平均值/中等值而不是第一个样本
- 在Java中:给你的基准测试一个预热阶段(在测量之前执行几次)。在第一次运行时,必须加载类,并且在热点VM特性启动并执行本机编译之前,可能会解释代码
- 使用
System.nanoTime()
测量时间增量。这将提供更精确的时间戳System。currentTimeMillis()
没有那么精确(取决于VM),通常以十几毫秒或更长的时间“跳跃”,使结果的波动性比实际波动性大几倍。顺便说一句:1毫秒=1'000'000纳秒
现代建筑是复杂的,所以回答这类问题绝非易事
运行时间可以相同,也可以更快
这种情况下要考虑的主要是内存访问和优化。
一个好的优化器会意识到这些值永远不会被读取,因此循环可以被读取