Статьи

Миф о Java StringBuilder развенчан

Миф

Объединение двух строк с помощью оператора плюс является источником всего зла

— анонимный Java-разработчик

ПРИМЕЧАНИЕ . Исходный код тестов, обсуждаемых здесь, можно найти на Github.

Еще со времен университета я научился рассматривать конкатенацию строк в Java с использованием оператора «+» плюс как смертельный грех производительности. Недавно в Backbase R & D был проведен внутренний обзор, в котором такая повторяющаяся мантра была отвергнута как миф из-за того, что javac использовал StringBuilder под колпаком каждый раз, когда вы используете оператор плюс для присоединения к Strings. Я настроен на то, чтобы доказать такую ​​точку зрения и проверить реальность в различных условиях.

Тест

Если вы полагаетесь на свой компилятор для оптимизации конкатенации строк, это может сильно измениться в зависимости от выбранного вами поставщика JDK. Что касается поддержки платформы для моей повседневной работы, следует рассмотреть три основных поставщика:

  • Oracle JDK
  • IBM JDK
  • ECJ — только для разработчиков

Более того, хотя мы официально поддерживаем Java 5–6, мы также ищем поддержку Java 7 для наших продуктов, добавив еще один тройной уровень косвенности по сравнению с тремя поставщиками. Во имя ленивости Для простоты скомпилированный байт-код ecj будет работать с одним JDK, а именно Oracle JDK7. Я подготовил виртуальную машину Virtualbox со всем вышеупомянутым JDK, а затем разработал несколько классов для выражения трех различных методов конкатенации, что в зависимости от конкретного контрольного примера составляет от трех до четырех конкатенаций на один вызов метода. Тестовые классы проводятся тысячи раз для каждого тестового раунда, всего 100 тестов в каждом тестовом случае. Одна и та же виртуальная машина используется для выполнения всех циклов для одного и того же тестового примера, и она перезапускается для разных тестовых случаев, и все это позволяет среде выполнения Java выполнять все возможные оптимизации без какого-либо влияния на другие тестовые случаи. Параметры по умолчанию использовались для запуска всех JVM. Более подробную информацию можно найти в сценарии запуска тестов.

Код

Полный код для тестовых случаев и набора тестов доступен на Github . Следующие различные тестовые случаи были созданы для измерения различий в производительности конкатенации строк с плюсом и прямого использования StringBuilder :

1
2
3
// String concat with plus
String result = 'const1' + base;
result = result + 'const2';
1
2
3
4
5
6
7
8
// String concat with a StringBuilder
new StringBuilder()
              .append('const1')
              .append(base)
              .append('const2')
              .append(append)
              .toString();
}
1
2
3
4
5
6
//String concat with an initialized StringBuilder
new StringBuilder('const1')
              .append(base)
              .append('const2')
              .append(append)
              .toString();

Общая идея состоит в том, чтобы обеспечить конкатенацию как в начале, так и в хвосте константы String s над переменной. Разница между двумя последними случаями, в обоих из которых явно используется StringBuilder , заключается в том, что в последнем используется конструктор с 1 аргументом, который инициализирует конструктор с начальной частью результата.

Результаты

Хватит говорить, ниже здесь вы можете взглянуть на сгенерированные графики, где каждая точка данных соответствует одному раунду теста (например, 1000 выполнений одного и того же класса теста). Обсуждение результатов и некоторые более сочные детали будут следовать.

catplus

catsb

catsb2

Дискуссия

Oracle JKD5 — явный неудачник, похоже, находится в лиге B по сравнению с другими. Но на самом деле это не является предметом этого упражнения, и поэтому мы его пока затушевываем. Тем не менее, есть два других интересных момента, которые я наблюдаю на приведенном выше графике. Первое состоит в том, что действительно существует большая разница между использованием оператора «плюс» и явного StringBuilder , особенно если вы используете Oracle Java5, который работает в три раза хуже, чем остальные члены команды.

Второе замечание заключается в том, что, хотя для большинства JDK обычно справедливо то, что явный StringBuilder будет предлагать вдвое большую скорость, чем обычный оператор плюс, IBM JDK6, похоже, не страдает от каких-либо потерь производительности, всегда составляющих в среднем 25 мс для выполнения задачи все тестовые случаи. Более внимательный взгляд на сгенерированный байт-код раскрывает некоторые интересные детали

Байт-код

ПРИМЕЧАНИЕ: декомпилированные классы также доступны на Github. Для всех возможных JDK StringBuilders всегда используются для реализации конкатенации строк даже при наличии знака плюс. Более того, для всех поставщиков и версий практически нет различий для одного и того же теста. Единственный, который стоит немного в стороне, — это ecj , единственный, кто хитро оптимизирует тестовый пример CatPlus для вызова 1-аргументного конструктора StringBuilder вместо 0-аргументной версии.

Сравнение полученного байт-кода показывает, что может повлиять на производительность в различных сценариях:

  • при конкатенации с плюсом новые экземпляры StringBuilder создаются каждый раз, когда происходит конкатенация. Это может легко привести к снижению производительности из-за бесполезного вызова конструктора и большей нагрузке на сборщик мусора из-за выбрасывания экземпляров.
  • компиляторы воспримут вас буквально и только инициализируют StringBuilder с его 1-аргументным конструктором, если и только если вы напишите его таким образом в исходном коде. Это приводит к соответственно четырем и трем вызовам StringBuilder.append для CatSB и CatSB2 .

Вывод

Анализ байт-кода предлагает окончательный ответ на исходный вопрос. Вам нужно явно использовать StringBuilder для повышения производительности? Да Приведенные выше графики ясно показывают, что, если вы не используете среду выполнения IBM JDK6, вы потеряете 50% производительности при использовании оператора плюс, хотя это тот, который работает несколько хуже среди кандидатов при StringBuilder . Кроме того, довольно интересно посмотреть, как JIT-оптимизации влияют на общую производительность: например, даже при наличии разных байт-кодов между двумя явными тестовыми StringBuilder конечный результат в конечном итоге абсолютно одинаков.

Миф подтвержденном
Справка: миф о Java StringBuilder развенчан нашим партнером по JCG Карло Скиоллой в блоге Skuro .