为什么字符串串联比 String.valueOf 更快地将整数转换为字符串?

2022-09-02 04:38:00

我有一个基准:

@BenchmarkMode(Mode.Throughput)
@Fork(1)
@State(Scope.Thread)
@Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS, batchSize = 1000)
@Measurement(iterations = 40, time = 1, timeUnit = TimeUnit.SECONDS, batchSize = 1000)
public class StringConcatTest {

    private int aInt;

    @Setup
    public void prepare() {
        aInt = 100;
    }

    @Benchmark
    public String emptyStringInt() {
        return "" + aInt;
    }

    @Benchmark
    public String valueOfInt() {
        return String.valueOf(aInt);
    }

}

这是结果:

Benchmark                                          Mode  Cnt      Score      Error  Units
StringConcatTest.emptyStringInt                   thrpt   40  66045.741 ± 1306.280  ops/s
StringConcatTest.valueOfInt                       thrpt   40  43947.708 ± 1140.078  ops/s

它表明,将空字符串与整数连接起来比调用 String.value(100) 快 30%。我知道“”+ 100转换为

new StringBuilder().append(100).toString()

并应用优化,使其快速。我不明白的是,为什么它本身比串联慢。有人可以解释到底发生了什么,以及为什么“”+ 100更快。魔法能产生什么?-XX:+OptimizeStringConcatvalueOfOptimizeStringConcat


答案 1

正如你所提到的,HotSpot JVM具有识别StringBuilder模式的优化,并将其替换为高度调整的手写IR图,同时依赖于一般的编译器优化。-XX:+OptimizeStringConcatString.valueOf()

通过分析生成的汇编代码,我发现了以下主要区别:

  • 优化的 concat 不会为结果字符串创建的数组为零,而 创建的数组在分配后会像任何其他常规对象一样被清除。char[]Integer.toString
  • 优化的 concat 通过简单地添加 “0” 常量将数字转换为字符,同时使用表查找和相关的数组边界检查等。Integer.getChars

PhaseStringOpts::int_getCharsInteger.getChars的实现还有其他细微差异,但我想它们对性能来说并不那么重要。


顺便说一句,如果您采用较大的数字(例如1234567890),则性能差异可以忽略不计,因为一次转换两位数的额外循环Integer.getChars


答案 2

推荐