public static void main(String[] args) {
final Random r = new Random(0);
boolean b = false;
boolean decision;
long startTime;
long endTime;
startTime = System.currentTimeMillis();
for (long i = 0; i < 1000000000; i++) {
decision = r.nextDouble() > 0.1; // Will be true MOST of the time.
if (decision) {
// if (!b) {
b = true;
// }
}
}
endTime = System.currentTimeMillis();
System.err.println(endTime - startTime);
System.err.println(b);
System.exit(0);
}
With bluntly writing (ms):
18139
18140
18196
(18220)
(18181)
----------
Average of 3: 18158.333333333333333333333333333
Average of 5: 18175.2
With checking before writing (ms):
18097
18109
18115
(18129)
(18133)
----------
Average of 3: 18107
Average of 5: 18116.6
With checking, it only takes this % (3 samples): 99.71730151445617255621844882974
With checking, it only takes this % (5 samples): 99.677582640080989480170782164708
# 1 楼答案
有几件事会起作用,对实际性能的最终影响是您需要根据您的用例来衡量的。我认为这是一种你经常发现的方法:
分支预测-如果var几乎总是错误的,这就是代码所建议的,那么分支预测几乎总是正确的。如果字段经常更改,则这将成为一个经常预测失误的分支,并且成本高昂
读取未命中-如果var大部分是读取的(并且读取了很多),那么避免无故更改可以帮助您的软件不会使它所在的缓存线无效。如果您对它进行写入,那么读取它(以及同一缓存线上的任何内容)的每个其他内核都需要获得一个新的副本,该副本将经历读未命中。这意味着,为了使读取具有更一致的速度,上述方法可能值得降低速度
写入成本与读取成本-如果var是易变的,那么它的写入是一个负载存储障碍,这相当昂贵。相比之下,读取volatile(负载屏障)是相当便宜的(对于经常使用且几乎没有更改的值的缓存命中)。相比之下,这可以使分支非常便宜
这是一个优化的人,例子可以在JDK(Irc)中找到,我想你有理由考虑它。
# 2 楼答案
第一个代码包含一个比较,因此编译器可能会生成如下所示的java字节码:
对于第二个代码生成的字节码较短,因为缺少比较:
在第一个示例中,虚拟机执行8个命令所需的时间比在第二个示例中执行4个命令所需的时间多。虽然这个差别不应该太大,但第二个代码更清楚
将代码放在一个简单的main方法中并编译该类。然后运行命令提示符并更改为
java/bin
目录。要反汇编类调用javap -c path/to/YourClass.class >> path/to/bytecode.txt
。字节码。txt将包含类的java字节码# 3 楼答案
“速度差异”(如果有的话)将完全取决于JVM。任何优秀的编译器都应该能够优化测试,在这一点上,两者是相同的
例外:如果
var
被声明为volatile
,则条件版本将始终较慢在任何情况下,如果性能至关重要,最好的选择是在预期条件下(机器、系统、JVM、典型负载等)测量性能
# 4 楼答案
这是另一个廉价的“基准”(几乎不配使用这个术语)。对我来说,对于OP的问题,猴子扳手的基准测试的结果并不是很清楚,所以我想我也应该把它放在这里
结果(在这些条件下,并且仅在很少的测试中):在编写局部布尔值之前检查它比在没有必要的情况下经常编写要好
通过检查,大约需要99.7%的书写时间。如果写操作不必要地发生,通常。在这个糟糕的“基准”中,也就是说
# 5 楼答案
我在这场比赛中迟到了,但我写这个测试课是为了回答一个类似的问题
我的结果: 每个循环的平均时间/十亿(毫秒)注释时间