TL; DR: 副作用
append显然破坏了StringConcat优化。
原始问题和更新中的分析非常好!
为了完整起见,以下是一些缺少的步骤:
- 请
-XX:+PrintInlining
同时参阅7u55和8u5。在7u55中,您将看到以下内容:
@ 16 org.sample.IntStr::inlineSideEffect (25 bytes) force inlineby CompilerOracle
@ 4 java.lang.StringBuilder::(7 bytes) inline (hot)
@ 18 java.lang.StringBuilder::append (8 bytes) already compiled
into a big method
@ 21 java.lang.StringBuilder::toString (17 bytes) inline (hot)
…在8u5中:
@ 16 org.sample.IntStr::inlineSideEffect (25 bytes) force inlineby CompilerOracle
@ 4 java.lang.StringBuilder::(7 bytes) inline (hot)
@ 3 java.lang.AbstractStringBuilder::(12 bytes) inline
(hot)
@ 1 java.lang.Object::(1 bytes) inline (hot)
@ 18 java.lang.StringBuilder::append (8 bytes) inline (hot)
@ 2 java.lang.AbstractStringBuilder::append (62 bytes) already
compiled into a big method
@ 21 java.lang.StringBuilder::toString (17 bytes) inline (hot)
@ 13 java.lang.String::(62 bytes) inline (hot)
@ 1 java.lang.Object::(1 bytes) inline (hot)
@ 55 java.util.Arrays::copyOfRange (63 bytes) inline (hot)
@ 54 java.lang.Math::min (11 bytes) (intrinsic)
@ 57 java.lang.System::arraycopy (0 bytes) (intrinsic)
您可能会注意到7u55版本较浅,并且在
StringBuilder方法之后似乎什么也没叫-这很好地表明了字符串优化已经生效。确实,如果您使用来运行7u55
-XX:-OptimizeStringConcat,子调用将重新出现,并且性能将下降到8u5级别。
好的,因此我们需要弄清楚为什么8u5没有进行相同的优化。使用Grep http://hg.openjdk.java.net/jdk9/jdk9/hotspot查找“ StringBuilder”,以了解VM在哪里处理StringConcat优化。这将带您进入
src/share/vm/opto/stringopts.cpp
hg log src/share/vm/opto/stringopts.cpp
找出那里的最新变化。候选人之一是:
changeset: 5493:90abdd727e64user: iveresovdate: Wed Oct 16 11:13:15 2013 -0700summary: 8009303: Tiered: incorrect results in VM testsstringconcat…
在OpenJDK邮件列表上查找审阅线程(很容易用Google搜索更改集摘要):http : //mail.openjdk.java.net/pipermail/hotspot-compiler-dev/2013-October/012084.html
现货“字符串连接优化,将模式折叠成单个字符串分配并直接形成结果。在优化代码中可能发生的所有可能的取消操作都从头开始重新启动该模式(从StringBuffer分配开始) 。 这意味着,整个模式必须我没有副作用。 “尤里卡?
写下对比基准:
@Fork(5)@Warmup(iterations = 5)@Measurement(iterations = 5)@BenchmarkMode(Mode.AverageTime)@OutputTimeUnit(TimeUnit.NANOSECONDS)@State(Scope.Benchmark)public class IntStr { private int counter;@GenerateMicroBenchmark public String inlineSideEffect() { return new StringBuilder().append(counter++).toString(); } @GenerateMicroBenchmark public String spliceSideEffect() { int cnt = counter++; return new StringBuilder().append(cnt).toString(); }}
- 在JDK 7u55上进行测量,看到内联/拼接副作用的性能相同:
Benchmark Mode Samples Mean Meanerror Units
o.s.IntStr.inlineSideEffect avgt 25 65.460 1.747
ns/op
o.s.IntStr.spliceSideEffect avgt 25 64.414 1.323
ns/op
- 在JDK 8u5上对其进行测量,看到内联效果会导致性能下降:
Benchmark Mode Samples Mean Meanerror Units
o.s.IntStr.inlineSideEffect avgt 25 84.953 2.274
ns/op
o.s.IntStr.spliceSideEffect avgt 25 65.386 1.194
ns/op
提交错误报告(https://bugs.openjdk.java.net/browse/JDK-8043677),以与VM人员讨论此行为。原始修复的基本原理是坚如磐石,但是有趣的是,如果我们能够/应该在这种琐碎的情况下恢复这种优化。
???
利润。
是的,我应该发布基准的结果,该基准将增量从
StringBuilder链中移出,并在整个链之前进行。同样,切换到平均时间和ns / op。这是JDK
7u55:
BenchmarkMode Samples Mean Mean errorUnits
o.s.IntStr.integerToString avgt 25 153.805 1.093
ns/op
o.s.IntStr.stringBuilder0 avgt 25 128.284 6.797
ns/op
o.s.IntStr.stringBuilder1 avgt 25 131.524 3.116
ns/op
o.s.IntStr.stringBuilder2 avgt 25 254.384 9.204
ns/op
o.s.IntStr.stringFormat avgt 25 2302.501 103.032
ns/op
这是8u5:
BenchmarkMode Samples Mean Mean errorUnits
o.s.IntStr.integerToString avgt 25 153.032 3.295
ns/op
o.s.IntStr.stringBuilder0 avgt 25 127.796 1.158
ns/op
o.s.IntStr.stringBuilder1 avgt 25 131.585 1.137
ns/op
o.s.IntStr.stringBuilder2 avgt 25 250.980 2.773
ns/op
o.s.IntStr.stringFormat avgt 25 2123.706 25.105
ns/op
stringFormat实际上在8u5中要快一些,其他所有测试都相同。这巩固了原问题中主要罪魁祸首中SB链副作用断裂的假说。



