Java 为什么String.equals对于不相同(但相等)的字符串对象要慢得多?
我正在深入研究isJava 为什么String.equals对于不相同(但相等)的字符串对象要慢得多?,java,string,performance,equals,Java,String,Performance,Equals,我正在深入研究isString.equals()是否真的那么糟糕,在尝试对其进行基准测试时,遇到了一些令人惊讶的结果 使用,我编写了一个简单的测试(最后是代码和pom),查看函数在1秒内可以运行多少次 Benchmark Mode Samples Score Score error Units c.s.SimpleBenchmark.testEqualsIntern thrpt 5 69
String.equals()
是否真的那么糟糕,在尝试对其进行基准测试时,遇到了一些令人惊讶的结果
使用,我编写了一个简单的测试(最后是代码和pom),查看函数在1秒内可以运行多少次
Benchmark Mode Samples Score Score error Units
c.s.SimpleBenchmark.testEqualsIntern thrpt 5 698910949.710 47115846.650 ops/s
c.s.SimpleBenchmark.testEqualsNew thrpt 5 529118.774 21164.872 ops/s
c.s.SimpleBenchmark.testIsEmpty thrpt 5 470846539.546 19922172.099 ops/s
maven的.pom(如果您希望复制它,可以自己快速设置):
4.0.0
com.shagie
长凳
1
罐子
使用JMH的字符串基准测试
3
org.openjdk.jmh
jmh核
${jmh.version}
org.openjdk.jmh
jmh发生器
${jmh.version}
假如
UTF-8
0.9.5
1.6
基准
org.apache.maven.plugins
maven编译器插件
3.1
${javac.target}
${javac.target}
${javac.target}
org.apache.maven.plugins
maven阴影插件
2.2
包裹
阴凉处
${uberjar.name}
org.openjdk.jmh.Main
maven清洁插件
2.5
maven部署插件
2.8.1
maven安装插件
2.5.1
maven jar插件
2.4
maven javadoc插件
2.9.1
maven资源插件
2.6
maven站点插件
3.3
maven源插件
2.2.1
maven surefire插件
2.17
这是自动生成的(对组和工件进行了适当的调整):
$mvn原型:生成\
-DinteractiveMode=false\
-DarchetypeGroupId=org.openjdk.jmh\
-DarchetypeArtifactId=jmh java基准原型\
-DgroupId=org.sample\
-DartifactId=测试\
-Dversion=1.0
要运行测试,请执行以下操作:
$mvn干净安装
$java-jar target/benchmarks.jar“.*SimpleBenchmark.*”—wi 5-i 5-f 1
这将是一个问题,它运行的Java版本是:
$java-version
java版本“1.6.0_65”
Java(TM)SE运行时环境(build 1.6.0_65-b14-462-11M4609)
Java HotSpot(TM)64位服务器虚拟机(构建20.65-b04-462,混合模式)
硬件(可能会受到质疑)是英特尔至强处理器上的OS X,10.9.4。解释似乎是(在第一种情况下,
intern()
'd),JVM能够测试引用相等性,这是一种直接的数值比较
相反,非引用相等(值相等)的测试必须迭代两个字符串的字符序列。你观察到的结果并不像你认为的那么重要。JIT和其他优化正在进行,性能在实践中可能会提高(因为不是每个字符串都是相等的,当它们不相等时,它可能会短路)
最后,微观基准是出了名的不可靠。但是您已经发现了一种性能优化,它是通过设计内置到JVM中的。引用相等性检查要快得多。public int testequalintern(){
public int testEqualsIntern() {
int count = 0;
String str = EMPTY;
for(int i = 0; i < ITERATIONS; i++) {
if(str.equals(EMPTY)) {
count++;
}
}
return count;
}
整数计数=0;
字符串str=空;
对于(int i=0;i
这里str.equals(EMPTY)将首先通过==检查相等性,它将返回true,因为str和EMPTY都具有相同的引用,并且都在字符串池中,并且操作将变得更快,但在
public int testEqualsNew() {
int count = 0;
String str = NEW_EMPTY;
for(int i = 0; i < ITERATIONS; i++) {
if(str.equals(EMPTY)) {
count++;
}
}
return count;
}
public int testEqualsNew(){
整数计数=0;
字符串str=NEW_空;
对于(int i=0;i
空字符串在字符串池中,而NEW_EMPTY不是池的一部分,两者都有不同的引用,因为空字符串是文字常量,而NEW_EMPTY不是。因此equals()首先尝试比较equality by==将返回false,因为两者都有不同的引用,并且它将检查内容,因此在本例中equals()将花费更多的时间。针对新字符串测试equality不会产生荒谬的性能影响。您看到的效果很简单,Hotspot在一种情况下能够优化循环,但在另一种情况下却不能 以下是OpenJDK 7(IcedTea7 2.1.7)(7u3-2.1.7-1)64位服务器的
testEqualsIntern
热点程序集转储,显示了无环结果(为TesteSempty
生成了类似的代码):
当你将一件事的1000次迭代与另一件事的1次迭代进行比较时,结果相差1000倍也就不足为奇了
在向迭代添加四个零之后,我运行了相同的测试,正如预期的那样,testequalintern
采用了equa
public int testEqualsNew() {
int count = 0;
String str = NEW_EMPTY;
for(int i = 0; i < ITERATIONS; i++) {
if(str.equals(EMPTY)) {
count++;
}
}
return count;
}
Decoding compiled method 0x00007fb360a1a0d0:
Code:
[Entry Point]
[Constants]
# {method} 'testEqualsIntern' '()I' in 'Test'
# [sp+0x20] (sp of caller)
0x00007fb360a1a200: mov 0x8(%rsi),%r10d
0x00007fb360a1a204: cmp %r10,%rax
0x00007fb360a1a207: jne 0x00007fb3609f38a0 ; {runtime_call}
0x00007fb360a1a20d: data32 xchg %ax,%ax
[Verified Entry Point]
0x00007fb360a1a210: push %rbp
0x00007fb360a1a211: sub $0x10,%rsp
0x00007fb360a1a215: nop ;*synchronization entry
; - Test::testEqualsIntern@-1 (line 8)
0x00007fb360a1a216: mov $0x3e8,%eax
0x00007fb360a1a21b: add $0x10,%rsp
0x00007fb360a1a21f: pop %rbp
0x00007fb360a1a220: test %eax,0x6232dda(%rip) # 0x00007fb366c4d000
; {poll_return}
0x00007fb360a1a226: retq
....[Hottest Region 1]..............................................................................
[0x7fb9e11acda0:0x7fb9e11acdc8] in org.sample.generated.MyBenchmark_testEqualsIntern::testEqualsIntern_thrpt_jmhLoop
; - org.sample.generated.MyBenchmark_testEqualsIntern::testEqualsIntern_thrpt_jmhLoop@19 (line 103)
0x00007fb9e11acd82: movzbl 0x94(%rdx),%r11d ;*getfield isDone
; - org.sample.generated.MyBenchmark_testEqualsIntern::testEqualsIntern_thrpt_jmhLoop@29 (line 105)
0x00007fb9e11acd8a: mov $0x2,%ebp
0x00007fb9e11acd8f: test %r11d,%r11d
0x00007fb9e11acd92: jne 0x00007fb9e11acdcc ;*ifeq
; - org.sample.generated.MyBenchmark_testEqualsIntern::testEqualsIntern_thrpt_jmhLoop@32 (line 105)
0x00007fb9e11acd94: nopl 0x0(%rax,%rax,1)
0x00007fb9e11acd9c: xchg %ax,%ax ;*aload
; - org.sample.generated.MyBenchmark_testEqualsIntern::testEqualsIntern_thrpt_jmhLoop@13 (line 103)
6.50% 3.37% 0x00007fb9e11acda0: mov 0xb0(%rdi),%r11d ;*getfield i1
; - org.openjdk.jmh.infra.Blackhole::consume@2 (line 350)
; - org.sample.generated.MyBenchmark_testEqualsIntern::testEqualsIntern_thrpt_jmhLoop@19 (line 103)
0.06% 0.05% 0x00007fb9e11acda7: mov 0xb4(%rdi),%r10d ;*getfield i2
; - org.openjdk.jmh.infra.Blackhole::consume@15 (line 350)
; - org.sample.generated.MyBenchmark_testEqualsIntern::testEqualsIntern_thrpt_jmhLoop@19 (line 103)
0.06% 0.09% 0x00007fb9e11acdae: cmp $0x3e8,%r10d
0.03% 0x00007fb9e11acdb5: je 0x00007fb9e11acdf1 ;*return
; - org.openjdk.jmh.infra.Blackhole::consume@38 (line 354)
; - org.sample.generated.MyBenchmark_testEqualsIntern::testEqualsIntern_thrpt_jmhLoop@19 (line 103)
48.85% 44.47% 0x00007fb9e11acdb7: movzbl 0x94(%rdx),%ecx ;*getfield isDone
; - org.sample.generated.MyBenchmark_testEqualsIntern::testEqualsIntern_thrpt_jmhLoop@29 (line 105)
0.33% 0.62% 0x00007fb9e11acdbe: add $0x1,%rbp ; OopMap{r9=Oop rbx=Oop rdi=Oop rdx=Oop off=226}
;*ifeq
; - org.sample.generated.MyBenchmark_testEqualsIntern::testEqualsIntern_thrpt_jmhLoop@32 (line 105)
0.03% 0.05% 0x00007fb9e11acdc2: test %eax,0x16543238(%rip) # 0x00007fb9f76f0000
; {poll}
42.31% 49.43% 0x00007fb9e11acdc8: test %ecx,%ecx
0x00007fb9e11acdca: je 0x00007fb9e11acda0 ;*aload_2
; - org.sample.generated.MyBenchmark_testEqualsIntern::testEqualsIntern_thrpt_jmhLoop@35 (line 106)
0x00007fb9e11acdcc: mov $0x7fb9f706fe40,%r10
0x00007fb9e11acdd6: callq *%r10 ;*invokestatic nanoTime
; - org.sample.generated.MyBenchmark_testEqualsIntern::testEqualsIntern_thrpt_jmhLoop@36 (line 106)
0x00007fb9e11acdd9: mov %rbp,0x10(%rbx) ;*putfield operations
; - org.sample.generated.MyBenchmark_testEqualsIntern::testEqualsIntern_thrpt_jmhLoop@51 (line 108)
0x00007fb9e11acddd: mov %rax,0x28(%rbx) ;*putfield stopTime
; - org.sample.generated.MyBenchmark_testEqualsIntern::testEqualsIntern_thrpt_jmhLoop@39 (line 106)
....................................................................................................
public void testEqualsIntern_avgt_jmhLoop(InfraControl control, RawResults result, MyBenchmark_1_jmh l_mybenchmark0_0, Blackhole_1_jmh l_blackhole1_1) throws Throwable {
long operations = 0;
long realTime = 0;
result.startTime = System.nanoTime();
do {
l_blackhole1_1.consume(l_mybenchmark0_0.testEqualsIntern());
operations++;
} while(!control.isDone);
result.stopTime = System.nanoTime();
result.realTime = realTime;
result.operations = operations;
}