Почему конкатенация строк быстрее, чем объединение массивов? - PullRequest
105 голосов
/ 04 сентября 2011

Сегодня я прочитал этой ветки о скорости объединения строк.

Удивительно, но конкатенация строк была победителем:

http://jsben.ch/#/OJ3vo

Результат был противоположен тому, что я думал.Кроме того, есть много статей об этом, которые объясняют как this .

Я могу догадаться, что браузеры оптимизированы для строки concat в последней версии, но как они это делают?Можно ли сказать, что лучше использовать + при конкатенации строк?

Обновление

Таким образом, в современных браузерах конкатенация строк оптимизирована, поэтому используются знаки +быстрее, чем join, когда вы хотите объединить строк.

Но @ Артур указал , что join быстрее, если вы действительно хотите объединить строки с разделителем.

Ответы [ 9 ]

143 голосов
/ 04 сентября 2011

Оптимизация строки браузера изменила изображение конкатенации строк.

Firefox был первым браузером, который оптимизировал конкатенацию строк. Начиная с версии 1.0, метод массива фактически медленнее, чем использование оператора «плюс» во всех случаях. Другие браузеры также оптимизировали конкатенацию строк, поэтому Safari, Opera, Chrome и Internet Explorer 8 также показывают лучшую производительность, используя оператор «плюс». В Internet Explorer до версии 8 такой оптимизации не было, поэтому метод массива всегда быстрее, чем оператор «плюс».

- Написание эффективного JavaScript: глава 7 - Еще более быстрые сайты

Механизм JavaScript V8 (используемый в Google Chrome) использует этот код для объединения строк:

// ECMA-262, section 15.5.4.6
function StringConcat() {
  if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
    throw MakeTypeError("called_on_null_or_undefined", ["String.prototype.concat"]);
  }
  var len = %_ArgumentsLength();
  var this_as_string = TO_STRING_INLINE(this);
  if (len === 1) {
    return this_as_string + %_Arguments(0);
  }
  var parts = new InternalArray(len + 1);
  parts[0] = this_as_string;
  for (var i = 0; i < len; i++) {
    var part = %_Arguments(i);
    parts[i + 1] = TO_STRING_INLINE(part);
  }
  return %StringBuilderConcat(parts, len + 1, "");
}

Итак, внутренне они оптимизируют его, создав InternalArray (переменная parts), который затем заполняется. Функция StringBuilderConcat вызывается с этими частями. Это быстро, потому что функция StringBuilderConcat является сильно оптимизированным кодом C ++. Здесь слишком долго цитировать, но в файле runtime.cc найдите RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderConcat), чтобы увидеть код.

22 голосов
/ 04 сентября 2011

Firefox работает быстро, потому что он использует нечто, называемое Веревки ( Веревки: альтернатива строкам ).В принципе, веревка - это просто DAG, где каждый узел является строкой.

Так, например, если вы сделаете a = 'abc'.concat('def'), вновь созданный объект будет выглядеть следующим образом. Конечно, это не совсем так, как это выглядит в памяти, потому что вам все еще нужно иметь поле для типа строки, длины и, возможно, других.

a = {
 nodeA: 'abc',
 nodeB: 'def'
}

И b = a.concat('123')

b = {
  nodeA: a, /* {
             nodeA: 'abc',
             nodeB: 'def'
          } */
  nodeB: '123'
}           

Таким образом, в простейшем случае виртуальная машина почти не выполняет работы.Единственная проблема заключается в том, что это немного замедляет другие операции с полученной строкой.Также это, конечно, уменьшает накладные расходы памяти.

С другой стороны, ['abc', 'def'].join('') обычно просто выделяет память для размещения новой строки в памяти.(Может быть, это должно быть оптимизировано)

4 голосов
/ 27 марта 2018

Я знаю, что это старая тема, но ваш тест неверен. Вы делаете output += myarray[i];, а это должно быть больше похоже на output += "" + myarray[i];, потому что вы забыли, что вам нужно склеивать предметы вместе с чем-то. Код concat должен выглядеть примерно так:

var output = myarray[0];
for (var i = 1, len = myarray.length; i<len; i++){
    output += "" + myarray[i];
}

Таким образом, вы делаете две операции вместо одной из-за склеивания элементов.

Array.join() быстрее.

3 голосов
/ 29 мая 2015

Тесты там тривиальные.Повторное объединение одних и тех же трех элементов будет встроено, результаты окажутся детерминированными и запомненными, обработчик мусора будет просто выбрасывать объекты массива (размер которых будет почти нулевым) и, вероятно, просто вытолкнут и вытолкнут из стека из-за отсутствиявнешние ссылки и потому что строки никогда не меняются.Я был бы более впечатлен, если бы в тесте было большое количество случайно сгенерированных строк.Как в концерте или на две строки.

Array.join FTW!

2 голосов
/ 04 сентября 2011

Я бы сказал, что со строками легче заранее выделить больший буфер.Каждый элемент занимает всего 2 байта (если UNICODE), поэтому, даже если вы консервативный, вы можете предварительно выделить довольно большой буфер для строки.С arrays каждый элемент является более "сложным", потому что каждый элемент является Object, поэтому консервативная реализация будет предварительно выделять пространство для меньшего количества элементов.

Если вы попытаетесь добавить for(j=0;j<1000;j++) перед каждым for вы увидите, что (под хромом) разница в скорости становится меньше.В конце концов, для конкатенации строк он все еще был в 1,5 раза, но меньше, чем в 2,6.

И при необходимости копировать элементы, символ Unicode, вероятно, меньше, чем ссылка на объект JS.

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

1 голос
/ 18 января 2014

Этот тест показывает наказание за фактическое использование строки, созданной с помощью конкатенации присваиваний, по сравнению с методом array.join. Хотя общая скорость назначения в Chrome v31 все еще в два раза выше, но она уже не так велика, как при отсутствии результирующей строки.

0 голосов
/ 03 марта 2019

При большом количестве данных соединение происходит быстрее, поэтому вопрос задан неверно.

let result = "";
let startTime = new Date().getTime();
for (let i = 0; i < 2000000; i++) {
    result += "x";
}
console.log("concatenation time: " + new Date().getTime() - startTime);

startTime = new Date().getTime();
let array = new Array(2000000);
for (let i = 0; i < 2000000; i++) {
    array[i] = "x";
}
result = array.join("");
console.log("join time: " + new Date().getTime() - startTime);

Протестировано на Chrome 72.0.3626.119, Firefox 65.0.1, Edge 42.17134.1.0. Обратите внимание, что это быстрее, даже с включенным созданием массива!

0 голосов
/ 09 февраля 2013

Это явно зависит от реализации движка javascript.Даже для разных версий одного движка вы можете получить существенно разные результаты.Вы должны сделать свой собственный тест для проверки этого.

Я бы сказал, что String.concat имеет лучшую производительность в последних версиях V8.Но для Firefox и Opera Array.join является победителем.

0 голосов
/ 04 сентября 2011

Я предполагаю, что, хотя каждая версия имеет стоимость многих конкатенаций, версии для объединения в дополнение к этому создают массивы.

...