Jawaban broofa cukup apik, memang - sangat pintar, benar-benar ... rfc4122 sesuai, agak mudah dibaca, dan kompak. Luar biasa!
Tetapi jika Anda melihat ekspresi reguler itu, banyak replace()
panggilan balik, toString()
dan Math.random()
panggilan fungsi (di mana dia hanya menggunakan 4 bit hasilnya dan membuang-buang sisanya), Anda mungkin mulai bertanya-tanya tentang kinerja. Memang, joelpt bahkan memutuskan untuk membuang RFC untuk kecepatan GUID generik dengannyagenerateQuickGUID
.
Tapi, bisakah kita mendapatkan kecepatan dan kepatuhan RFC? Saya katakan, YA! Bisakah kita mempertahankan keterbacaan? Ya ... Tidak juga, tetapi mudah jika Anda mengikuti.
Tapi pertama-tama, hasil saya, dibandingkan dengan broofa, guid
(jawaban yang diterima), dan yang tidak sesuai dengan rfc generateQuickGuid
:
Desktop Android
broofa: 1617ms 12869ms
e1: 636ms 5778ms
e2: 606ms 4754ms
e3: 364ms 3003ms
e4: 329ms 2015ms
e5: 147ms 1156ms
e6: 146ms 1035ms
e7: 105ms 726ms
guid: 962ms 10762ms
generateQuickGuid: 292ms 2961ms
- Note: 500k iterations, results will vary by browser/cpu.
Jadi dengan iterasi keenam saya tentang optimisasi, saya mengalahkan jawaban paling populer dengan lebih dari 12X , jawaban yang diterima lebih dari 9X , dan jawaban cepat-tidak-sesuai dengan 2-3X . Dan saya masih rfc4122 compliant.
Tertarik bagaimana? Saya telah memasukkan sumber lengkapnya di http://jsfiddle.net/jcward/7hyaC/3/ dan di http://jsperf.com/uuid-generator-opt/4
Untuk penjelasan, mari kita mulai dengan kode broofa:
function broofa() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
return v.toString(16);
});
}
console.log(broofa())
Jadi itu menggantikan x
dengan digit hex acak, y
dengan data acak (kecuali memaksa 2 bit teratas 10
sesuai dengan spesifikasi RFC), dan regex tidak cocok dengan karakter -
atau 4
, jadi dia tidak harus berurusan dengan mereka. Sangat, sangat apik.
Hal pertama yang perlu diketahui adalah bahwa panggilan fungsi itu mahal, seperti halnya ekspresi reguler (meskipun ia hanya menggunakan 1, ia memiliki 32 panggilan balik, satu untuk setiap pertandingan, dan di masing-masing 32 panggilan balik itu disebut Math.random () dan v. toString (16)).
Langkah pertama menuju kinerja adalah menghilangkan RegEx dan fungsi-fungsi panggilan baliknya dan menggunakan loop sederhana sebagai gantinya. Ini berarti kita harus berurusan dengan -
dan 4
karakter sedangkan broofa tidak. Juga, perhatikan bahwa kita dapat menggunakan pengindeksan String Array untuk menjaga arsitektur template String yang apik:
function e1() {
var u='',i=0;
while(i++<36) {
var c='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'[i-1],r=Math.random()*16|0,v=c=='x'?r:(r&0x3|0x8);
u+=(c=='-'||c=='4')?c:v.toString(16)
}
return u;
}
console.log(e1())
Pada dasarnya, logika batin yang sama, kecuali kami memeriksa -
atau 4
, dan menggunakan loop sementara (bukanreplace()
panggilan balik) memberi kami peningkatan hampir 3X!
Langkah selanjutnya adalah langkah kecil di desktop tetapi membuat perbedaan yang layak di ponsel. Mari kita membuat lebih sedikit panggilan Math.random () dan memanfaatkan semua bit acak itu alih-alih membuang 87% dari mereka dengan buffer acak yang digeser keluar setiap iterasi. Mari kita juga memindahkan definisi template dari loop, untuk berjaga-jaga jika itu membantu:
function e2() {
var u='',m='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx',i=0,rb=Math.random()*0xffffffff|0;
while(i++<36) {
var c=m[i-1],r=rb&0xf,v=c=='x'?r:(r&0x3|0x8);
u+=(c=='-'||c=='4')?c:v.toString(16);rb=i%8==0?Math.random()*0xffffffff|0:rb>>4
}
return u
}
console.log(e2())
Ini menghemat 10-30% tergantung pada platform. Tidak buruk. Tetapi langkah besar selanjutnya menghilangkan panggilan fungsi toString bersamaan dengan optimasi klasik - tabel pencarian. Tabel pencarian 16-elemen sederhana akan melakukan pekerjaan toString (16) dalam waktu yang jauh lebih singkat:
function e3() {
var h='0123456789abcdef';
var k='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
/* same as e4() below */
}
function e4() {
var h=['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'];
var k=['x','x','x','x','x','x','x','x','-','x','x','x','x','-','4','x','x','x','-','y','x','x','x','-','x','x','x','x','x','x','x','x','x','x','x','x'];
var u='',i=0,rb=Math.random()*0xffffffff|0;
while(i++<36) {
var c=k[i-1],r=rb&0xf,v=c=='x'?r:(r&0x3|0x8);
u+=(c=='-'||c=='4')?c:h[v];rb=i%8==0?Math.random()*0xffffffff|0:rb>>4
}
return u
}
console.log(e4())
Optimasi berikutnya adalah klasik lain. Karena kita hanya menangani 4-bit output di setiap iterasi loop, mari kita memotong jumlah loop menjadi dua dan memproses 8-bit setiap iterasi. Ini rumit karena kita masih harus menangani posisi bit yang sesuai dengan RFC, tetapi itu tidak terlalu sulit. Kami kemudian harus membuat tabel pencarian yang lebih besar (16x16, atau 256) untuk menyimpan 0x00 - 0xff, dan kami membangunnya hanya sekali, di luar fungsi e5 ().
var lut = []; for (var i=0; i<256; i++) { lut[i] = (i<16?'0':'')+(i).toString(16); }
function e5() {
var k=['x','x','x','x','-','x','x','-','4','x','-','y','x','-','x','x','x','x','x','x'];
var u='',i=0,rb=Math.random()*0xffffffff|0;
while(i++<20) {
var c=k[i-1],r=rb&0xff,v=c=='x'?r:(c=='y'?(r&0x3f|0x80):(r&0xf|0x40));
u+=(c=='-')?c:lut[v];rb=i%4==0?Math.random()*0xffffffff|0:rb>>8
}
return u
}
console.log(e5())
Saya mencoba e6 () yang memproses 16-bit pada suatu waktu, masih menggunakan LUT 256-elemen, dan itu menunjukkan hasil optimasi yang semakin berkurang. Meskipun memiliki iterasi yang lebih sedikit, logika bagian dalam diperumit oleh peningkatan pemrosesan, dan ia melakukan hal yang sama pada desktop, dan hanya ~ 10% lebih cepat di ponsel.
Teknik optimalisasi akhir untuk diterapkan - membuka gulungan lingkaran. Karena kita mengulang-ulang beberapa kali, kita secara teknis dapat menulis ini dengan tangan. Saya mencoba ini sekali dengan variabel acak tunggal r yang saya terus menetapkan ulang, dan kinerja mabuk. Tetapi dengan empat variabel ditugaskan data acak di depan, kemudian menggunakan tabel pencarian, dan menerapkan bit RFC yang tepat, versi ini merokok semuanya:
var lut = []; for (var i=0; i<256; i++) { lut[i] = (i<16?'0':'')+(i).toString(16); }
function e7()
{
var d0 = Math.random()*0xffffffff|0;
var d1 = Math.random()*0xffffffff|0;
var d2 = Math.random()*0xffffffff|0;
var d3 = Math.random()*0xffffffff|0;
return lut[d0&0xff]+lut[d0>>8&0xff]+lut[d0>>16&0xff]+lut[d0>>24&0xff]+'-'+
lut[d1&0xff]+lut[d1>>8&0xff]+'-'+lut[d1>>16&0x0f|0x40]+lut[d1>>24&0xff]+'-'+
lut[d2&0x3f|0x80]+lut[d2>>8&0xff]+'-'+lut[d2>>16&0xff]+lut[d2>>24&0xff]+
lut[d3&0xff]+lut[d3>>8&0xff]+lut[d3>>16&0xff]+lut[d3>>24&0xff];
}
console.log(e7())
Dimodernisasi: http://jcward.com/UUID.js -UUID.generate()
Lucunya, menghasilkan 16 byte data acak adalah bagian yang mudah. Seluruh trik mengekspresikannya dalam format String dengan kepatuhan RFC, dan itu paling ketat dicapai dengan 16 byte data acak, loop terbuka dan tabel pencarian.
Saya harap logika saya benar - sangat mudah untuk membuat kesalahan dalam jenis pekerjaan yang membosankan ini. Tapi hasilnya terlihat bagus untuk saya. Saya harap Anda menikmati perjalanan gila ini melalui optimasi kode!
Maklum: tujuan utama saya adalah menunjukkan dan mengajarkan strategi optimasi yang potensial. Jawaban lain mencakup topik-topik penting seperti tabrakan dan angka acak, yang penting untuk menghasilkan UUID yang baik.