Java 有两种方法来检查两个 boolean 值是否不同。您可以将它们与 != 或 ^ (xor) 进行比较。当然,这两个运算符在所有情况下都会产生相同的结果。尽管如此,将它们都包括在内还是有意义的,例如在 What's the difference between XOR and NOT-EQUAL-TO? 中讨论过的。 .对于开发人员来说,根据上下文更喜欢一个甚至是有意义的——有时“这些 boolean 值中的一个是真的”读起来更好,而其他时候“这两个 boolean 值是否不同”更好地传达了意图。所以,也许使用哪一个应该是品味和风格的问题。
令我吃惊的是 javac 并没有对它们一视同仁!考虑这个类:
class Test {
public boolean xor(boolean p, boolean q) {
return p ^ q;
}
public boolean inequal(boolean p, boolean q) {
return p != q;
}
}
显然,这两种方法具有相同的可见行为。但是它们有不同的字节码:
$ javap -c Test
Compiled from "Test.java"
class Test {
Test();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public boolean xor(boolean, boolean);
Code:
0: iload_1
1: iload_2
2: ixor
3: ireturn
public boolean inequal(boolean, boolean);
Code:
0: iload_1
1: iload_2
2: if_icmpeq 9
5: iconst_1
6: goto 10
9: iconst_0
10: ireturn
}
如果非要我猜的话,我会说 xor 的性能更好,因为它只返回比较的结果;增加跳跃和额外的负荷似乎是在浪费工作。但是我没有猜测,而是使用 Clojure 的“criterium”基准测试工具对这两种方法的数十亿次调用进行了基准测试。它已经足够接近了,虽然看起来 xor 快了一点,但我在统计方面还不够好,无法判断结果是否显着:
user=> (let [t (Test.)] (bench (.xor t true false)))
Evaluation count : 4681301040 in 60 samples of 78021684 calls.
Execution time mean : 4.273428 ns
Execution time std-deviation : 0.168423 ns
Execution time lower quantile : 4.044192 ns ( 2.5%)
Execution time upper quantile : 4.649796 ns (97.5%)
Overhead used : 8.723577 ns
Found 2 outliers in 60 samples (3.3333 %)
low-severe 2 (3.3333 %)
Variance from outliers : 25.4745 % Variance is moderately inflated by outliers
user=> (let [t (Test.)] (bench (.inequal t true false)))
Evaluation count : 4570766220 in 60 samples of 76179437 calls.
Execution time mean : 4.492847 ns
Execution time std-deviation : 0.162946 ns
Execution time lower quantile : 4.282077 ns ( 2.5%)
Execution time upper quantile : 4.813433 ns (97.5%)
Overhead used : 8.723577 ns
Found 2 outliers in 60 samples (3.3333 %)
low-severe 2 (3.3333 %)
Variance from outliers : 22.2554 % Variance is moderately inflated by outliers
在性能方面,是否有某种理由更喜欢编写一个而不是另一个?1?在某些情况下,它们的实现差异使一个比另一个更合适?或者,有谁知道为什么 javac 以如此不同的方式实现这两个相同的操作?
1 当然,我不会鲁莽地使用这些信息进行微优化。我只是好奇这一切是如何运作的。
最佳答案
好吧,我将提供 CPU 如何很快翻译并更新帖子,但与此同时,您看到的差异太小而无法关心。
Java 中的字节码并不表示方法执行的速度(或速度)有多快,有两种 JIT 编译器一旦足够热,就会使该方法看起来完全不同。众所周知,javac 在编译 代码后只做很少的优化,真正的优化来自 JIT。
我已经使用 JMH 进行了一些测试,为此仅使用 C1 编译器或将 C2 替换为 GraalVM 或根本没有 JIT ...(后面有很多测试代码,您可以跳过它,只看结果,这是使用 jdk-12 顺便说一句).此代码使用 JMH - 在微型基准测试的 Java 世界中使用的事实上的工具(众所周知,如果手动完成,它们容易出错)。
@Warmup(iterations = 10)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Measurement(iterations = 2, time = 2, timeUnit = TimeUnit.SECONDS)
public class BooleanCompare {
public static void main(String[] args) throws Exception {
Options opt = new OptionsBuilder()
.include(BooleanCompare.class.getName())
.build();
new Runner(opt).run();
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@Fork(1)
public boolean xor(BooleanExecutionPlan plan) {
return plan.booleans()[0] ^ plan.booleans()[1];
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@Fork(1)
public boolean plain(BooleanExecutionPlan plan) {
return plan.booleans()[0] != plan.booleans()[1];
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@Fork(value = 1, jvmArgsAppend = "-Xint")
public boolean xorNoJIT(BooleanExecutionPlan plan) {
return plan.booleans()[0] != plan.booleans()[1];
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@Fork(value = 1, jvmArgsAppend = "-Xint")
public boolean plainNoJIT(BooleanExecutionPlan plan) {
return plan.booleans()[0] != plan.booleans()[1];
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@Fork(value = 1, jvmArgsAppend = "-XX:-TieredCompilation")
public boolean xorC2Only(BooleanExecutionPlan plan) {
return plan.booleans()[0] != plan.booleans()[1];
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@Fork(value = 1, jvmArgsAppend = "-XX:-TieredCompilation")
public boolean plainC2Only(BooleanExecutionPlan plan) {
return plan.booleans()[0] != plan.booleans()[1];
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@Fork(value = 1, jvmArgsAppend = "-XX:TieredStopAtLevel=1")
public boolean xorC1Only(BooleanExecutionPlan plan) {
return plan.booleans()[0] != plan.booleans()[1];
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@Fork(value = 1, jvmArgsAppend = "-XX:TieredStopAtLevel=1")
public boolean plainC1Only(BooleanExecutionPlan plan) {
return plan.booleans()[0] != plan.booleans()[1];
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@Fork(value = 1,
jvmArgsAppend = {
"-XX:+UnlockExperimentalVMOptions",
"-XX:+EagerJVMCI",
"-Dgraal.ShowConfiguration=info",
"-XX:+UseJVMCICompiler",
"-XX:+EnableJVMCI"
})
public boolean xorGraalVM(BooleanExecutionPlan plan) {
return plan.booleans()[0] != plan.booleans()[1];
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
@Fork(value = 1,
jvmArgsAppend = {
"-XX:+UnlockExperimentalVMOptions",
"-XX:+EagerJVMCI",
"-Dgraal.ShowConfiguration=info",
"-XX:+UseJVMCICompiler",
"-XX:+EnableJVMCI"
})
public boolean plainGraalVM(BooleanExecutionPlan plan) {
return plan.booleans()[0] != plan.booleans()[1];
}
}
结果:
BooleanCompare.plain avgt 2 3.125 ns/op
BooleanCompare.xor avgt 2 2.976 ns/op
BooleanCompare.plainC1Only avgt 2 3.400 ns/op
BooleanCompare.xorC1Only avgt 2 3.379 ns/op
BooleanCompare.plainC2Only avgt 2 2.583 ns/op
BooleanCompare.xorC2Only avgt 2 2.685 ns/op
BooleanCompare.plainGraalVM avgt 2 2.980 ns/op
BooleanCompare.xorGraalVM avgt 2 3.868 ns/op
BooleanCompare.plainNoJIT avgt 2 243.348 ns/op
BooleanCompare.xorNoJIT avgt 2 201.342 ns/op
我不是一个多才多艺的人,无法阅读汇编程序,尽管我有时喜欢这样做……这里有一些有趣的东西。如果我们这样做:
C1 compiler only with !=
/*
* run many iterations of this with :
* java -XX:+UnlockDiagnosticVMOptions
* -XX:TieredStopAtLevel=1
* "-XX:CompileCommand=print,com/so/BooleanCompare.compare"
* com.so.BooleanCompare
*/
public static boolean compare(boolean left, boolean right) {
return left != right;
}
我们得到:
0x000000010d1b2bc7: push %rbp
0x000000010d1b2bc8: sub $0x30,%rsp ;*iload_0 {reexecute=0 rethrow=0 return_oop=0}
; - com.so.BooleanCompare::compare@0 (line 22)
0x000000010d1b2bcc: cmp %edx,%esi
0x000000010d1b2bce: mov $0x0,%eax
0x000000010d1b2bd3: je 0x000000010d1b2bde
0x000000010d1b2bd9: mov $0x1,%eax
0x000000010d1b2bde: and $0x1,%eax
0x000000010d1b2be1: add $0x30,%rsp
0x000000010d1b2be5: pop %rbp
对我来说,这段代码有点明显:将 0 放入 eax,compare (edx, esi) -> 如果不相等,将 1 放入 eax 。返回 eax & 1。
C1 compiler with ^:
public static boolean compare(boolean left, boolean right) {
return left ^ right;
}
# parm0: rsi = boolean
# parm1: rdx = boolean
# [sp+0x40] (sp of caller)
0x000000011326e5c0: mov %eax,-0x14000(%rsp)
0x000000011326e5c7: push %rbp
0x000000011326e5c8: sub $0x30,%rsp ;*iload_0 {reexecute=0 rethrow=0 return_oop=0}
; - com.so.BooleanCompare::compare@0 (line 22)
0x000000011326e5cc: xor %rdx,%rsi
0x000000011326e5cf: and $0x1,%esi
0x000000011326e5d2: mov %rsi,%rax
0x000000011326e5d5: add $0x30,%rsp
0x000000011326e5d9: pop %rbp
我真的不知道为什么这里需要 和 $0x1,%esi ,否则我猜这也相当简单。
But if I enable C2 compiler, things are a lot more interesting.
/**
* run with java
* -XX:+UnlockDiagnosticVMOptions
* -XX:CICompilerCount=2
* -XX:-TieredCompilation
* "-XX:CompileCommand=print,com/so/BooleanCompare.compare"
* com.so.BooleanCompare
*/
public static boolean compare(boolean left, boolean right) {
return left != right;
}
# parm0: rsi = boolean
# parm1: rdx = boolean
# [sp+0x20] (sp of caller)
0x000000011a2bbfa0: sub $0x18,%rsp
0x000000011a2bbfa7: mov %rbp,0x10(%rsp)
0x000000011a2bbfac: xor %r10d,%r10d
0x000000011a2bbfaf: mov $0x1,%eax
0x000000011a2bbfb4: cmp %edx,%esi
0x000000011a2bbfb6: cmove %r10d,%eax
0x000000011a2bbfba: add $0x10,%rsp
0x000000011a2bbfbe: pop %rbp
我什至没有看到经典的结尾 push ebp;移动 ebp,特别是; sub esp, x,而不是一些非常不寻常的东西(至少对我来说)通过:
sub $0x18,%rsp
mov %rbp,0x10(%rsp)
....
add $0x10,%rsp
pop %rbp
再次,比我多才多艺的人可以有希望地解释。否则它就像生成的 C1 的更好版本:
xor %r10d,%r10d // put zero into r10d
mov $0x1,%eax // put 1 into eax
cmp %edx,%esi // compare edx and esi
cmove %r10d,%eax // conditionally move the contents of r10d into eax
AFAIK cmp/cmove 优于 cmp/je 因为分支预测 - 这至少是我读过的......
XOR with C2 compiler:
public static boolean compare(boolean left, boolean right) {
return left ^ right;
}
0x000000010e6c9a20: sub $0x18,%rsp
0x000000010e6c9a27: mov %rbp,0x10(%rsp)
0x000000010e6c9a2c: xor %edx,%esi
0x000000010e6c9a2e: mov %esi,%eax
0x000000010e6c9a30: and $0x1,%eax
0x000000010e6c9a33: add $0x10,%rsp
0x000000010e6c9a37: pop %rbp
看起来确实与 C1 编译器生成的几乎相同。
关于java - 对于 boolean 值,(p ^ q) 和 (p != q) 之间是否存在有用的区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55598277/
给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru
我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此
这个问题在这里已经有了答案:Checktoseeifanarrayisalreadysorted?(8个答案)关闭9年前。我只是想知道是否有办法检查数组是否在增加?这是我的解决方案,但我正在寻找更漂亮的方法:n=-1@arr.flatten.each{|e|returnfalseife
我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/
请帮助我理解范围运算符...和..之间的区别,作为Ruby中使用的“触发器”。这是PragmaticProgrammersguidetoRuby中的一个示例:a=(11..20).collect{|i|(i%4==0)..(i%3==0)?i:nil}返回:[nil,12,nil,nil,nil,16,17,18,nil,20]还有:a=(11..20).collect{|i|(i%4==0)...(i%3==0)?i:nil}返回:[nil,12,13,14,15,16,17,18,nil,20] 最佳答案 触发器(又名f/f)是
我有一个包含多个键的散列和一个字符串,该字符串不包含散列中的任何键或包含一个键。h={"k1"=>"v1","k2"=>"v2","k3"=>"v3"}s="thisisanexamplestringthatmightoccurwithakeysomewhereinthestringk1(withspecialcharacterslike(^&*$#@!^&&*))"检查s是否包含h中的任何键的最佳方法是什么,如果包含,则返回它包含的键的值?例如,对于上面的h和s的例子,输出应该是v1。编辑:只有字符串是用户定义的。哈希将始终相同。 最佳答案
我需要检查DateTime是否采用有效的ISO8601格式。喜欢:#iso8601?我检查了ruby是否有特定方法,但没有找到。目前我正在使用date.iso8601==date来检查这个。有什么好的方法吗?编辑解释我的环境,并改变问题的范围。因此,我的项目将使用jsapiFullCalendar,这就是我需要iso8601字符串格式的原因。我想知道更好或正确的方法是什么,以正确的格式将日期保存在数据库中,或者让ActiveRecord完成它们的工作并在我需要时间信息时对其进行操作。 最佳答案 我不太明白你的问题。我假设您想检查
我的日期格式如下:"%d-%m-%Y"(例如,今天的日期为07-09-2015),我想看看是不是在过去的七天内。谁能推荐一种方法? 最佳答案 你可以这样做:require"date"Date.today-7 关于ruby-检查日期是否在过去7天内,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/32438063/
在Cooper的书BeginningRuby中,第166页有一个我无法重现的示例。classSongincludeComparableattr_accessor:lengthdef(other)@lengthother.lengthenddefinitialize(song_name,length)@song_name=song_name@length=lengthendenda=Song.new('Rockaroundtheclock',143)b=Song.new('BohemianRhapsody',544)c=Song.new('MinuteWaltz',60)a.betwee
这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下