var toSizeString = (function() {
var KB = 1024.0,
MB = 1024 * KB,
GB = 1024 * MB;
return function(size) {
var gbSize = size / GB,
gbMod = size % GB,
mbSize = gbMod / MB,
mbMod = gbMod % MB,
kbSize = mbMod / KB;
if (Math.floor(gbSize)) {
return gbSize.toFixed(1) + 'GB';
} else if (Math.floor(mbSize)) {
return mbSize.toFixed(1) + 'MB';
} else if (Math.floor(kbSize)) {
return kbSize.toFixed(1) + 'KB';
} else {
return size + 'B';
}
};
})();
Dan fungsi yang lebih cepat: (perhatikan bahwa ia harus selalu menghitung variabel yang sama kb / mb / gb berulang-ulang). Di mana ia mendapatkan kinerja?
function toSizeString (size) {
var KB = 1024.0,
MB = 1024 * KB,
GB = 1024 * MB;
var gbSize = size / GB,
gbMod = size % GB,
mbSize = gbMod / MB,
mbMod = gbMod % MB,
kbSize = mbMod / KB;
if (Math.floor(gbSize)) {
return gbSize.toFixed(1) + 'GB';
} else if (Math.floor(mbSize)) {
return mbSize.toFixed(1) + 'MB';
} else if (Math.floor(kbSize)) {
return kbSize.toFixed(1) + 'KB';
} else {
return size + 'B';
}
};
javascript
performance
Tomy
sumber
sumber
Jawaban:
Mesin JavaScript modern semuanya melakukan kompilasi tepat waktu. Anda tidak dapat membuat anggapan tentang apa yang "harus dibuatnya berulang kali." Perhitungan semacam itu relatif mudah dioptimalkan, dalam kedua kasus tersebut.
Di sisi lain, penutupan variabel konstan bukanlah hal yang biasa Anda targetkan untuk kompilasi JIT. Anda biasanya membuat penutupan ketika Anda ingin dapat mengubah variabel-variabel itu pada permintaan yang berbeda. Anda juga membuat dereference pointer tambahan untuk mengakses variabel-variabel tersebut, seperti perbedaan antara mengakses variabel anggota dan int lokal di OOP.
Situasi seperti inilah yang menyebabkan orang membuang garis "optimasi prematur". Optimalisasi yang mudah sudah dilakukan oleh kompiler.
sumber
Variabelnya murah. Konteks dan rantai lingkup eksekusi mahal.
Ada berbagai jawaban yang pada dasarnya bermuara pada "karena penutupan", dan itu pada dasarnya benar, tetapi masalahnya tidak secara khusus dengan penutupan, itu adalah fakta bahwa Anda memiliki fungsi yang mereferensikan variabel dalam lingkup yang berbeda. Anda akan memiliki masalah yang sama jika ini adalah variabel global di Internet
window
objek, dibandingkan dengan variabel lokal di dalam IIFE. Cobalah dan lihatlah.Jadi dalam fungsi pertama Anda, ketika mesin melihat pernyataan ini:
Itu harus mengambil langkah-langkah berikut:
size
dalam cakupan saat ini. (Menemukannya.)GB
dalam cakupan saat ini. (Tidak ditemukan.)GB
dalam lingkup induk. (Menemukannya.)gbSize
.Langkah 3 jauh lebih mahal daripada hanya mengalokasikan variabel. Selain itu, Anda melakukan ini lima kali , termasuk dua kali untuk keduanya
GB
danMB
. Saya menduga bahwa jika Anda menggunakan ini di awal fungsi (misalnyavar gb = GB
) dan mereferensikan alias, itu sebenarnya akan menghasilkan speedup kecil, meskipun ada juga kemungkinan bahwa beberapa mesin JS sudah melakukan optimasi ini. Dan tentu saja, cara paling efektif untuk mempercepat eksekusi adalah sama sekali tidak melintasi rantai lingkup sama sekali.Perlu diingat bahwa JavaScript tidak seperti bahasa yang dikompilasi, diketik secara statis di mana kompiler menyelesaikan alamat variabel ini pada waktu kompilasi. Mesin JS harus menyelesaikannya dengan nama , dan pencarian ini terjadi saat runtime, setiap saat. Jadi, Anda ingin menghindarinya jika memungkinkan.
Tugas variabel sangat murah dalam JavaScript. Ini mungkin sebenarnya operasi termurah, walaupun saya tidak mendukung pernyataan itu. Meskipun demikian, aman untuk mengatakan bahwa hampir tidak pernah merupakan ide yang baik untuk mencoba menghindari membuat variabel; hampir semua pengoptimalan yang Anda coba lakukan di bidang itu sebenarnya akan membuat segalanya menjadi lebih buruk, dari segi kinerja.
sumber
var a, b, c
kita dapat mengaksesnyab
sebagaiscope[1]
. Semua cakupan diberi nomor, dan jika cakupan ini bersarang sedalam lima cakupan, makab
sepenuhnya ditangani denganenv[5][1]
yang diketahui selama penguraian. Dalam kode asli, cakupan terkait dengan segmen stack. Penutupan lebih rumit karena harus mencadangkan dan menggantienv
*(scope->outer + variable_offset)
untuk akses; setiap tingkat ruang lingkup fungsi tambahan biaya satu dereference pointer tambahan. Sepertinya kami berdua benar :)Satu contoh melibatkan penutupan, yang lain tidak. Menerapkan penutupan agak rumit, karena variabel tertutup tidak berfungsi seperti variabel normal. Ini lebih jelas dalam bahasa tingkat rendah seperti C, tapi saya akan menggunakan JavaScript untuk menggambarkan ini.
Penutupan tidak hanya terdiri dari suatu fungsi, tetapi juga dari semua variabel yang ditutup. Ketika kita ingin menjalankan fungsi itu, kita juga perlu menyediakan semua variabel yang ditutup. Kita bisa memodelkan penutupan dengan fungsi yang menerima objek sebagai argumen pertama yang mewakili variabel tertutup ini:
Perhatikan konvensi pemanggilan yang canggung yang
closure.apply(closure, ...realArgs)
diperlukanDukungan objek bawaan JavaScript memungkinkan untuk menghilangkan
vars
argumen eksplisit , danthis
sebagai gantinya kami menggunakan :Contoh-contoh itu setara dengan kode ini yang benar-benar menggunakan closure:
Dalam contoh terakhir ini, objek hanya digunakan untuk mengelompokkan dua fungsi yang dikembalikan; yang
this
mengikat tidak relevan. Semua perincian untuk membuat penutupan mungkin - meneruskan data tersembunyi ke fungsi aktual, mengubah semua akses ke variabel penutupan untuk mencari dalam data tersembunyi - diurus oleh bahasa.Tetapi penutupan panggilan melibatkan overhead melewatkan data tambahan itu, dan menjalankan penutupan melibatkan overhead pencarian dalam data tambahan - diperburuk oleh lokalitas cache yang buruk dan biasanya penunjuk dereferensi bila dibandingkan dengan variabel biasa - sehingga tidak mengherankan bahwa solusi yang tidak bergantung pada penutupan berkinerja lebih baik. Terutama karena semua penutupan Anda menghemat Anda lakukan adalah beberapa operasi aritmatika yang sangat murah, yang bahkan mungkin dilipat konstan selama parsing.
sumber