首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >对于相互排斥的条件,多个"if“语句与"if else if”之间是否存在性能差异?

对于相互排斥的条件,多个"if“语句与"if else if”之间是否存在性能差异?
EN

Stack Overflow用户
提问于 2019-05-02 22:46:00
回答 3查看 3.3K关注 0票数 11

我很好奇Java是如何对具有相互排斥条件的多个"if“语句进行优化的,但我自己还没有分析它的知识。这个问题基本上是这个问题Performance difference of "if if" vs "if else if"的Java版本。

对于if语句和return语句,我看到了这样的回答,但这个问题是针对具有相互排斥条件但不返回的if语句的。

1.多个if语句

代码语言:javascript
复制
if (x == 0) doSomething();
if (x == 2) doSomething();
if (x == 5) doSomething();

2.链式If-else语句

代码语言:javascript
复制
if (x == 0) doSomething();
else if (x == 2) doSomething();
else if (x == 5) doSomething();

问题

#1和#2执行相同的后编译吗?

(如果是这样的话,Java可以优化条件的复杂程度吗?)

EN

回答 3

Stack Overflow用户

发布于 2019-05-03 00:50:59

没有什么比一个好的老式时间测试更好的了:

代码语言:javascript
复制
long total = 0;
long startTime;
long endTime;

for (int j = 0; j < 10; j++) {
    startTime = System.currentTimeMillis();

    for (int i = 0; i < 100000000; i++) {
        if (i % 3 == 0) total += 1;
        if (i % 3 == 1) total += 2;
        if (i % 3 == 2) total += 3;
    }

    endTime = System.currentTimeMillis();
    System.out.println("If only: " + (endTime - startTime));

    startTime = System.currentTimeMillis();

    for (int i = 0; i < 100000000; i++) {
        if (i % 3 == 0) total += 1;
        else if (i % 3 == 1) total += 2;
        else if (i % 3 == 2) total += 3;
    }

    endTime = System.currentTimeMillis();
    System.out.println("If-else: " + (endTime - startTime));
}
System.out.println(total);

(为了防止编译器移除整个循环,必须使用“总量”值!)

产出:

代码语言:javascript
复制
If only: 215
If-else: 137
If only: 214
If-else: 121
If only: 210
If-else: 120
If only: 211
If-else: 120
If only: 211
If-else: 121
If only: 210
If-else: 121
If only: 210
If-else: 121
If only: 211
If-else: 120
If only: 211
If-else: 120
If only: 211
If-else: 121
3999999980

正如我们所看到的,if- are块确实运行得更快,即使if-条件显然是相互排斥的。因为这两个循环所用的时间不同,所以每个循环的编译代码必须是不同的。显然编译器没有对此进行优化。JIT或CPU分支预测也不完全。仍然有很大的不同。

我的建议:尽可能使用If-否则。

编辑:--我也尝试了交换两个循环,得到了相同的结果。如果-否则如果快得多的话。

编辑2:,我在整个测试中添加了一个for-循环,以消除初始化或热身方面的任何差异。结果是一样的。

票数 3
EN

Stack Overflow用户

发布于 2019-05-03 11:32:43

好吧,只有适当的JMH测试才能证明某一种方法有多快。当然,如果你真的想知道为什么数字是这样的话,你也应该理解底层的机器代码。我把这个问题留给你们,只是在这个测试中给出数字,只稍微给你们展示一些细节。

代码语言:javascript
复制
package com.so;

import java.util.concurrent.TimeUnit;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

@Warmup(iterations = 5)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Measurement(iterations = 2, time = 2, timeUnit = TimeUnit.SECONDS)
public class IfElseCompare {

    public static void main(String[] args) throws Exception {
        Options opt = new OptionsBuilder()
            .include(IfElseCompare.class.getName())
            .jvmArgs("-ea")
            .build();

        new Runner(opt).run();
    }

    private int resolveValueMultipleIfs(IfElseExecutionPlan plan) {

        int x = -1;

        if (plan.value() == 0) {
            x = 0;
        }

        if (plan.value() == 1) {
            x = 1;
        }

        if (plan.value() == 2) {
            x = 2;
        }

        assert x != -1;
        return x;
    }

    private int resolveValueIfElse(IfElseExecutionPlan plan) {
        int x = -1;
        if (plan.value() == 0) {
            x = 0;
        } else if (plan.value() == 1) {
            x = 1;
        } else if (plan.value() == 2) {
            x = 2;
        }

        assert x != -1;
        return x;
    }

    @Benchmark
    @BenchmarkMode(Mode.AverageTime)
    @Fork(1)
    public int multipleIf(IfElseExecutionPlan plan) {
        return resolveValueMultipleIfs(plan);
    }

    @Benchmark
    @BenchmarkMode(Mode.AverageTime)
    @Fork(1)
    public int ifElse(IfElseExecutionPlan plan) {
        return resolveValueIfElse(plan);
    }

    @Benchmark
    @BenchmarkMode(Mode.AverageTime)
    @Fork(value = 1, jvmArgsAppend = "-Xint")
    public int multipleIfsfNoJIT(IfElseExecutionPlan plan) {
        return resolveValueMultipleIfs(plan);
    }

    @Benchmark
    @BenchmarkMode(Mode.AverageTime)
    @Fork(value = 1, jvmArgsAppend = "-Xint")
    public int ifElseNoJIT(IfElseExecutionPlan plan) {
        return resolveValueIfElse(plan);
    }

    @Benchmark
    @BenchmarkMode(Mode.AverageTime)
    @Fork(value = 1, jvmArgsAppend = "-XX:-TieredCompilation")
    public int multipleIfsC2Only(IfElseExecutionPlan plan) {
        return resolveValueMultipleIfs(plan);
    }

    @Benchmark
    @BenchmarkMode(Mode.AverageTime)
    @Fork(value = 1, jvmArgsAppend = "-XX:-TieredCompilation")
    public int ifElseC2Only(IfElseExecutionPlan plan) {
        return resolveValueIfElse(plan);
    }

    @Benchmark
    @BenchmarkMode(Mode.AverageTime)
    @Fork(value = 1, jvmArgsAppend = "-XX:TieredStopAtLevel=1")
    public int multipleIfsC1Only(IfElseExecutionPlan plan) {
        return resolveValueMultipleIfs(plan);
    }

    @Benchmark
    @BenchmarkMode(Mode.AverageTime)
    @Fork(value = 1, jvmArgsAppend = "-XX:TieredStopAtLevel=1")
    public int ifElseC1Only(IfElseExecutionPlan plan) {
        return resolveValueIfElse(plan);
    }

    @Benchmark
    @BenchmarkMode(Mode.AverageTime)
    @Fork(value = 1,
        jvmArgsAppend = {
            "-XX:+UnlockExperimentalVMOptions",
            "-XX:+EagerJVMCI",
            "-Dgraal.ShowConfiguration=info",
            "-XX:+UseJVMCICompiler",
            "-XX:+EnableJVMCI"
        })
    public int multipleIfsGraalVM(IfElseExecutionPlan plan) {
        return resolveValueMultipleIfs(plan);
    }

    @Benchmark
    @BenchmarkMode(Mode.AverageTime)
    @Fork(value = 1,
        jvmArgsAppend = {
            "-XX:+UnlockExperimentalVMOptions",
            "-XX:+EagerJVMCI",
            "-Dgraal.ShowConfiguration=info",
            "-XX:+UseJVMCICompiler",
            "-XX:+EnableJVMCI"
        })
    public int ifElseGraalVM(IfElseExecutionPlan plan) {
        return resolveValueIfElse(plan);
    }
}

以下是研究结果:

代码语言:javascript
复制
IfElseCompare.ifElse              avgt    2    2.826          ns/op
IfElseCompare.multipleIf          avgt    2    3.061          ns/op

IfElseCompare.ifElseC1Only        avgt    2    3.927          ns/op
IfElseCompare.multipleIfsC1Only   avgt    2    4.397          ns/op

IfElseCompare.ifElseC2Only        avgt    2    2.507          ns/op
IfElseCompare.multipleIfsC2Only   avgt    2    2.428          ns/op

IfElseCompare.ifElseGraalVM       avgt    2    2.587          ns/op
IfElseCompare.multipleIfsGraalVM  avgt    2    2.854          ns/op

IfElseCompare.ifElseNoJIT         avgt    2  232.418          ns/op   
IfElseCompare.multipleIfsfNoJIT   avgt    2  303.371          ns/op

如果使用多个if条件解压缩版本:

代码语言:javascript
复制
  0x000000010cf8542c: test   %esi,%esi
  0x000000010cf8542e: je     0x000000010cf8544f             ;*ifne {reexecute=0 rethrow=0 return_oop=0}
                                                            ; - com.so.IfElseCompare::resolveValueMultipleIfs@3 (line 21)

  0x000000010cf85430: cmp    $0x1,%esi
  0x000000010cf85433: je     0x000000010cf8545e             ;*if_icmpne {reexecute=0 rethrow=0 return_oop=0}
                                                            ; - com.so.IfElseCompare::resolveValueMultipleIfs@10 (line 25)

  0x000000010cf85435: cmp    $0x2,%esi
  0x000000010cf85438: je     0x000000010cf8546e             ;*if_icmpne {reexecute=0 rethrow=0 return_oop=0}
                                                            ; - com.so.IfElseCompare::resolveValueMultipleIfs@17 (line 29)

一系列的cmp/je -比较和跳跃,如果相等,嗯,非常期待。

if/else的反编译代码是相同的(我将让您自己看一下);生成的ASM代码使用(java-12):

代码语言:javascript
复制
java -XX:+UnlockDiagnosticVMOptions  
     -XX:CICompilerCount=2 
     -XX:-TieredCompilation  
     "-XX:CompileCommand=print,com/so/IfElseCompare.resolveValueMultipleIfs"  
     com.so.IfElseCompare
票数 2
EN

Stack Overflow用户

发布于 2019-05-03 01:15:26

让我告诉您条件运算符"if()“是如何工作的。当您编写if()语句时,它检查这些"()“中提供的条件的真实性。如果条件失败,则编译器将查找替换语句或代码块,在if()条件失败时可以使用这些语句或代码块。现在,对于这个替代内容,我们使用“block”块。

根据你的问题,答案很容易理解。两种方式都有很大的不同。

1)。多个If语句

代码语言:javascript
复制
if (x == 0) doSomething();
if (x == 2) doSomething();
if (x == 5) doSomething();

在上面的代码中,无论是否满足任何条件,编译器都将解析所有if语句。因为它们是分开使用的,没有任何替代部件。

2)。链式If-else语句

代码语言:javascript
复制
if (x == 0) doSomething();
else if (x == 2) doSomething();
else if (x == 5) doSomething();

现在,在上面的代码中,有一个主要条件检查器(x==0),如果失败了,那么还有其他选择,所以编译器将检查它们,直到找到满意的解决方案。

性能问题

在第一种情况下,编译器必须检查每个条件,因为它们都是独立的,这可能需要更多的时间。但是在第二种情况下,只有在if()语句不能满足条件时,它才会编译"else“部分。因此,是的,在性能方面,它们之间可能会有一些差别。

希望能帮上忙。

票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55961168

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档