Java 一个循环中有两个操作,而两个循环执行相同的操作,每个循环一个
这个问题和这个相同 但在我的例子中,我使用Java 我有两个循环,运行了十亿次Java 一个循环中有两个操作,而两个循环执行相同的操作,每个循环一个,java,performance,loops,Java,Performance,Loops,这个问题和这个相同 但在我的例子中,我使用Java 我有两个循环,运行了十亿次 int a = 188, b = 144, aMax = 0, bMax = 0; for (int i = 0; i < 1000000000; i++) { int t = a ^ i; if (t > aMax) aMax = t; } for (int i = 0; i < 1000000000; i++) { int t = b ^ i; if
int a = 188, b = 144, aMax = 0, bMax = 0;
for (int i = 0; i < 1000000000; i++) {
int t = a ^ i;
if (t > aMax)
aMax = t;
}
for (int i = 0; i < 1000000000; i++) {
int t = b ^ i;
if (t > bMax)
bMax = t;
}
inta=188,b=144,aMax=0,bMax=0;
对于(int i=0;i<100000000;i++){
int t=a^i;
如果(t>aMax)
aMax=t;
}
对于(int i=0;i<100000000;i++){
int t=b^i;
如果(t>bMax)
bMax=t;
}
在我的机器上运行这两个循环所需的时间约为4秒。当我将这两个循环融合成一个循环并在该循环中执行所有操作时,它将在2秒内运行。正如您所见,琐碎的操作构成了循环内容,因此需要恒定的时间
我的问题是,我从哪里获得了这种性能改进
我猜想,在两个单独的循环中,性能受到影响的唯一可能的地方是,它增加I并检查I是否小于10000000002倍,而如果我将两个循环融合在一起,则仅检查10亿倍。里面还有什么事吗
谢谢 在我看来,在单循环的情况下,JIT可能会选择进行循环展开,因此性能稍好一些您是否使用了-server?如果没有,您应该这样做-客户机JIT不是一个可预测的好方法,也不是一个好方法。如果您真的对到底发生了什么感兴趣,那么可以使用UnlockDiagnostic+LogCompilation检查在这两种情况下(一直到生成的程序集)应用了哪些优化 另外,从您提供的代码中,我看不出您是否进行了预热,是否为同一个JVM运行了一次或多次测试,是否进行了几次(不同的JVM)。无论你考虑的是最佳时间、平均时间还是平均时间,你会剔除异常值吗 下面是一个关于编写Java微基准测试的好链接:
编辑:还有一个微基准标记提示,注意堆栈上的替换:如果不运行预热阶段,可能会优化和编译第一个循环,但不会编译第二个循环,而当合并它们时,会编译整个合并循环。另外,使用
服务器
选项和您的代码,大多数都会得到优化,因为您不使用结果
我已经运行了下面的测试,将每个循环以及合并的循环放入它们自己的方法中,并预热JVM以确保所有内容都得到编译
结果(JVM选项:-server-XX:+printcomilation
):
- 回路1=500ms
- 回路2=900毫秒
- 合并回路=1300毫秒
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 3; i++) {
loop1();
loop2();
loopBoth();
}
long start = System.nanoTime();
loop1();
long end = System.nanoTime();
System.out.println((end - start) / 1000000);
start = System.nanoTime();
loop2();
end = System.nanoTime();
System.out.println((end - start) / 1000000);
start = System.nanoTime();
loopBoth();
end = System.nanoTime();
System.out.println((end - start) / 1000000);
}
public static void loop1() {
int a = 188, aMax = 0;
for (int i = 0; i < 1000000000; i++) {
int t = a ^ i;
if (t > aMax) {
aMax = t;
}
}
System.out.println(aMax);
}
public static void loop2() {
int b = 144, bMax = 0;
for (int i = 0; i < 1000000000; i++) {
int t = b ^ i;
if (t > bMax) {
bMax = t;
}
}
System.out.println(bMax);
}
public static void loopBoth() {
int a = 188, b = 144, aMax = 0, bMax = 0;
for (int i = 0; i < 1000000000; i++) {
int t = a ^ i;
if (t > aMax) {
aMax = t;
}
int u = b ^ i;
if (u > bMax) {
bMax = u;
}
}
System.out.println(aMax);
System.out.println(bMax);
}
publicstaticvoidmain(String[]args)抛出InterruptedException{
对于(int i=0;i<3;i++){
loop1();
loop2();
循环二者();
}
长启动=System.nanoTime();
loop1();
long end=System.nanoTime();
System.out.println((结束-开始)/1000000);
start=System.nanoTime();
loop2();
end=System.nanoTime();
System.out.println((结束-开始)/1000000);
start=System.nanoTime();
循环二者();
end=System.nanoTime();
System.out.println((结束-开始)/1000000);
}
公共静态void loop1(){
int a=188,aMax=0;
对于(int i=0;i<100000000;i++){
int t=a^i;
如果(t>aMax){
aMax=t;
}
}
System.out.println(aMax);
}
公共静态void loop2(){
int b=144,bMax=0;
对于(int i=0;i<100000000;i++){
int t=b^i;
如果(t>bMax){
bMax=t;
}
}
系统输出打印项数(bMax);
}
公共静态void loopBoth(){
int a=188,b=144,aMax=0,bMax=0;
对于(int i=0;i<100000000;i++){
int t=a^i;
如果(t>aMax){
aMax=t;
}
int u=b^i;
如果(u>bMax){
bMax=u;
}
}
System.out.println(aMax);
系统输出打印项数(bMax);
}
简而言之,CPU可以并行执行合并循环中的指令,使性能加倍
第二个回路也可能没有得到有效优化。这是因为第一个循环将触发要编译的整个方法,而第二个循环将在没有任何可能打乱第二个循环计时的度量的情况下编译。我会将每个循环放在一个单独的方法中,以确保情况并非如此
CPU可以并行执行大量独立操作()。它尝试并行执行的一个操作是分支,使用分支预测,但如果它几乎每次都不使用相同的分支
我怀疑在循环展开的情况下,您的循环看起来更像以下内容(在本例中可能更像循环展开)
for(int i=0;i<100000000;i+=2){
//第一个区块几乎并行运行
int t1=a^i;
int t2=b^i;
int t3=a^(i+1);
int t4=b^(i+1);
//这个街区并行运行
如果(t1>aMax)aMax=t1;
如果(t2>bMax)bMax=t2;
如果(t3>aMax)aMax=t3;
如果(t4>bMax)bMax=t4;
}
我认为这是因为你在做1B次增量、1B次比较和1B次跳跃……移动int t的效果如何代码>在循环外声明,并仅执行赋值t=a^i代码>或t=b^i代码>在循环内部?@barrowc它将没有任何效果。JIT的第一个阶段之一是将AST图转换为单赋值表示,这将撤销此别名,以便更好地进行生存期分析。FWIW,它可以走任何一条路:感谢micro benchmarks上的链接。我的代码中没有循环预热。结果2秒/4秒来自同一JVM,这是许多试验的平均值。我也没有使用-server标志。
for (int i = 0; i < 1000000000; i += 2) {
// this first block is run almost in parallel
int t1 = a ^ i;
int t2 = b ^ i;
int t3 = a ^ (i+1);
int t4 = b ^ (i+1);
// this block run in parallel
if (t1 > aMax) aMax = t1;
if (t2 > bMax) bMax = t2;
if (t3 > aMax) aMax = t3;
if (t4 > bMax) bMax = t4;
}