Сравнение эффективности разных способов конкатенаций на JavaScript

Сравнение разных способов конкатенаций на JavaScript

Однажды проходя собеседование меня спросили

"Если вы будете соединять несколько строчек, то как лучше это сделать?"

и дали примерно такой код "a"+"b"+"c"+... с намеком на то, что каждая отдельная конкатенация будет кушать ресурсы.

И мне стало интересно как же в действительности будет лучше объединять строки на JS.

Начало измерений

Я решил, что хорошо бы все подходы для конкатенации строчек, которые я смогу придумать, проверить на практике.

Выбрал площадку для тестирования jsben.ch

Подготовка окружения для измерений

Добавил в setup block код для генерации различных строчек

const testData =
      [...new Array(1000)]
        .map((item, i) => [i+"1234567890"+Math.random(),i+"1a2s3d4f5g6h7j8kl9q0"+Math.random()]);

Конкатенация через Array.join

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

const test =  (arr) => {
  if (!arr || arr.length == 0) return "";

  const result = ["
"]; for (const item of arr) { result.push("
"); result.push(item[0]); result.push("
"); result.push("
"); result.push(item[1]); result.push("
"); } result.push("
"); return result.join(''); }; test(testData);

Конкатенация через Array.join с применением выноса некоторых строчек в константу

Вынос строк в константу довольно логичная, на мой взгляд, идея, ведь теперь не нужно отдельно для каждой такой строчки выделять дополнительное место в памяти и тратить на это ресурсы.

const test = (arr) => {
  if (!arr || arr.length == 0) return "";
  //выносим часто встречающиеся строки в константы
  const dt = "
"; const dt_ = "
"; const dd = "
"; const dd_ = "
"; const result = ["
"]; for (const item of arr) { result.push(dt); result.push(item[0]); result.push(dt_); result.push(dd); result.push(item[1]); result.push(dd_); } result.push("
"); return result.join(''); }; test(testData);

Конкатенация через Array.join с использованием интерполяции строки

Отлично выглядит/читается и рекомендуется всеми современными манулами

const test = (arr) => {
  if (!arr || arr.length == 0) return "";

  const result = ["
"]; for (const item of arr) { result.push(<dt>${item[0]}</dt><dd>${item[1]}</dd>); } result.push("
"); return result.join(''); }; test(testData);

Конкатенация через бинарный оператор "+"

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

Но у нас всё просто — строчки соединяются со строчками, поэтому проблем быть не должно.

const test = (arr) => {
  if (!arr || arr.length == 0) return "";

  let result = "
"; for (const item of arr) { result = result + "
" + item[0] + "
" + item[1] + "
"; } result = result + "
"; return result; }; test(testData);

Конкатенация через TypedArray(типизированный массив)

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

const test = (arr) => {
  const enc = (str)=>{
    const strChars = new Uint8Array(str.length);
    for (let q =0; q < str.length ; q++){
            strChars[q] = str.charCodeAt(q);
        }
    return strChars;
  };

  if (!arr || arr.length == 0) return "";
  const dt = enc("
") const dt_ = enc("
"); const dd = enc("
"); const dd_ = enc("
"); const fullLength = arr.reduce((acc, item) =>acc+item[0].length+item[1].length , 0) + (dt.length + dt_.length + dd.length + dd_.length)*arr.length + 9; let pos = 0; const result = new Uint8Array(fullLength); result.set(enc("
"), pos);pos += 4; for (const item of arr) { result.set(dt, pos);pos+=dt.length; result.set(enc(item[0]), pos);pos+=item[0].length; result.set(dt_, pos);pos+=dt_.length; result.set(dd, pos);pos+=dd.length; result.set(enc(item[1]), pos);pos+=item[1].length; result.set(dd_, pos);pos+=dd_.length; } result.set(enc("
"));pos += 5; const decoder = new TextDecoder("utf-8"); return decoder.decode(result); }; test(testData);

Итог

Опыты показали, что результаты могут очень сильно различаться в зависимости от используемого компьютера, браузера и его версии.

Например вот такая ситуация произошла при сравнении на разных браузерах FireFox94.0.2(64bit) и Chrome 96.0.4664.45(64bit) тут явно в FireFox есть проблемы с интерполяцией строк

FireFox94.0.2(64bit) и Chrome 96.0.4664.45(64bit)

Итак, объявим победителей:

Выиграл, с очень большим отрывом, обычный бинарный оператор "+".

На втором месте идет join с интерполяцией строки

Третье и четвертое место занимает способ который сначала складывает все строки в массив и уже потом делает join для всех строк.

Способ конкатенации через типизированные массивы оказался самым неудачным в плане производительности.


Итоговый результат можно посмотреть(измерить) тут

Сравнение эффективности разных способов конкатенаций на JavaScript: 1 комментарий

  1. Привет! Это комментарий.
    Чтобы начать модерировать, редактировать и удалять комментарии, перейдите на экран «Комментарии» в консоли.
    Аватары авторов комментариев загружаются с сервиса Gravatar.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *