关于java:为什么String连接比String.valueOf更快,以便将Integer转换为String?

Why is String concatenation faster than String.valueOf for converting an Integer to a String?

我有一个基准:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@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);
    }

}

结果是:

1
2
3
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转换成

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

并采用了-XX:+OptimizeStringConcat优化,使之快速。我不明白为什么valueOf本身比连接慢。有人能解释一下到底发生了什么,为什么"+100更快。OptimizeStringConcat创造了什么魔力?


正如您所提到的,Hotspot JVM具有识别StringBuilder模式的-XX:+OptimizeStringConcat优化,并用高度调优的手写红外图替换它,而String.valueOf()依赖于常规的编译器优化。

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

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

在执行PhaseStringOpts::int_getCharsInteger.getChars方面还有其他细微的差异,但我猜它们对性能没有那么重要。

顺便说一句,如果取一个更大的数字(例如1234567890),性能差异将可以忽略,因为Integer.getChars中有一个额外的循环可以同时转换两位数字。