Продуктивність простих і складних конструкцій в JavaScript (исходники), Різне, Програмування, статті

Періодично натикаючись на статті, присвячені оптимізації коду на JS (ось одна з популярних) я ловив себе на думці, що інформації в них катастрофічно мало. Перераховано 2-3 конструкції, 1-2 браузера і все на цьому.

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

Ну і якщо вже результати отримані, чому б викласти їх для загального користування?


Введення


Отже, машина для тестування – P4 3GHz (двоядерний), 1.5RAM, Vista 32-bit.
Набір браузерів – FF3, Opera 9.62, Chrom 1.0, IE7, IE8b2

Скажу відразу, що IE8 – не оригінальний, а запущений через програму IETester
Наскільки він відповідає оригінальному я не знаю, але принаймні від IE7 результати відрізняються досить сильно, причому в різні сторони.

Якщо хтось хоче протестувати IE6 або будь-який інший браузер – власне, welcome)


Методологія тестування


Всі нижчеописані мовні конструкції тестувалися шляхом повторення 1000000 раз в циклі. Дана дія повторювалося кілька разів в кожному браузері, щоб виявити якийсь середній результат (він зазвичай коливається в межах + -2-5% залежно від поточної завантаження системи). Середній результат (в мілісекундах) записувався в таблицю.

Скрізь (де явно не вказано інше) використовувався інвертований цикл for (var i = 1000000; i – 😉 як найбільш швидкий.


Переходимо до справи


Цикли

































Тести / Браузери  FF3 Opera Chrome IE7 IE8b2
Цикли 
Класичний цикл 71 100 16 238 218
Інвертований цикл 34 34 7 70 118
while-цикл 30 33 7 70 118

Отже, для початку я вирішив протестувати самі цикли (просто порожні цикли без виконуваного коду всередині). Т.к. сказати основу нашого тестування)
Про цикли вже було написано досить багато, так що результати цілком передбачувані.
Як видно, інвертований цикл for (var i = 1000000; i – 😉 як мінімум удвічі швидше класичного for (var i = 0; i <1000000; i + +), тому саме він використовувався в подальшому тестуванні.
Також можна відмітити що while цикл практично не відрізняється за швидкістю від інвертованого for, мабуть тому що це фактично одна й та ж конструкція (по логіці роботи).


Робота з масивами та об’єктами













































































Тести / Браузери  FF3 Opera Chrome IE7 IE8b2
Перебір масивів та об’єктів 
Отримання значень великого масиву (1M) за індексом (повний перебір у зворотному порядку) 430 170 18 790 1020
Отримання значення маленького масиву (100) за індексом 124 146 18 428 515
For-in цикл по об’єкту (1M) 2020 2160 385 39400 35400
Перебір об’єкта (1M) через інвертований цикл 390 170 18 745 746
Заповнення масивів та об’єктів 
Заповнення (1M) масиву через array.length 190 485 82 2640 865
Заповнення (1M) масиву в прямому порядку через значення кроку циклу (класичний цикл) 200 432 75 2500 760
Заповнення (1M) масиву в зворотному порядку через значення кроку циклу (інвертований цикл) 1180 310 124 2270 2260
Заповнення (1M) масиву через push () 176 1120 98 4450 1186
Заповнення об’єкта (1M) в прямому порядку через значення кроку циклу (класичний цикл) 1080 368 74 2400 2205

Ця частина присвячена роботі з масивами і об’єктами-замінниками. Під об’єктом-замінником в даному тесті мається на увазі об’єкт (типу Object), що використовується виключно як заміна масиву (тобто не містить ніяких властивостей, крім елементів як-би масиву). Цікавила швидкість роботи таких замінників.

Масив (або об’єкт) 1M – мається на увазі масив (або його замінник) з кол-вом елементом рівним 1000000, ну і індексами (ключами) відповідно від нуля до мільйона (точніше, 999999).

У першому тесті ми повністю перебираємо такий масив в інвертованому циклі, тобто приблизно ось так:
var a=0;
for(var i=1000000; i–;) {a=big_array[i];}

У другому тесті ми також робимо мільйон ітерацій циклу, але на кожному кроці отримуємо одне і те ж значення з маленького масиву (розміром 100 елементів). Цей тест я зробив для перевірки того, наскільки розмір масиву впливає на швидкість отримання з нього значень.
Як показав тест, у всіх браузерах крім Chrome час пошуку значення в масиві може значно зрости з ростом кількості його елементів.

У третьому тесті робиться повний for-in цикл по об’єкту-заміннику масиву.
Добре видно що цикл for-in є дуже повільним у всіх браузерах (у порівнянні з інвертованим for), і ДУЖЕ повільним в IE для великих обсягів даних. Ніколи не використовуйте його для перебору масивів або об’єктів, де можна обійтися циклом for (тобто ключі числові і впорядковані).

Четвертий тест – повний аналог першого тесту, крім того що big_array є не масивом а об’єктом.
Помітно, що швидкість перебору через цикл слабо залежить від того, масив у нас або об’єкт-замінник. Відчутна різниця тільки в IE8, але це ще бета, так що все може змінитися.

Також варто зауважити, що якщо перед нами стоїть завдання вибрати діапазон елементів масиву то використання методу array.slice () в будь-якому браузері в кілька разів швидше, ніж просто перебір масиву в циклі з вибіркою потрібних елементів за умовою. Різниця настільки очевидна, що я навіть не став включати це в тест.

Крім перебору масивів, часто доводиться їх заповнювати, тому наступні 5 тестів якраз про це.
В принципі, з таблиці все має бути зрозуміло:



Функції, об’єкти, змінні











































































Функції, об’єкти, змінні 
Тести / Браузери  FF3 Opera Chrome IE7 IE8b2
Виклик порожній функції з передачею їй поточного значення циклу 129 270 17 3100 860
Створення об’єкта (створення 2-х методів і одного властивості через конструктор) 2460 1900 593 18600 11700
Створення об’єкта (створення 2-х методів з прототипу і одного властивості через конструктор) 1260 636 64 7830 4210
Отримання властивості об’єкта (власне властивість) 84 142 16 406 412
Отримання властивості об’єкта (властивість прототипу) 90 147 29 474 474
Отримання властивості об’єкта (метод-геттер приватного var властивості) 260 354 33 3430 1160
Виклик інкрементного методу об’єкта (збільшує власну властивість через this) 326 460 60 3810 1520
Виклик інкрементного методу об’єкта (збільшує власну властивість через явне вказівку об’єкта) 356 520 65 3985 1633
Виклик інкрементного методу об’єкта (збільшує приватне var властивість) 412 370 38 3530 1320

У першу чергу, я протестував виклик порожній функції в циклі.
var f=function(){}
for(var i=1000000; i–;) {f();}

В більшості браузерів виклик функції є досить дешевою операцією (на мільйон викликів всього кілька сотень доданих мілісекунд, в порівнянні з повністю порожнім циклом).
Однак IE, як завжди, підносить неприємні сюрпризи. Любителям обертати код в безліч функцій-обгорток буде над чим подумати)

Зазначу, що повільний виклик функції як такої негативно впливає і на інші тести, пов’язані з викликом функцій (наприклад, створення об’єкта або виклик методу), тому час їх виконання в IE ще сильніше збільшується.

Наступні 2 тесту присвячені створенню мільйона об’єктів, в одному випадку всі методи створюються через конструктор, в іншому – через прототип.
Різниця, я думаю, очевидна.

Три наступних тесту викликають властивість об’єкта: власне властивість (створене через this.prop = value), властивість прототипу і приватне властивість (створене через замикання з функції конструктора). Очевидно, що останній варіант отримуємо через геттер.
Результат, загалом, передбачуваний – власне властивість об’єкта можна отримати значно швидше.

Далі слід 3 тіста, що викликають інкрементний метод об’єкта (тобто метод, що при кожному виклику збільшує властивість об’єкта на одиницю). Власне, різниця тут знову ж таки в тому, як саме дане властивість було створено (тобто тестується швидкість доступу до властивостей об’єкта з його ж методів).
Наочно видно, що в даному випадку швидкість зміни приватного властивості вище скрізь, крім Firefox, проте потрібно пам’ятати, що така властивість є загальним для всіх однотипних об’єктів і має найгірше час читання зовні (через геттер).


Гілки

































Гілки 
Тести / Браузери  FF3 Opera Chrome IE7 IE8b2
Вибір гілки з 8 можливих через if 800 500 60 1500 1460
Вибір гілки з 8 можливих через switch 315 334 54 868 1039
Вибір гілки з 8 можливих через хеш функцій 620 400 86 4520 1820

Перші два тести в цій таблиці я думаю коментувати не потрібно, на третьому зупинимося детальніше.
Хеш функцій – це об’єкт, емулює своєю поведінкою switch.
Наприклад, у нас є такий шматок коду:
switch(a) {
case 0: b=6+2; break;
case 1: b=8*3; break;
}
Тоді хеш функцій буде виглядати так:
hash={
`0`: function() {return 6+2;},
`1`: function() {return 8*3;}
}
І використовуватися так b = hash [a] ();

При таких простих діях хеш показує гірші результати в порівнянні зі switch (що обумовлено необхідністю виклику функції). Однак, якщо ви з switch все одно збираєтеся викликати функції, то хеш можливо буде швидше.


Загальний висновок по браузерам


Знову ж все передбачувано. Chrome впевнено лідирує з відривом у кілька парсеків, огнеліс і опера ділять між собою друге та третє місце (у різних тестах по різному), ослик понуро плентається в кінці, проте бета-ослик подає деякі надії на те, що пацієнт швидше живий, ніж мертвий.


Висновок


Перше, на що хотілося б звернути увагу – всі вищенаведені тести є синтетичними, причому тестується навантаження значно перевищує звичайну для веб-додатках (не так вже й часто доводиться мати справу з масивами по мільйону значень, наприклад).
Отже, ставитися до них стоїть саме як до синтетичних тестів із спеціально завищеною навантаженням – тобто усвідомлювати, що з цього важливо для вашого конкретного скрипта, а що не принципово.

З іншого боку, той метод, який сьогодні викликається сто разів, завтра може викликатися вже десять тисяч разів, так що зовсім вже махати рукою на цю справу не варто. І якщо щось десь початок гальмувати, буде хоча б розуміння, в який бік слід “копати”)

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*