Perché arr = [] è più veloce di arr = new Array?


146

Ho eseguito questo codice e ho ottenuto il risultato di seguito. Sono curioso di sapere perché []è più veloce?

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')
  • usando []: 299ms
  • usando new: 363ms

Grazie a Raynos ecco un benchmark di questo codice e qualche altro modo possibile per definire una variabile.

inserisci qui la descrizione dell'immagine


5
Potresti essere interessato a jsperf .
Puntato


Nota la parola chiave new. Questo significa "per favore, sii meno efficiente". Non ha mai senso e richiede che il browser esegua l'istanza normale invece di provare a fare ottimizzazioni.
Beatgammit,

2
@kinakuta no. Entrambi creano nuovi oggetti non uguali. Volevo dire che []equivale a new Array()in termini di codice sorgente, non di oggetti restituiti da espressioni
Raynos,

1
Sì, non è molto importante. Ma mi piace saperlo.
Mohsen,

Risposte:


195

Espandere ulteriormente le risposte precedenti ...

Dal punto di vista dei compilatori generali e ignorando le ottimizzazioni specifiche della VM:

Innanzitutto, passiamo alla fase di analisi lessicale in cui tokenizziamo il codice.

A titolo di esempio, possono essere prodotti i seguenti token:

[]: 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)

Speriamo che questo ti fornisca una visualizzazione sufficiente in modo da poter capire quanto più (o meno) elaborazione è richiesta.

  1. Sulla base dei token precedenti, sappiamo che ARRAY_INIT produrrà sempre un array. Pertanto, semplicemente creiamo un array e lo popoliamo. Per quanto riguarda l'ambiguità, la fase di analisi lessicale ha già distinto ARRAY_INIT da un oggetto accessor (ad esempio obj[foo]) o parentesi all'interno di stringhe / valori letterali regex (ad esempio "foo [] bar" o / [] /)

  2. Questo è minuscolo, ma abbiamo anche più token con new Array. Inoltre, non è ancora del tutto chiaro che vogliamo semplicemente creare un array. Vediamo il "nuovo" token, ma "nuovo" cosa? Vediamo quindi il token IDENTIFIER che significa che vogliamo un nuovo "array", ma le VM JavaScript generalmente non distinguono un token IDENTIFIER e token per "oggetti globali nativi". Perciò...

  3. Dobbiamo cercare la catena di portata ogni volta che incontriamo un token IDENTIFIER. Le macchine virtuali Javascript contengono un "oggetto di attivazione" per ogni contesto di esecuzione che può contenere l'oggetto "argomenti", variabili definite localmente, ecc. Se non riusciamo a trovarlo nell'oggetto di attivazione, iniziamo a cercare la catena dell'ambito fino a raggiungere l'ambito globale . Se non viene trovato nulla, lanciamo a ReferenceError.

  4. Una volta individuata la dichiarazione della variabile, invochiamo il costruttore. new Arrayè una chiamata di funzione implicita e la regola empirica è che le chiamate di funzione sono più lente durante l'esecuzione (quindi perché i compilatori C / C ++ statici consentono "allineamento di funzioni" - che i motori JS JIT come SpiderMonkey devono fare al volo)

  5. Il Arraycostruttore è sovraccarico. Il costruttore di array è implementato come codice nativo, quindi fornisce alcuni miglioramenti delle prestazioni, ma deve comunque verificare la lunghezza degli argomenti e agire di conseguenza. Inoltre, nel caso in cui venga fornito un solo argomento, è necessario verificare ulteriormente il tipo di argomento. new Array ("foo") produce ["foo"] dove come nuovo Array (1) produce [indefinito]

Quindi, per semplificare tutto: con i letterali dell'array, la VM sa che vogliamo un array; con new Array, la VM deve usare cicli di CPU extra per capire cosa fa new Array realmente .


non è a = new Array (1000); for (da 0 a 999) {a [i] = i} più veloce di a = []; for (da 0 a 999) {a [i] = i} a causa di spese generali di allocazione però?
Y. Yoshii il

Ho appena fatto un caso di prova. Il nuovo array (n) è più veloce nei casi in cui si conosce in anticipo la dimensione dell'array jsperf.com/square-braces-vs-new-array
Y. Yoshii

27

Un possibile motivo è che new Arrayrichiede una ricerca del nome Array(è possibile avere una variabile con quel nome nell'ambito), mentre []non lo è.


4
Anche il controllo degli argomenti può contribuire.
Leonid,

Arrayaccetta sia un argomento lenche più argomenti. Dove as []accetta solo più argomenti. Anche i test di Firefox non mostrano quasi alcuna differenza.
Raynos,

Penso che ci sia un po 'di verità in questo. L'esecuzione del loop test dell'OP in un IIFE ha un impatto (relativamente) sostanziale sulle prestazioni. Incluso var Array = window.Arraymigliora le prestazioni del new Arraytest.
user113716

Non penso che sia giusto perché this console.time ('more vars new'); per (var i = 0; i <200000; i ++) {var arr = new Array ()}; console.timeEnd ('more vars new'); more vars new: 390ms e this console.time ('more vars new'); var myOtherObject = {}, myOtherArray = []; per (var i = 0; i <200000; i ++) {var arr = new Array ()}; console.timeEnd ('more vars new'); più nuovo: 369ms Restituisce alla stessa ora
Mohsen

2

Buona domanda. Il primo esempio è chiamato array letterale. È il modo preferito per creare array tra molti sviluppatori. È possibile che la differenza di prestazioni sia causata controllando gli argomenti della nuova chiamata Array () e quindi creando l'oggetto, mentre il valore letterale crea direttamente un array.

La differenza relativamente piccola nelle prestazioni supporta questo punto credo. A proposito, potresti fare lo stesso test con l'oggetto e l'oggetto letterale {}.


1

Questo avrebbe un senso

I letterali di oggetti ci consentono di scrivere codice che supporta molte funzionalità ma che lo rende ancora relativamente semplice per gli implementatori del nostro codice. Non è necessario richiamare direttamente i costruttori o mantenere l'ordine corretto degli argomenti passati alle funzioni, ecc.

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


1

Inoltre, interessante, se la lunghezza dell'array è nota in anticipo (gli elementi verranno aggiunti subito dopo la creazione), l'uso di un costruttore di array con una lunghezza specificata è molto più veloce sul recente Google Chrome 70+.

  • " nuova matrice ( % ARR_LENGTH% ) " - 100% (più veloce) !

  • " [] " - 160-170% (più lento)

Grafico con i risultati delle misure.

Il test può essere trovato qui - https://jsperf.com/small-arr-init-with-known-length-brackets-vs-new-array/2

Nota: questo risultato è stato testato su Google Chrome v.70 + ; in Firefox v.70 e IE entrambe le varianti sono quasi uguali.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.