• Ввойдите на сайт >>>

Статьи

Производительность строк: анализ - Блог

  1. Фон: Теория струн.
  2. Раунд 1: Измерение собственных операций.
  3. Раунд 2: Сравнение типов техники буферизации.
  4. Результаты
  5. Сравнение браузеров
  6. Раунд 3: применение результатов к dojox.string.Builder.
  7. Нахождение виновных
  8. Оптимизация 1: решение простейшего случая.
  9. Оптимизация 2: Исправление Internet Explorer
  10. Оптимизация 3: Исправление Firefox.
  11. Сравнение производительности dojox.string.Builder с собственными операциями
  12. Выводы

Недавно я писал пост в блоге с советами и рекомендациями, который собирался сосредоточиться на идее, что лучше использовать объект в качестве «буфера строк»; Идея заключалась в том, что, передавая этот объект различным функциям и вставляя в него строковые фрагменты, вы можете повысить производительность с помощью механизма JavaScript. Мой друг и коллега Алекс Рассел заставил меня показать ему достоверные данные, подтверждающие эту гипотезу, и результаты оказались весьма откровенными!

Алекс Рассел

Для этого анализа я использовал два источника для тестов: dojox.string.Builder Тест производительности строителя и пользовательский тест, реализующий три версии общей задачи JavaScript: сериализатор JSON. Первоначальная концепция заключалась в том, чтобы показать, что вставка строк в буфер объектов будет быстрее, чем при использовании простых операций JavaScript, доступных любому разработчику.

Фон: Теория струн.

От моего друга и коллеги Евгений Лазуткин приходит это отличное объяснение о строках в программировании:

Многие современные языки (особенно функциональные языки) используют парадигму «неизменяемый объект», которая решает множество проблем, включая сохранение памяти, локализацию кэша, решение проблем параллелизма и так далее. Идея проста: если объект неизменен, он может быть представлен ссылкой (указатель, дескриптор), и передача ссылки аналогична передаче объекта по значению - если объект неизменен, его значение не может быть изменено = > указатель на этот объект может быть разыменован, создавая то же самое исходное значение. Это означает, что мы можем копировать указатели, не копируя объекты. И все они будут указывать на один и тот же объект. Что мы делаем, когда нам нужно изменить объект? Одним из популярных решений является использование Копирование при записи (COW) , По принципу COW у нас есть указатель на объект (ссылка, дескриптор), мы клонируем объект, на который он указывает, мы меняем значение указателя, чтобы теперь он указывал на клонированный объект, и продолжаем наш алгоритм мутирования. Все остальные ссылки все те же.

JavaScript выполняет все «неизменяемые объекты» для строк, но он не выполняет COW для строк, потому что он использует еще более простой трюк: нет способа изменить строку. Все строки являются неизменяемыми в зависимости от спецификации языка и стандартной библиотеки, с которой он поставляется. Например: str.replace («a», «b») вообще не изменяет str, а возвращает новую строку с выполненной заменой. Или нет. Если шаблон не был найден, я подозреваю, что он вернет указатель на исходную строку. Каждая «мутирующая» операция для строк фактически возвращает новый объект, оставляя исходную строку без изменений: replace, concat, slice, substr, split и даже экзотический якорь, big, bold и т. Д.

Проблема обычно заключается в том, что строковые операции имеют «скрытую стоимость»; поскольку строки (в теории) являются неизменяемыми, можно ожидать, что любой вид строковой мутации будет сопровождаться затратами на копирование и замену, занимая ценные циклы ЦП, занимая пространство памяти и создавая нагрузку на сборщик мусора (среди прочего) , Типичная техника, используемая во многих языках, заключается в использовании чего-либо, что вы можете передать по ссылке (например, объект или массив) в ваши методы, и вставке в него фрагментов строки, что приводит к задержке затрат на мутацию строки до тех пор, пока это не станет абсолютно необходимым. На самом деле, именно по этой причине я написал оригинал dojox.string.Builder ; Я заимствовал эту идею в C # как способ повышения эффективности для больших строковых операций, таких как создание фрагмента документа на лету или сериализация JavaScript Object Literal.

(Примечание: оригинал прошел довольно много итераций, последняя из которых Бен Лоури , который изначально широко использовал Builder Bloglines .)

Проведя довольно обширное тестирование, я узнал, что с современными браузерами это совсем не так; на самом деле, не только типичные строковые операции были оптимизированы кросс-браузерно, но и производительность dojox.string.Builder была неутешительной, особенно по сравнению с нативными операциями.

Раунд 1: Измерение собственных операций.

Диаграмма в верхней части этой статьи представляет собой сводную информацию о производительности собственных операций. Все тесты проводились на MacBook Pro 2,4 ГГц Core 2 Duo с 4 ГБ установленной оперативной памяти; тесты проводились как на OS X, так и на Windows 2003 Server, работающем под Parallels Desktop (с достаточным объемом ОЗУ, выделенным для обеспечения согласованных результатов)

Первый набор тестов был основан на тестах, выполненных Беном Лоури (тест BuilderPerf, см. Ссылку выше) с использованием словаря Lorem Ipsum с номерами по умолчанию (1000 итераций с использованием 100 слов). Основная концепция заключается в тестировании производительности на основе как разовых, так и итерационных операций. В качестве примера, вот тесты для concat () один раз и concat () для :

{name: "concatOnce", test: function () {var s = ""; s = String.prototype.concat.apply (s, words); возврат с; }}, {name: "concatFor", test: function () {var s = ""; for (var i = 0; i <words.length; i ++) {s = s.concat (words [i]); } return s; }}

… Где переменная words - это изначально созданный словарь, используемый для всех тестов.

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

Один интересный результат: оператор + = в строках на самом деле быстрее , чем [string] .concat ().

При более внимательном рассмотрении мы также можем точно измерить производительность с помощью браузера. Например, сравнивая основные браузеры с операциями Once:

Например, сравнивая основные браузеры с операциями Once:

… Мы видим, что два наиболее распространенных браузера, Internet Explorer и Firefox (под Windows) имеют некоторые проблемы; Производительность соединения с Firefox невелика, но совместная работа с Internet Explorer абсолютно неутешительна.

Однако одноразовые операции, подобные этим, хотя и привлекательны и, безусловно, наиболее эффективны, встречаются не так часто, как операции, выполняемые во время итераций. Вот сравнение основных браузеров, использующих три наиболее распространенные операции во время итерации (join, concat и оператор «+ =»):

Из этого конкретного графика можно сделать несколько выводов:

  1. Оператор + = работает быстрее - даже больше, чем вставка фрагментов строки в массив и соединение их в последнюю минуту
  2. Массив как строковый буфер более эффективен во всех браузерах, за исключением Firefox 2.0.0.14/Windows, чем использование String.prototype.concat.apply.

Производительность dojox.string.Builder была также измерена с помощью этой батареи тестов; мы представим эти данные позже в статье.

Раунд 2: Сравнение типов техники буферизации.

Второй раунд испытаний проводился с использованием трех слегка отличающихся версий типичного сериализатора JSON, основанного на один из используемых в Dojo Toolkit , Сериализатор имеет три версии:

  1. Рекурсивная сериализация с использованием + = (передача строк как результат функции)
  2. Рекурсивная сериализация с использованием локальных массивов в качестве буферов (созданных внутри функции)
  3. Сериализация с использованием одного массива, переданного методам в качестве аргумента без возврата (т. Е. Void)

Тест принимает большую предварительно загруженную структуру Javascript Object Literal (сериализованная версия весит 19 200 символов) и сериализует ее, используя все три сериализатора 10 раз, принимая средний результат. Каждый тест проводится 5 раз подряд. Наконец, 20 образцов были взяты для каждого браузера.

Ожидалось, что один объект в виде строкового буфера даст лучшие результаты производительности, одинаково для всех браузеров.

Результаты

Результаты

Во всех случаях (за исключением Opera 9.27 / Win) использование одного объекта в качестве строкового буфера показало наихудший результат, при этом наилучшая общая производительность достигается за счет использования рекурсивной сериализации с оператором «+ =».

Сравнение браузеров

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



Как подробно описано в другом месте, Safari (как на OS X, так и на Windows) является безусловно самым быстрым исполняющим браузером; неожиданно Opera 9.27 - которая исторически имеет превосходный послужной список с точки зрения производительности - оказалась наименее эффективной в этих тестах. Также интересен тот факт, что кроссплатформенные браузеры (Safari, Firefox и Opera) остаются достаточно согласованными с точки зрения фактической производительности.

Раунд 3: применение результатов к dojox.string.Builder.

С результатами обоих доступных раундов был проведен анализ производительности dojox.string.Builder. Цифры производительности (предварительная оптимизация) были получены во время первой серии тестов, что выявило ряд проблем с методом Builder.append (безусловно, наиболее часто используемый метод). Тем не менее, некоторые исследования и тестирование привели к увеличению производительности в 10 раз, а в некоторых случаях даже сравнимо или даже превзошло некоторые операции браузера!

Из-за очень хорошей производительности в Safari и Opera целью процесса оптимизации было устранение недостатков как в Internet Explorer, так и в Firefox.

Нахождение виновных

Тщательно анализируя исходный код с помощью dojox.string.Builder.append:

append: function (s) {return this.appendArray (dojo._toArray (arguments)); }

... мы определили, что ряд факторов вызывал проблемы с производительностью:

  • Вызов dojo._toArray с объектом arguments
  • Передача результатов в dojox.string.Builder.appendArray без предварительного тестирования для более короткой ветви
  • Динамические аргументы доступа к объекту

Оптимизация 1: решение простейшего случая.

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

append: function (s) {if (arguments.length> 1) {return this.appendArray (dojo._toArray (arguments)); } else {this.b + = s; верни это; }}

Выполняя эту простую проверку, мы избавляемся от необходимости вызывать как dojo._toArray, так и переход к методу appendArray . Это изменение увеличило производительность метода добавления с впечатляющими результатами - со ссылкой на диаграмму выше, это был самый большой усилитель производительности.

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

Оптимизация 2: Исправление Internet Explorer

Внутренний строковый буфер, используемый для Internet Explorer, отличается от любого другого браузера; Основываясь на результатах теста нашего первого раунда, мы знаем, что вставка фрагментов строки в массив и последующее объединение их только тогда, когда требуется полная строка, является лучшим методом для обработки строки.

Зная, что виновником реальных проблем с производительностью является вызов dojo._toArray с объектом arguments, цель состояла в том, чтобы устранить необходимость его использования. Одним из простых способов решения проблемы было бы просто перебрать аргументы и добавить каждый из них, один за другим:

append: function (s) {if (arguments.length> 1) {for (var i = 0, l = arguments.length; i
Это немного помогло, но не было резкого увеличения производительности. Чтобы немного больше узнать о методе, мы проверили разницу между использованием [] .push и указанием следующего доступного индекса в массиве вручную; мы обнаружили, что указание следующего доступного индекса было немного быстрее. Наконец, мы прибегли к реализации Устройство Даффа Техника раскручивания петли. Вот окончательный код: append: function (s) {if (arguments.length> 1) {// Устройство некоторых Даффов здесь нравится. var n = Math.floor (arguments.length / 8), r = arguments.length% 8, i = 0; do {switch (r) {case 0: this.b [this.b.length] = arguments [i ++]; случай 7: this.b [this.b.length] = arguments [i ++]; случай 6: this.b [this.b.length] = arguments [i ++]; случай 5: this.b [this.b.length] = arguments [i ++]; случай 4: this.b [this.b.length] = arguments [i ++]; случай 3: this.b [this.b.length] = arguments [i ++]; case 2: this.b [this.b.length] = arguments [i ++]; case 1: this.b [this.b.length] = arguments [i ++]; } r = 0; } while (- n> 0); } else {this.b [this.b.length] = s; } вернуть это; }
Эта развертка цикла снизила производительность добавления с несколькими аргументами до одинакового или более быстрого значения, чем при использовании добавления с одним аргументом несколько раз.

Оптимизация 3: Исправление Firefox.


Последний набор оптимизаций включал в себя исправление мрачной производительности в Firefox. Как и в Internet Explorer, у Firefox были серьезные проблемы при использовании dojo._toArray , поэтому первым делом было перебирать итерацию по объекту аргументов внутри самого метода добавления: append: function (s) {if (arguments.length> 1) {var i = 0; в то время как я
Это открыло то, что доступ к членам объекта arguments, казалось, был очень медленным - до такой степени, что итерация по каждому аргументу вызывала достаточно снижения производительности, чтобы фактически снизить производительность по сравнению с использованием dojo._toArray !
Имея это в виду, и помощь моего коллеги Крис Зип мы определили, что проблема в Firefox не столько в том, что доступ к членам объекта arguments является проблемой; Дело в том, что проблема заключается в динамическом доступе к объекту аргументов . Имея это в виду, был вызван другой вид развертывания цикла: append: function (s) {if (arguments.length> 1) {var tmp = "", l = arguments.length; switch (l) {case 9: tmp = arguments [8] + tmp; случай 8: tmp = arguments [7] + tmp; case 7: tmp = arguments [6] + tmp; случай 6: tmp = arguments [5] + tmp; случай 5: tmp = arguments [4] + tmp; случай 4: tmp = arguments [3] + tmp; случай 3: tmp = arguments [2] + tmp; case 2: {this.b + = arguments [0] + arguments [1] + tmp; перерыв; } по умолчанию: {var i = 0; в то время как я
С помощью этой техники развертывания мы выполняем следующее:

  1. Создаем временный строковый буфер ( tmp )
  2. Мы используем преимущества сквозного переключателя Javascript ... case для обработки до 9 аргументов
  3. В каждом случае мы указываем фактический номер аргумента
  4. Мы добавляем аргумент во временный буфер
  5. ... наконец, добавив результат во внутренний буфер ( this.b ).

Это развертывание не оказало заметного влияния на производительность Safari или Opera, но имело огромное значение с Firefox; На приведенном выше графике вы можете увидеть почти 10-кратное увеличение производительности в версиях Firefox для Windows и OS X. Обратите внимание, что передача более 9 аргументов заставит Firefox вернуться к версии доступа к динамическим аргументам; в теории мы можем просто добавить больше случаев для решения этой проблемы, но с практической точки зрения аргументы должны быть хорошим пределом.

Сравнение производительности dojox.string.Builder с собственными операциями


Наконец, мы должны сравнить сравнение новой оптимизированной версии dojox.string.Builder с собственными операциями, которые мы проанализировали в раундах 1 и 2. Опять же, тесты (включая новую, которая тестировала производительность Builder.append с несколькими аргументами) были запущены, как и в первом раунде - на этот раз с оптимизированным Builder.
Наконец, мы должны сравнить сравнение новой оптимизированной версии dojox
В Safari и Opera dojox.string.Builder все еще немного медленнее, когда используется в итерационных ситуациях; однако разница достаточно близка, так что при ее использовании не должно быть заметного снижения производительности. С Internet Explorer мы обнаруживаем, что производительность Builder все еще довольно плоха по сравнению с нативными операциями.
Однако в Firefox единственной операцией, которая быстрее, чем использование Builder.append, является оператор «+ =»; и join, и concat достаточно близки к Builder.append, поэтому не имеет значения, какую версию вы используете.

Выводы


Хотя этот анализ, ряд вещей о производительности строки были замечены:

  • Собственные строковые операции во всех браузерах были оптимизированы до такой степени, что методы заимствования из других языков (например, передача одного буфера для использования многими методами) по большей части не нужны.
  • Array.join все еще кажется самым быстрым методом в Internet Explorer; + + или String.prototype.concat.apply ("", аргументы) лучше всего работают для всех других браузеров.
  • Firefox имеет определенные проблемы с доступом к членам аргумента через динамические / переменные
  • И, конечно же, напоминание не игнорировать данные

Для тех, кто склонен численно, вы можете скачать PDF-версию всех собранных данных.

Похожие

Общие рецепты
Эта страница содержит ассортимент проблем и их соответствующих решений, связанных с Grav в целом. Изменить версию PHP CLI Иногда на терминале версия PHP отличается от версии PHP, используемой веб-сервером. Вы можете проверить версию PHP, запущенную в CLI, выполнив команду php -v. Если версия PHP меньше 5.5.9, Grav не будет работать, так как для этого требуется как минимум PHP 5.5.9. Как исправить? Вам нужно ввести некоторую конфигурацию
Замена лучших улиц и поездок
Теперь, когда Microsoft решила прекратить MapPoint и Streets and Trips, многие торговые представители ищут программу замены. К счастью, сейчас есть
Демо Magento Магазин для расширения предложения: Cart2Quote
Насчет нас Вместе мы Cart2Quote и кучка преданных профессионалов Magento. Мы обладаем более чем десятилетним опытом в области электронной коммерции в области консалтинга, управления проектами, кодирования и веб-дизайна. Мы работали более 200 интернет-компаний и специализируемся на расширениях
Как быстро добавить часы / минуты / секунды к дате и времени в Excel?
Например, у вас есть список дат и времени на листе, и теперь вы хотите добавить те же часы / минуты / секунды к тем ячейкам, которые смешаны с датой и временем. Вы можете использовать формулы для решения этой задачи, но здесь я могу рассказать вам еще один прием для быстрого достижения этой цели в Excel. Использование формул для добавления часов / минут / секунд к дате и времени В Excel обычно можно использовать формулы для добавления часов, минут или секунд
Что мы делаем, когда нам нужно изменить объект?
5.5.9. Как исправить?

Новости

Карта