Mengapa arr = [] lebih cepat daripada arr = new Array?

146

Saya menjalankan kode ini dan mendapatkan hasil di bawah ini. Saya penasaran ingin tahu mengapa []lebih cepat?

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

Terima kasih kepada Raynos di sini adalah patokan kode ini dan beberapa cara yang lebih mungkin untuk mendefinisikan variabel.

masukkan deskripsi gambar di sini

Mohsen
sumber
5
Anda mungkin tertarik pada jsperf .
Runcing
11
Benchmark
Raynos
Perhatikan kata kunci baru. Ini berarti "harap kurang efisien". Itu tidak pernah masuk akal, dan mengharuskan browser untuk melakukan instantiasi normal alih-alih mencoba melakukan optimasi.
beatgammit
2
@kinakuta no. Keduanya membuat objek baru yang tidak sama. Yang saya maksud []adalah sama new Array()dalam hal kode sumber, bukan objek yang dikembalikan dari ekspresi
Raynos
1
Ya, itu tidak terlalu penting. Tapi saya ingin tahu.
Mohsen

Jawaban:

195

Lebih lanjut memperluas jawaban sebelumnya ...

Dari perspektif kompiler umum dan mengabaikan optimasi spesifik VM:

Pertama, kita melalui fase analisis leksikal di mana kita tokenize kode.

Sebagai contoh, token berikut dapat diproduksi:

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

Mudah-mudahan ini akan memberi Anda visualisasi yang cukup sehingga Anda dapat memahami berapa banyak (atau kurang) pemrosesan yang diperlukan.

  1. Berdasarkan token di atas, kita tahu sebagai fakta ARRAY_INIT akan selalu menghasilkan array. Karena itu kami cukup membuat sebuah array dan mengisinya. Sejauh ambiguitas, tahap analisis leksikal telah membedakan ARRAY_INIT dari accessor properti objek (misalnya obj[foo]) atau tanda kurung di dalam string / regex literal (mis. "Foo [] bar" atau / [] /)

  2. Ini sangat kecil, tetapi kami juga memiliki lebih banyak token new Array. Selain itu, belum sepenuhnya jelas bahwa kami hanya ingin membuat array. Kita melihat token "baru", tetapi "baru" apa? Kami kemudian melihat token IDENTIFIER yang menandakan kami menginginkan "Array" baru, tetapi JavaScript VM pada umumnya tidak membedakan token dan token IDENTIFIER untuk "objek global asli." Karena itu...

  3. Kami harus mencari rantai cakupan setiap kali kami menemukan token IDENTIFIER. Javascript VMs berisi "objek Aktivasi" untuk setiap konteks eksekusi yang mungkin berisi objek "argumen", variabel yang ditentukan secara lokal, dll. Jika kita tidak dapat menemukannya di objek Aktivasi, kita mulai mencari rantai lingkup sampai kita mencapai lingkup global . Jika tidak ada yang ditemukan, kami melempar ReferenceError.

  4. Setelah kami menemukan deklarasi variabel, kami memanggil konstruktor. new Arrayadalah pemanggilan fungsi implisit, dan aturan praktisnya adalah pemanggilan fungsi lebih lambat selama eksekusi (karenanya mengapa kompiler C / C ++ statis memungkinkan "fungsi inlining" - yang harus dilakukan mesin JS JIT seperti SpiderMonkey saat on-the-fly)

  5. The Arraykonstruktor kelebihan beban. Konstruktor Array diimplementasikan sebagai kode asli sehingga memberikan beberapa peningkatan kinerja, tetapi masih perlu memeriksa panjang argumen dan bertindak sesuai. Selain itu, jika hanya ada satu argumen yang disediakan, kita perlu memeriksa lebih lanjut jenis argumen. Array baru ("foo") menghasilkan ["foo"] sedangkan Array baru (1) menghasilkan [tidak terdefinisi]

Jadi untuk menyederhanakan semuanya: dengan literal array, VM tahu kita menginginkan sebuah array; dengan new Array, VM perlu menggunakan siklus CPU tambahan untuk mencari tahu apa yang new Array sebenarnya dilakukan.

Roger Poon
sumber
bukan a = array baru (1000); untuk (dari 0 hingga 999) {a [i] = i} lebih cepat dari a = []; untuk (dari 0 hingga 999) {a [i] = i} karena Alokasi overhead?
Y. Yoshii
Baru saja membuat test case. Array baru (n) lebih cepat dalam kasus di mana Anda mengetahui ukuran array sebelumnya. jsperf.com/square-braces-vs-new-array
Y. Yoshii
27

Salah satu alasan yang mungkin adalah bahwa new Arraymemerlukan pencarian nama pada Array(Anda dapat memiliki variabel dengan nama itu dalam lingkup), sedangkan []tidak.

hammar
sumber
4
Memeriksa argumen dapat berkontribusi juga.
Leonid
Arraykecuali satu argumen lendan beberapa argumen. Dimana []hanya menerima banyak argumen. Juga tes firefox menunjukkan hampir tidak ada perbedaan.
Raynos
Saya pikir ada kebenarannya. Menjalankan tes loop OP dalam IIFE membuat dampak (relatif) substansial pada kinerja. Termasuk var Array = window.Arraymeningkatkan kinerja new Arraytes.
user113716
Saya rasa itu tidak benar karena konsol ini. Waktu ('lebih banyak vars baru'); untuk (var i = 0; i <200000; i ++) {var arr = new Array ()}; console.timeEnd ('more vars new'); lebih banyak vars baru: 390ms dan konsol ini.waktu ('lebih banyak vars baru'); var myOtherObject = {}, myOtherArray = []; untuk (var i = 0; i <200000; i ++) {var arr = new Array ()}; console.timeEnd ('more vars new'); lebih banyak vars baru: 369ms Kembali bersamaan
Mohsen
2

Pertanyaan bagus. Contoh pertama disebut array literal. Ini adalah cara yang disukai untuk membuat array di antara banyak pengembang. Bisa jadi perbedaan kinerja disebabkan oleh memeriksa argumen dari panggilan Array () baru dan kemudian membuat objek, sedangkan literal membuat array secara langsung.

Perbedaan yang relatif kecil dalam kinerja mendukung hal ini, saya pikir. Anda bisa melakukan tes yang sama dengan Object dan objek literal {} dengan cara.

Laurent Zuijdwijk
sumber
1

Ini masuk akal

Objek literal memungkinkan kita untuk menulis kode yang mendukung banyak fitur namun masih membuatnya relatif mudah bagi para pelaksana kode kita. Tidak perlu memanggil konstruktor secara langsung atau mempertahankan urutan argumen yang benar diteruskan ke fungsi, dll.

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

lnguyen55
sumber
1

Juga menarik, jika panjang array diketahui sebelumnya (elemen akan ditambahkan tepat setelah pembuatan), penggunaan konstruktor array dengan panjang tertentu jauh lebih cepat di Google Chrome 70+ baru-baru ini.

  • " Array baru ( % ARR_LENGTH% ) " - 100% (lebih cepat) !

  • " [] " - 160-170% (lebih lambat)

Bagan dengan hasil tindakan.

Tes dapat ditemukan di sini - https://jsperf.com/small-arr-init-with-known-length-brackets-vs-new-array/2

Catatan: hasil ini diuji di Google Chrome v.70 + ; di Firefox v.70 dan IE kedua varian hampir sama.

Oleg Zarevennyi
sumber