Либо литералы ES6 быстрее, чем конкатенация строк?

Кто-нибудь сделал тесты? Мне любопытно, если код генерации HTML быстрее с конкатенацией строк или с литералами шаблонов в Node и современных браузерах.

Например:

Конкатенация строк

"<body>"+
  "<article>"+
    "<time datetime='" + date.toISOString() +"'>"+ date +"</time>"+
  "</article>"+
"</body>"

Литературный шаблон

`<body>
  <article>
    <time datetime='${ date.toISOString() }'>${ date }</time>
  </article>
</body>`

Ответ 1

Кажется, на данный момент конкатенация строк происходит быстрее: http://jsperf.com/es6-string-literals-vs-string-concatenation

ES6 with variable                     19,992,512    ±5.21%    78% slower
String concatenation with variable    89,791,408    ±2.15%    fastest
ES6 with function                     461,358       ±3.12%    99% slower
String concatenation with function    503,255       ±1.77%    99% slower

Я тестировал, работал на канале Chrome 43.0.2334.0 (64-разрядная версия), который использует V8 4.3.31, с включенным флагом #enable-javascript-harmony.

Для справки: последняя версия Node.js(0.12.0 на момент написания статьи) использует V8 3.28.73: https://raw.githubusercontent.com/joyent/node/master/ChangeLog

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


Редактировать: Спасибо за комментарии @user1329482, @icl7126, Николай Борисик и FesterCluck. Теперь, когда прошло около 2 лет с тех пор, как был задан этот вопрос, поддержка браузеров ES6 значительно возросла, и была достигнута значительная оптимизация производительности. Вот некоторые обновления:

В Chrome (по состоянию на 59.0.3035) строковые литералы ES6 работают быстрее:

ES6 with variable                     48,161,401       ±1.07%    fastest
String concatenation with variable    27,046,298       ±0.48%    44% slower
ES6 with function                     820,441          ±1.10%    98% slower
String concatenation with function    807,088          ±1.08%    98% slower

В Firefox (по состоянию на 57.0.0) строковые литералы ES6 работают быстрее:

ES6 with variable                     1,924,610,984    ±0.50%    fastest
String concatenation with variable    1,876,993,458    ±0.79%    3% slower
ES6 with function                     539,762          ±5.04%    100% slower
String concatenation with function    546,030          ±5.88%    100% slower

В Safari (по состоянию на 11.0.2) это зависит от:

ES6 with variable                     1,382,752,744    ±0.71%    fastest
String concatenation with variable    1,355,512,037    ±0.70%    2% slower
ES6 with function                     876,516          ±1.01%    100% slower
String concatenation with function    883,370          ±0.79%    100% slower

При использовании строки Typecast строковые литералы ES6 работают быстрее. Однако при вызове функции из литерала конкатенация строк в этом примере выполняется быстрее.

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

Ответ 2

Я сделал наивный тест на node.js v6.0.0 и получил почти ту же производительность. Поскольку тест настолько наивен, не верьте слишком большим числам. Но, похоже, компилятор JIT генерирует очень оптимизированный код в наши дни. Это позволило мне выбрать предпочтительные шаблоны для конкатенации для моих приложений node.

Для справки это код, который я использовал:

'use strict'

function strConcat(i) {
    return 'abc' + i + 'def'
}

function strTemplate(i) {
    return `abc${i}def`
}

function run(strategy) {
    let before = new Date().getTime()
    let len = 0
    for ( let i = 0; i < 10000000; i+=1 ) {
        len += strategy(i).length
    }
    console.log(len + ' - ' + ((new Date().getTime()) - before) + 'ms')
}

console.log('strConcat')
run(strConcat)

console.log('strTemplate')
run(strTemplate)

И результат был:

strConcat
128888890 - 1904ms
strTemplate
128888890 - 1979ms

Я использовал len, чтобы убедиться, что оптимизатор не оптимизирует весь цикл. Во всяком случае, это еще очень простой тест. Возможно, кто-то может сделать более сложный.

Ответ 3

Для простого теста со случайными числами как строкой, оба приближаются так близко в Chrome и FF

Тестирование в Chrome 58.0.3029/Windows 10

Строковые литералы 2,996,883 ± 2,36% быстрее

Оператор (+) 3,054,078 ± 2,01% быстрее

Конкатентная функция 2,659,391 ± 2,35% 13% медленнее

Тестирование в Firefox 53.0.2/Windows 10

Строковые литералы 1 923 835 ± 1,52% быстрее

Оператор (+) 1,948,503 ± 1,13% быстрее

Конкатентная функция 1,810,857 ± 1,81% 8% медленнее

Проверить здесь на jsperf

Ответ 4

TL; DR

Конкатенация быстрее и более последовательна в отношении ее скорости. Но разница очень мало для 1 или 2 переменных (ниже 0,3 секунды для 100 миллионов вызовов).

Изменить

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


Итак, я хотел развернуть аналог-nico ответ, предоставив тест, который был более обширным, а также немного посмотрел на масштабируемость двух функций.

Код на pastebin

Я решил использовать четыре тестовых примера для каждой функции, имея переменную спереди, одну в конце, одну в середине и две переменные в середине. Базовая установка такая же. Я просто использую 100 000 000 итераций функции, и эти итерации выполняются 100 раз. Я использовал те же механизмы для предотвращения оптимизации, а именно получения суммы длин результирующих строк и ведения журнала. Я также записал необходимое время (чтобы угадать, сколько времени это займет), но также сохранил его в массиве.

Затем я вычислил среднее, минимальное, максимальное и стандартное отклонения для каждого метода.

Вот результаты:

{ 
  sum: { 
    t: { 
      start: 2072751, 
      mid: 2338476, 
      end: 2083695, 
      double: 2950287 
    },
    c: { 
      start: 2086059, 
      mid: 2345551, 
      end: 2074732, 
      double: 2922929 
    } 
  },
  avg: { 
    t: { 
      start: 20727.51,
      mid: 23384.76,
      end: 20836.95,
      double: 29502.87 
    },
    c: { 
      start: 20860.59,
      mid: 23455.51,
      end: 20747.32,
      double: 29229.29 
    } 
  },
  sd: {
    t: {
      start: 335.6251329981114,
      mid: 282.9490809315344,
      end: 286.2220947096852,
      double: 216.40844045461824 
    },
    c: {
      start: 255.4803356424913,
      mid: 221.48744862858484,
      end: 238.98242111084238,
      double: 209.9309074433776 
    } 
  },
  min: { 
    t: { 
      start: 20490, 
      mid: 23216, 
      end: 20588, 
      double: 29271 
    },
    c: { 
      start: 20660, 
      mid: 23258, 
      end: 20534, 
      double: 28985 
    } 
  },
  max: { 
    t: { 
      start: 23279, 
      mid: 25616, 
      end: 22887, 
      double: 30843 
    },
    c: { 
      start: 22603, 
      mid: 25062, 
      end: 22403, 
      double: 30536 
    } 
  } 
}

значения в t -объектах предназначены для шаблонов, значения в c -объектах предназначены для конкатенации. start означает, что переменная находится в начале, в середине, когда она находится в середине, заканчивается, что она находится в конце и в два раза, что есть две переменные. sum - сумма всех 100 прогонов. avg - средний пробег, что означает sum / 100. sd Вот простой выход, wikipedia (простой английский). min и max являются минимальным и максимальным значением пробега соответственно.

Результаты

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

Несмотря на то, что как минимум, так и среднее число шаблонов лучше, чем их конкатенационные аналоги в отношении первых двух условий, стандартное отклонение последовательно хуже. Разница, похоже, сокращается с большим количеством переменных (требуется больше тестов).

Так как большинство шаблонов, вероятно, не будут использоваться только для одной переменной в строке, можно сказать, что прилипание к конкатенации дает лучшую производительность. Но разница (по крайней мере на данный момент) очень незначительна. При 100 000 000 (100 миллионов) оценок с двумя переменными разница составляет всего 273,58 мс, около четверти секунды...


Второй запуск

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

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

Вот результаты:

{
  "sum": {
    "t": {
      "start": 1785103,
      "mid": 1826679,
      "end": 1719594,
      "double": 2110823,
      "many": 4153368
    },
    "c": {
      "start": 1720260,
      "mid": 1799579,
      "end": 1716883,
      "double": 2097473,
      "many": 3836265
    }
  },
  "avg": {
    "t": {
      "start": 17851.03,
      "mid": 18266.79,
      "end": 17195.94,
      "double": 21108.23,
      "many": 41533.68
    },
    "c": {
      "start": 17202.6,
      "mid": 17995.79,
      "end": 17168.83,
      "double": 20974.73,
      "many": 38362.65
    }
  },
  "sd": {
    "t": {
      "start": 858.7857061572462,
      "mid": 886.0941856823124,
      "end": 786.5366719994689,
      "double": 905.5376950188214,
      "many": 1744.9005638144542
    },
    "c": {
      "start": 599.0468429096342,
      "mid": 719.1084521127534,
      "end": 935.9367719563112,
      "double": 991.5642274204934,
      "many": 1465.1116774840066
    }
  },
  "aad": {
    "t": {
      "start": 579.1207999999996,
      "mid": 576.5628000000003,
      "end": 526.8268,
      "double": 586.9651999999998,
      "many": 1135.9432000000002
    },
    "c": {
      "start": 467.96399999999966,
      "mid": 443.09220000000016,
      "end": 551.1318000000008,
      "double": 610.2321999999999,
      "many": 1020.1310000000003
    }
  },
  "min": {
    "t": {
      "start": 16932,
      "mid": 17238,
      "end": 16387,
      "double": 20016,
      "many": 39327
    },
    "c": {
      "start": 16477,
      "mid": 17137,
      "end": 16226,
      "double": 19863,
      "many": 36424
    }
  },
  "max": {
    "t": {
      "start": 23310,
      "mid": 24102,
      "end": 21258,
      "double": 26883,
      "many": 49103
    },
    "c": {
      "start": 19328,
      "mid": 23203,
      "end": 22859,
      "double": 26875,
      "many": 44352
    }
  },
  "median": {
    "t": {
      "start": 17571,
      "mid": 18062,
      "end": 16974,
      "double": 20874,
      "many": 41171.5
    },
    "c": {
      "start": 16893.5,
      "mid": 18213,
      "end": 17016.5,
      "double": 20771,
      "many": 38849
    }
  }
}

Код здесь

Ответ 5

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