Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/391.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
java编译器有多聪明?关于枚举方法的几个问题_Java_Enums_Compiler Construction - Fatal编程技术网

java编译器有多聪明?关于枚举方法的几个问题

java编译器有多聪明?关于枚举方法的几个问题,java,enums,compiler-construction,Java,Enums,Compiler Construction,考虑这种情况: 样式1: 样式2: static enum Style2{ FIRE_BALL,ICE_BALL,FIRE_ARROW,ICE_ARROW; public boolean isCold(){ //return this.toString().contains("ICE")?true:false; //sorry return this.toString().contains("ICE"); } } 现在,我只想知道天气是

考虑这种情况:

样式1:

样式2:

static enum Style2{
    FIRE_BALL,ICE_BALL,FIRE_ARROW,ICE_ARROW;
    public boolean isCold(){
        //return this.toString().contains("ICE")?true:false; //sorry
        return this.toString().contains("ICE");
    }
}
现在,我只想知道天气是否冷。所以我要问:

编译器能否知道最终结果和常量折叠样式2?

如果不是,Style1应该明显更快,但更详细。假设这是一个更复杂的情况,并且有更多的组合,例如BIG_FIRE_SLOW_BALL和isFast()、isBig()、Style1将以代码块结束


所以我用jmh和jUnit做了一些测试:

1.jmh:

@Benchmark
public boolean testStyle1() {
    return Style1.values()[ThreadLocalRandom.current().nextInt(4)].isCold();
}

@Benchmark
public boolean testStyle2() {
    return Style2.values()[ThreadLocalRandom.current().nextInt(4)].isCold();
}
设置时:

            .warmupIterations(10)
            .measurementIterations(10)
            .threads(8)

Benchmark             Mode  Cnt   Score   Error  Units
EnumTest1.testStyle1  avgt   10  34.057 ± 0.101  ns/op
EnumTest1.testStyle2  avgt   10  36.196 ± 0.453  ns/op
那么,将线程数设置为1

            .threads(1)
Benchmark             Mode  Cnt   Score    Error  Units
EnumTest1.testStyle1  avgt   10  34.306 ± 11.692  ns/op
EnumTest1.testStyle2  avgt   10  44.279 ± 11.103  ns/op
因此,编译器似乎无法优化样式2。

2、使用jUnit:

private static final int LOOP_TIMES = 100000000;
private static final Random random1=new Random(47);
private static final Random random2=new Random(47);

@Test
public void testStyle1() {
    int cnt = 0;
    for (int i = 0; i < LOOP_TIMES; i++) {
        if(Style1.values()[random1.nextInt(4)].isCold()){
            cnt++;
        }
    }
    System.out.println(cnt);
}

@Test
public void testStyle2() {
    int cnt = 0;
    for (int i = 0; i < LOOP_TIMES; i++) {
        if(Style2.values()[random2.nextInt(4)].isCold()){
            cnt++;
        }
    }
    System.out.println(cnt);
}
因此,Style1可能会更快


但是为什么两个结果非常接近,特别是当我同时使用jmh进行测试时?或者我们究竟应该如何处理这一问题?

也许给Style1一些字段来存储每个结果可以减少冗余。但我还是觉得不太满意。希望你们中的一些人能告诉我更多


非常感谢你们@安迪举了一个很好的例子,我在这里补充:

enum Style4{
    FIRE_BALL,
    ICE_BALL,
    FIRE_ARROW,
    ICE_ARROW;

    private boolean cold;

    private Style4(){
        this.cold = this.toString().contains("ICE");
    }

    public boolean isCold(){
        return cold;
    }
}

第四种样式在工作时没有提到true或false。

如果使用构造函数,可以在某种程度上改进样式1的详细性。这将是快速的,而且(在我看来)更容易阅读

enum Style1{
    FIRE_BALL(false),
    ICE_BALL(true),
    FIRE_ARROW(false),
    ICE_ARROW(true);

    private final cold;

    private Style1(boolean cold){
        this.cold = cold;
    }

    public boolean isCold(){
        return cold;
    }
}

请注意,这三种样式都不可能成为代码中的热点。更重要的是编写更易于阅读的代码,并在以后根据需要对性能进行调整。

如果使用构造函数,可以在某种程度上改进style1的冗长性。这将是快速的,而且(在我看来)更容易阅读

enum Style1{
    FIRE_BALL(false),
    ICE_BALL(true),
    FIRE_ARROW(false),
    ICE_ARROW(true);

    private final cold;

    private Style1(boolean cold){
        this.cold = cold;
    }

    public boolean isCold(){
        return cold;
    }
}
请注意,这三种样式都不可能成为代码中的热点。更重要的是编写更易于阅读的代码,并在以后需要时对性能进行调整

编译器能否知道最终结果和常量折叠样式2

没有

但是为什么两个结果很接近,特别是当我同时使用jmh进行测试时?或者我们究竟应该如何处理

正如@JB Nizet所说,基准测试方法中的大部分时间都花在查找
ThreadLocalRandom
实例和生成下一个随机数上

我不确定这一点,但我认为
values()
调用必须在每次调用时创建一个新数组。这在一定程度上可以解释为什么你在时间安排上会有很多变化。(JLS对此没有具体说明,但如果
values()
没有每次返回新数组,则不受信任的代码可以更新数组并导致受信任的代码出现问题。)

编译器能否知道最终结果和常量折叠样式2

没有

但是为什么两个结果很接近,特别是当我同时使用jmh进行测试时?或者我们究竟应该如何处理

正如@JB Nizet所说,基准测试方法中的大部分时间都花在查找
ThreadLocalRandom
实例和生成下一个随机数上

我不确定这一点,但我认为
values()
调用必须在每次调用时创建一个新数组。这在一定程度上可以解释为什么你在时间安排上会有很多变化。(JLS对此没有具体说明,但如果
values()
没有每次返回新数组,则不受信任的代码可以更新数组并导致受信任的代码出现问题。)

但是为什么两个结果很接近,特别是当我同时使用jmh进行测试时?或者我们究竟应该如何处理

您真正关心的代码是这样一种方法

return true;
但是,您要添加到这一点

if(Style2.values()[random2.nextInt(4)].isCold()){
        cnt++;
    }
方法
Style2.value()
是一个昂贵的方法,它返回类型
Style2

方法
nextInt(4)
相当便宜,但包括相对昂贵的
%

您还有一个
if
条件,这取决于您拥有的Java版本,因为它会导致分支未命中。较新版本的Java使用
CMOV
指令,这一指令处理得更好

简言之,结果与您花费大部分时间做的工作基本相同,也就是说,您认为您正在测试的代码并不相同

但是为什么两个结果很接近,特别是当我同时使用jmh进行测试时?或者我们究竟应该如何处理

您真正关心的代码是这样一种方法

return true;
但是,您要添加到这一点

if(Style2.values()[random2.nextInt(4)].isCold()){
        cnt++;
    }
方法
Style2.value()
是一个昂贵的方法,它返回类型
Style2

方法
nextInt(4)
相当便宜,但包括相对昂贵的
%

您还有一个
if
条件,这取决于您拥有的Java版本,因为它会导致分支未命中。较新版本的Java使用
CMOV
指令,这一指令处理得更好


简言之,结果与您花费大部分时间做的工作基本相同,也就是说,您认为您正在测试的代码中没有测试结果。

您的基准测试主要测试生成随机数的时间。这两种方法都足够快,在实际应用程序中,这永远不会导致任何性能问题。因此,请使用您认为最具可读性和可维护性的。您可以添加第三种方法:在每个枚举的构造函数中初始化一个布尔实例变量,并由一个通用的isCold()方法返回。我可以建议吗?第四种方法,基于第三种方法:不要传递构造函数参数,只需在ctor中计算
toString().contains(“ICE”)
,然后分配给一个字段。Nit:
this.toString().contains(“ICE”)?真:假
为e