Чому arr = [] швидше, ніж arr = новий масив?


146

Я запустив цей код і отримав результат нижче. Мені цікаво дізнатися, чому []швидше?

console.time('using[]')
for(var i=0; i<200000; i++){var arr = []};
console.timeEnd('using[]')

console.time('using new')
for(var i=0; i<200000; i++){var arr = new Array};
console.timeEnd('using new')
  • використання []: 299 мс
  • використання new: 363ms

Завдяки Райносу ось орієнтир цього коду та ще якийсь можливий спосіб визначення змінної.

введіть тут опис зображення


5
Можливо, вас зацікавить jsperf .
Pointy


Зверніть увагу на ключове слово new. Це означає "будьте менш ефективними". Це ніколи не має сенсу, і вимагає від браузера робити звичайну інстанцію замість того, щоб намагатися робити оптимізацію.
beatgammit

2
@kinakuta немає. Вони обидва створюють нові нерівні об'єкти. Я мав на увазі []рівнозначний з new Array()точки зору вихідного коду, а не об'єктів, що повертаються у виразах форм
Raynos

1
Так, це не дуже важливо. Але мені подобається знати.
Мохсен

Відповіді:


195

Подальше розгортання попередніх відповідей ...

З точки зору загальної компіляції та зневаги до оптимізацій, характерних для VM:

Спочатку ми проходимо фазу лексичного аналізу, де ми використовуємо код.

В якості прикладу можуть бути створені такі жетони:

[]: ARRAY_INIT
[1]: ARRAY_INIT (NUMBER)
[1, foo]: ARRAY_INIT (NUMBER, IDENTIFIER)
new Array: NEW, IDENTIFIER
new Array(): NEW, IDENTIFIER, CALL
new Array(5): NEW, IDENTIFIER, CALL (NUMBER)
new Array(5,4): NEW, IDENTIFIER, CALL (NUMBER, NUMBER)
new Array(5, foo): NEW, IDENTIFIER, CALL (NUMBER, IDENTIFIER)

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

  1. Виходячи з вищевказаних лексем, ми знаємо, що ARRAY_INIT завжди створюватиме масив. Тому ми просто створюємо масив і заповнюємо його. Що стосується неоднозначності, етап лексичного аналізу вже відрізняв ARRAY_INIT від аксесуара властивостей об'єкта (наприклад obj[foo]) або дужок всередині рядків / прямовицьких літералів (наприклад, "foo [] bar" або / [] /)

  2. Це незначно, але ми також маємо більше лексем new Array. Крім того, поки не зовсім зрозуміло, що ми просто хочемо створити масив. Ми бачимо "новий" маркер, але "новий" що? Потім ми бачимо маркер IDENTIFIER, який означає, що ми хочемо новий "масив", але JavaScript VM зазвичай не виділяє маркер і лексеми IDENTIFIER для "нативних глобальних об'єктів". Тому ...

  3. Нам потрібно шукати ланцюг області щоразу, коли ми стикаємося з маркером IDENTIFIER. VM Javascript містять "об’єкт активації" для кожного контексту виконання, який може містити об'єкт "аргументи", локально визначені змінні тощо. Якщо ми не можемо знайти його в об'єкті активації, ми починаємо шукати ланцюг області, поки не досягнемо глобальної області застосування . Якщо нічого не знайдемо, ми кидаємо а ReferenceError.

  4. Після того як ми знайшли декларацію змінної, ми викликаємо конструктор. new Array- це неявний виклик функції, і правило роботи полягає в тому, що виклики функцій є повільнішими під час виконання (отже, чому статичні компілятори C / C ++ дозволяють "функцію вбудовування" - що JS JIT-двигуни, такі як SpiderMonkey, повинні робити на ходу)

  5. ArrayКонструктор перевантажений. Конструктор Array реалізований як нативний код, тому він забезпечує деякі покращення продуктивності, але йому все одно потрібно перевірити довжину аргументів і діяти відповідно. Більше того, якщо наводиться лише один аргумент, нам потрібно додатково перевірити тип аргументу. новий масив ("foo") виробляє ["foo"], де як новий масив (1) виробляє [undefined]

Отже, щоб спростити все це: з літералами масиву VM знає, що ми хочемо масив; з new Array, VM потрібно використовувати додаткові цикли процесора, щоб зрозуміти, що new Array насправді робить.


не = новий масив (1000); для (від 0 до 999) {a [i] = i} швидше, ніж a = []; для (від 0 до 999) {a [i] = i} через Виділення накладних, хоча?
Y.

Щойно зробили тестовий випадок. новий масив (n) швидший у тих випадках, коли ви знаєте розмір масиву достроково jsperf.com/square-braces-vs-new-array
Y.

27

Однією з можливих причин є те, що new Arrayпотрібен пошук імені Array(ви можете мати змінну з цим ім'ям в області застосування), тоді як []ні.


4
Перевірка аргументів може також сприяти.
Леонід

Arrayвиключає як один аргумент, так lenі кілька аргументів. Де як []приймає лише кілька аргументів. Також тести на firefox майже не відрізняються.
Райнос

Я думаю, що в цьому є якась правда. Запуск циклу тестування OP в IIFE робить (відносно) істотний вплив на продуктивність. У тому числі var Array = window.Arrayпокращує ефективність new Arrayтесту.
користувач113716

Я не думаю, що це правильно, оскільки це console.time ('більше варіантів нового'); for (var i = 0; i <200000; i ++) {var arr = new Array ()}; console.timeEnd ('більше нових нових'); більше варіантів new: 390ms та цей console.time ('більше vars new'); var myOtherObject = {}, myOtherArray = []; for (var i = 0; i <200000; i ++) {var arr = new Array ()}; console.timeEnd ('більше нових нових'); більше варіантів новий: 369ms Повертається в той же час
Mohsen

2

Гарне питання. Перший приклад називається літералом масиву. Це кращий спосіб створення масивів серед багатьох розробників. Можливо, різниця в продуктивності викликана перевіркою аргументів нового виклику Array (), а потім створення об'єкта, тоді як буквальний створює масив безпосередньо.

Думаю, порівняно невелика різниця у продуктивності. Ви можете зробити той же тест з об'єктом і об'єктом буквалом {} до речі.


1

Це мало б сенс

Літерали об'єктів дозволяють нам писати код, який підтримує безліч функцій, але все ще робить його відносно простим для реалізаторів нашого коду. Не потрібно безпосередньо викликати конструкторів або підтримувати правильний порядок аргументів, переданих функціям тощо.

http://www.dyn-web.com/tutorials/obj_lit.php


1

Також цікаво, якщо довжина масиву відома заздалегідь (елементи будуть додані відразу після створення), використання конструктора масивів із заданою довжиною набагато швидше в останніх Google Chrome 70+.

  • " новий масив ( % ARR_LENGTH% ) " - 100% (швидше) !

  • " [] " - 160-170% (повільніше)

Діаграма з результатами заходів.

Тест можна знайти тут - https://jsperf.com/small-arr-init-with- unknown-length-brackets-vs-new-array/2

Примітка. Цей результат перевірено на Google Chrome v.70 + ; у Firefox v.70 та IE обидва варіанти майже рівні.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.