2017-02-12 7 views
16

У меня есть ориентир:Почему конкатенация строк быстрее, чем String.valueOf для преобразования целого в строку?

@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 

Это показывает, что конкатенации пустой строки с целым числом в 30% быстрее, чем вызов String.value (100). Я понимаю, что «» + 100 преобразуется в

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

и -XX:+OptimizeStringConcat оптимизации применяется, что делает это быстро. Я не понимаю, почему valueOf сам медленнее, чем конкатенация. Может кто-нибудь объяснить, что именно происходит, и почему «+ 100 быстрее. Какую магию делает OptimizeStringConcat?

+1

'' "+ 100', вероятно, гораздо яснее, чтобы компилятор мог распознавать константу ... –

+0

Вы можете посмотреть исходный код' valueOf'. Я уверен, что он пытается обнаружить типы объектов, что означает автоматическое определение вашего примитива int и такого –

+1

. Один из них - вызов метода. Другой, компилятор может скомпилировать, однако он хочет. –

ответ

12

Как вы уже упоминали, у HotSpot JVM есть оптимизация -XX:+OptimizeStringConcat, которая распознает шаблон StringBuilder и заменяет его высоконаправленным рукописным IR-графиком, а String.valueOf() опирается на общие оптимизации компилятора.

Я нашел следующие основные различия, анализируя сгенерированный код сборки:

  • Оптимизированный CONCAT не обнуляет char[] массива, созданного для строки результата, в то время как массив, созданный Integer.toString очищается после выделения так же, как любой другой регулярный объект.
  • Оптимизированного CONCAT переводит цифры в голец простого addition of '0' constant, в то время как Integer.getChars использует table lookup с соответствующими границами массива проверки и т.д.

Есть другие незначительные отличия в реализации PhaseStringOpts::int_getChars против прогноза Integer.getChars, но я предполагаю, что они не так уж важно для производительности.


Кстати, если взять большее количество (например, 1234567890), разница в производительности будет незначительным из-за extra loop в Integer.getChars, который преобразует две цифры сразу.