Профессиональные курсы: Практика верстки HTML JavaScript для начинающих Программирование на PHP Онлайн-курсы по веб-технологиям

Производительность конкатенации строк

Практика может быть далека от теории — это известно. Программисты, однако, зачастую избегают вредных последствий этого тезиса, т.к. в первую очередь учатся на практике, а уж потом читают книжки), если вообще читают. В общем, я хочу рассмотреть один интересный факт, заявленный автором книги «Professional JavaScript for Web Developers» (Zakas N. C.):

Одна из общеизвестных проблем ECMAScript — это производительность конкатенации строк. Так же как и некоторые другие языки, ECMAScript поддерживает строки неизменяемыми, то есть их значения не могут быть изменены. Рассмотрим следующий код:

var str = "hello ";
str += "world";

Код выполнит следующие шаги, невидимые для разработчика:

1. Создание строки для хранения "hello ".
2. Создание строки для хранения "world".
3. Создание строки для хранения результата конкатенации.
4. Копирование текущего содержания str в результат.
5. Копирование "world" в результат.
6. Обновление str результирующим значением.

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

var arr  = new Array;
arr[0] = "hello ";
arr[1] = "world";
var str = arr.join("");

В этом случае выполняются следующие шаги:

1. Создание строки для хранения результата.
2. Копирование каждой строки в соответствующее место в результате.

На основании этого автор предлагает следующее масштабируемуе решение проблемы производительности конкатенации строк:

function StringBuffer() {
    this.__strings__ = new Array;
}
StringBuffer.prototype.append = function (str) {
    this.__strings__.push(str);
};
StringBuffer.prototype.toString = function () {
    return this.__strings__.join("");
};

И как явный пример использования:

var buffer = new StringBuffer();
var d1 = new Date();
var str = "";
for (var i=0; i < 10000; i++) {
    str += "text";
}
var d2 = new Date();
document.write("Concatenation with plus: " + (d2.getTime() - d1.getTime()) + " milliseconds");
var oBuffer = new StringBuffer();
d1 = new Date();
for (var i=0; i < 10000; i++) {
    oBuffer.append("text");
}
var sResult = buffer.toString();
d2 = new Date();
document.write("< br / >Concatenation with StringBuffer: " + (d2.getTime() -
d1.getTime()) + " milliseconds");


При этом он заявил, что тест должен показать экономию порядка 100—200% процентов над использованием оператора сложения +=.

Меня очень впечатлило данное утверждение и я решил его проверить. Результат в Firefox 3 удивил: 5 и 7 миллисекунд соответственно, т.е. с оператором конкатенации, который был явно унижен автором, скрипт выполнялся быстрее. Для сбора статистики число проходов в цикле было увеличено на два порядка. Результат теста в разных браузерах смотрим ниже:

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

Вывод получился очень простым: производительность оператора += кроссбраузерно не повышается выше описанным трюком. А в некоторых браузерах этот прием даже вреден.

Параметры ПО при проведении теста: Windows XP SP3, IE v.6, v.7, Opera v9,6, Mozilla v.3

Иван Котов 6 декабря 2008

© Все права на данную статью принадлежат порталу fastcoder.org. Перепечатка в интернет-изданиях разрешается только с указанием автора и прямой ссылки на оригинальную статью. Перепечатка в печатных изданиях допускается только с разрешения редакции.

Комментарии

Bur 9 декабря 2008, 12:19 #
Частично соглашусь с автором. Повторив аналогичные эксперименты в разных браузерах (Opera 9, FF2+, Safari 3, Chrome 0.2) не заметил существенного выйгрыша от предлагаемого Закасом способа. Как правило наоборот, обычная конкатенация работает быстрее.

Совсем по-другому дела обстоят в ИЕ. Поэкспериментровав в ИЕ7 выяснил, что "свежеоткрытым" он ведет себя вполне достойно в обоих случаях (см. изображение ниже). А вот если понаоткрывать в нем вкладок с нагруженными приложениями (Gmail, GDocs, пару ютьюб-страниц и т.д.) тем самым прилично забив память, то конкатенация += начинает сильно тормозить. Время выполнения цикла в 3000 итераций составляет более 11 секунд (Vista, 2Гб RAM, Pentium Dual 2.12ГГц):

IE-тесты на производительность конкатенации

Аналогичных проблем в других браузерах не наблюдается.
Таким образом, сам собой напрашивается вывод либо повсеметно использовать способ Закаса, либо использовать +=, но с проверкой браузера, что будет более трудоемким для разработчика и не факт, что оправданным.

Да и сам способ Закаса кажется громоздким. Ведь всегда проще и понятнее сделать так:
var result=[], i;
for (i = 0; i < 1000; i++) {
     result[result.length] = i; // push не работает в ИЕ5.5 и ниже
}
result = result.join('');
 
1234ru 6 февраля 2009, 17:00 #
Да уж, как-то грустно.. Получается, нельзя использовать встроенные средства языка.
Хотя описанный прием повышает производительность, но также и усложняет код, снизит его читаемость.
В то же время, ведь, JavaScript - клиентский язык, проблемы с производительностью на клиентских машинах обычно стоят не так остро, как для серверных языко.
Оправдано ли всё это?
 
Bur 6 февраля 2009, 19:26 #
Реализация JavaScript, как клиентского языка, напрямую зависит от клиента. Иначе не было бы проблем с кроссбраузерностью кода.
Этот прием не то что оправдан, он иногда просто необходим, т.к. прямая контатенация в большом цикле вешает браузер, что очень неприятно для пользователя. О проценте Internet Explorer среди используемых браузеров можно не говорить :-)
 
1234ru 6 февраля 2009, 21:45 #
Ну, из вышенаписанного я понял, что в большом цикле вешает.
Вопрос в том, как часто бывает настолько большой цикл - 1000 и более. В реальной JavaScript-жизни часто ли такое бывает?
 
1234ru 6 февраля 2009, 21:45 #
Да, еще вот что хотел спросить: ECMAScript - это что такое?
JavaScript - частный случай ECMAScript?
 
ivn 30 июля 2009, 13:49 #
ECMAScript (European Computer Manufacturers Association) есть в некотором смысле основа для реализации JavaScript в браузерах. В этом смысле JS можно поделить на ECMAScript, DOM (The Document Object Model) и BOM (The Browser Object Model). Эта основа определяет следующие возможности языка: Syntax, Types, Statements, Keywords, Reserved Words, Operators, Objects.
 
 
Rambler's Top100 Flede HTML valid CSS valid