![]() | ||||||||||||||||||||||||||||||
![]() |
![]() |
|
|
|||||||||||||||||||||||||||
![]() |
|
Производительность простых и сложных конструкций в JavaScript (исходники)Источник: habrahabr
Периодически натыкаясь на статьи, посвященные оптимизации кода на JS (вот одна из популярных) я ловил себя на мысли, что информации в них катастрофически мало. Перечислены 2-3 конструкции, 1-2 браузера и все на этом.
Как говорится, если хочешь сделать что-то хорошо, сделай это сам. Ну и раз уж результаты получены, почему бы выложить их для всеобщего пользования? ВведениеИтак, машина для тестирования - P4 3GHz (двухядерный), 1.5RAM, Vista 32-bit. Набор браузеров - FF3, Opera 9.62, Chrom 1.0, IE7, IE8b2 Скажу сразу, что IE8 - не оригинальный, а запущенный через программу IETester Если кто-то хочет протестировать IE6 или любой другой браузер - собственно, welcome )
Методология тестированияВсе нижеописанные языковые конструкции тестировались путем повторения 1 миллион раз в цикле. Данное действие повторялось несколько раз в каждом браузере, дабы выявить некий средний результат (он обычно колеблется в пределах +-2-5% в зависимости от текущей загрузки системы). Средний результат (в миллисекундах) записывался в таблицу. Везде (где явно не указано другое) использовался инвертированный цикл for(var i=1000000; i--;) как наиболее быстрый.
Переходим к делуЦиклы
Итак, для начала я решил протестировать сами циклы (просто пустые циклы без выполняемого кода внутри). Т.к. сказать основу нашего тестирования ) Про циклы уже было написано довольно много, так что результаты вполне предсказуемы. Как видно, инвертированный цикл for(var i=1000000; i--;) как минимум вдвое быстрее классического for(var i=0; i<1000000; i++), поэтому именно он использовался в дальнейшем тестировании. Также можно заметить что while цикл практически не отличается по скорости от инвертированного for, видимо потому что это фактически одна и та же конструкция (по логике работы).
Работа с массивами и объектами
Эта часть посвящена работе с массивами и объектами-заменителями. Под объектом-заменителем в данном тесте имеется ввиду объект (типа Object), использующийся исключительно как замена массиву (т.е. не содержащий никаких свойств, кроме элементов как-бы массива). Интересовала скорость работы таких заменителей. Массив (или объект) 1M - имеется ввиду массив (или его заменитель) с кол-вом элементом равным 1000000, ну и индексами (ключами) соответственно от нуля до миллиона (точнее, 999999). В первом тесте мы полностью перебираем такой массив в инвертированном цикле, т.е. примерно вот так: Во втором тесте мы также делаем миллион итераций цикла, но на каждом шаге получаем одно и то же значение из маленького массива (размером 100 элементов). Этот тест я сделал для проверки того, насколько размер массива влияет на скорость получения из него значений. В третьем тесте делается полный for-in цикл по объекту-заменителю массива. Четвертый тест - полный аналог первого теста, кроме того что big_array является не массивом а объектом. Также стоит заметить, что если перед нами стоит задача выбрать диапазон элементов массива то использование метода array.slice() в любом браузере в несколько раз быстрее, чем просто перебор массива в цикле с выборкой нужных элементов по условию. Разница настолько очевидна, что я даже не стал включать это в тест. Помимо перебора массивов, часто приходится их заполнять, поэтому следующие 5 тестов как раз об этом.
Функции, объекты, переменные
В первую очередь, я протестировал вызов пустой функции в цикле. В большинстве браузеров вызов функции является довольно дешевой операцией (на миллион вызовов всего несколько сотен добавленных миллисекунд, по сравнению с полностью пустым циклом). Отмечу, что медленный вызов функции как таковой отрицательно влияет и на другие тесты, связанные с вызовом функций (например, создание объекта или вызов метода), поэтому время их выполнения в IE еще сильнее увеличивается. Следующие 2 теста посвящены созданию миллиона объектов, в одном случае все методы создаются через конструктор, в другом - через прототип. Три следующих теста вызывают свойство объекта: собственное свойство (созданное через this.prop=value), свойство прототипа и приватное свойство (созданное через замыкание из функции конструктора). Очевидно, что последний вариант получаем через геттер. Далее следует 3 теста, вызывающих инкрементный метод объекта (т.е. метод, которая при каждом вызове увеличивает свойство объекта на единицу). Собственно, разница здесь опять же в том, как именно данное свойство было создано (т.е. тестируется скорость доступа к свойствам объекта из его же методов).
Ветви
Первые два теста в этой таблице я думаю комментировать не нужно, на третьем остановимся поподробнее. При таких простых действиях хэш показывает худшие результаты по сравнению со switch (что обусловлено необходимостью вызова функции). Однако, если вы из switch все равно собираетесь вызывать функции, то хэш возможно будет быстрее.
Общий вывод по браузерамОпять же все предсказуемо. Chrome уверенно лидирует с отрывом в несколько парсеков, огнелис и опера делят друг с другом второе и третье место (в разных тестах по разному), ослик уныло плетется в конце, однако бета-ослик подает некоторые надежды на то, что пациент скорее жив, чем мертв.
ЗаключениеПервое, на что хотелось бы обратить внимание - все вышеприведенные тесты являются синтетическими, причем тестируется нагрузка значительно превышающая обычную для веб-приложениях (не так уж и часто приходится иметь дело с массивами по миллиону значений, например). Следовательно, относиться к ним стоит именно как к синтетическим тестам со специально завышенной нагрузкой - т.е. осознавать, что из этого важно для вашего конкретного скрипта, а что не принципиально. С другой стороны, тот метод, который сегодня вызывается сто раз, завтра может вызываться уже десять тысяч раз, так что совсем уж махать рукой на это дело не стоит. И если что-то где-то начало тормозить, будет хотя бы понимание, в какую сторону следует "копать" ) Ссылки по теме
|
|