คำตอบของ broofa เรียบสวยแน่นอน - น่าประทับใจฉลาดจริง ๆ ... rfc4122 เป็นไปตามมาตรฐานอ่านได้ค่อนข้างกะทัดรัด ! น่ากลัว
แต่ถ้าคุณดูที่นิพจน์ทั่วไปการreplace()
โทรกลับจำนวนมากtoString()
และMath.random()
การเรียกฟังก์ชัน (ซึ่งเขาใช้เพียง 4 บิตของผลลัพธ์และการสูญเสียที่เหลือ) คุณอาจเริ่มสงสัยเกี่ยวกับประสิทธิภาพ แท้จริง joelpt ตัดสินใจแม้จะโยนออก RFC สำหรับความเร็ว GUID generateQuickGUID
ทั่วไปด้วย
แต่เราสามารถรับความเร็วและการปฏิบัติตาม RFC ได้หรือไม่ ฉันบอกว่าใช่! เราสามารถอ่านได้หรือไม่? ดี ... ไม่จริง แต่ง่ายถ้าคุณทำตาม
แต่ก่อนอื่นผลลัพธ์ของฉันเทียบกับ broofa guid
(คำตอบที่ยอมรับ) และไม่สอดคล้องกับ 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.
ดังนั้นโดยย้ำ 6 ของฉันเพิ่มประสิทธิภาพฉันชนะคำตอบที่ได้รับความนิยมมากที่สุดโดยกว่า12Xคำตอบที่ได้รับการยอมรับโดยกว่า9Xและคำตอบที่รวดเร็วไม่สอดคล้องโดย2-3x และฉันยังเป็นไปตาม rfc4122
สนใจอย่างไร ฉันใส่แหล่งข้อมูลเต็มรูปแบบในhttp://jsfiddle.net/jcward/7hyaC/3/และในhttp://jsperf.com/uuid-generator-opt/4
สำหรับคำอธิบายเริ่มต้นด้วยรหัสของ 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())
ดังนั้นแทนที่x
ด้วยเลขฐานสิบหกแบบสุ่มใด ๆ ที่y
มีข้อมูลแบบสุ่ม (ยกเว้นบังคับให้บิต 2 อันดับแรก10
เป็นข้อมูลจำเพาะ RFC) และ regex ไม่ตรงกับ-
หรือ4
อักขระดังนั้นเขาจึงไม่ต้องจัดการกับพวกเขา ลื่นมาก ๆ
สิ่งแรกที่ต้องทราบคือการเรียกใช้ฟังก์ชันมีราคาแพงเช่นเดียวกับการแสดงผลปกติ (แม้ว่าเขาจะใช้เพียง 1 แต่ก็มี 32 การเรียกกลับหนึ่งรายการต่อการแข่งขันแต่ละครั้งและในการเรียกกลับ 32 ครั้งแต่ละครั้ง toString (16))
ขั้นตอนแรกสู่ประสิทธิภาพคือการกำจัด RegEx และฟังก์ชันการเรียกกลับและใช้การวนซ้ำอย่างง่ายแทน ซึ่งหมายความว่าเราต้องจัดการกับ-
และ4
ตัวละครในขณะที่ broofa ไม่ได้ นอกจากนี้โปรดทราบว่าเราสามารถใช้การจัดทำดัชนีสตริงอาเรย์เพื่อเก็บสถาปัตยกรรมเทมเพลตสตริงของเขา:
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())
โดยพื้นฐานแล้วตรรกะภายในเดียวกันยกเว้นที่เราตรวจสอบ-
หรือ4
และการใช้ลูป while (แทนการreplace()
โทรกลับ) ทำให้เรามีการปรับปรุงเกือบ 3 เท่า!
ขั้นตอนต่อไปคือขั้นตอนเล็ก ๆ บนเดสก์ท็อป แต่สร้างความแตกต่างที่เหมาะสมบนมือถือ ลองโทร Math.random () น้อยลงและใช้บิตสุ่มเหล่านั้นทั้งหมดแทนที่จะทิ้ง 87% ของพวกมันออกไปพร้อมกับบัฟเฟอร์แบบสุ่มที่ได้รับการเลื่อนออกไปซ้ำแต่ละครั้ง ลองย้ายนิยามเทมเพลตนั้นออกจากลูปด้วยในกรณีที่มันช่วย:
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())
สิ่งนี้ช่วยให้เราประหยัด 10-30% ขึ้นอยู่กับแพลตฟอร์ม ไม่เลว. แต่ขั้นตอนสำคัญต่อไปคือกำจัดฟังก์ชั่น toString ไปพร้อม ๆ กับการเพิ่มประสิทธิภาพแบบคลาสสิก - ตารางค้นหา ตารางค้นหา 16 องค์ประกอบง่าย ๆ จะทำงานของ toString (16) ในเวลาที่น้อยลง:
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())
การเพิ่มประสิทธิภาพต่อไปเป็นคลาสสิกอื่น เนื่องจากเราจัดการเอาต์พุต 4 บิตในการวนซ้ำแต่ละครั้งเท่านั้นให้ตัดจำนวนลูปครึ่งหนึ่งและประมวลผล 8 บิตแต่ละรอบซ้ำ นี่เป็นเรื่องยากเนื่องจากเรายังต้องจัดการกับตำแหน่งบิตที่สอดคล้องกับ RFC แต่ก็ไม่ยากเกินไป จากนั้นเราต้องสร้างตารางการค้นหาที่ใหญ่ขึ้น (16x16 หรือ 256) เพื่อจัดเก็บ 0x00 - 0xff และเราสร้างเพียงครั้งเดียวนอกฟังก์ชั่น 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())
ฉันลองใช้ e6 () ที่ประมวลผลครั้งละ 16 บิตยังคงใช้ LUT 256- องค์ประกอบและมันแสดงให้เห็นถึงผลตอบแทนที่ลดลงของการปรับให้เหมาะสม แม้ว่ามันจะมีการวนซ้ำน้อยลง แต่ตรรกะภายในนั้นซับซ้อนโดยการประมวลผลที่เพิ่มขึ้นและมันทำงานบนเดสก์ท็อปเหมือนกันและเร็วขึ้นเพียง 10% บนมือถือ
เทคนิคการปรับให้เหมาะสมสุดท้ายที่จะนำไปใช้ - คลี่วง เนื่องจากเราวนซ้ำจำนวนครั้งเราจึงสามารถเขียนสิ่งนี้ออกมาได้ด้วยมือ ฉันลองสิ่งนี้ครั้งเดียวด้วยตัวแปรสุ่ม r เดียวที่ฉันยังคงทำการกำหนดใหม่และประสิทธิภาพของแท็งก์ แต่ด้วยตัวแปรสี่ตัวที่กำหนดข้อมูลแบบสุ่มไว้ล่วงหน้าจากนั้นใช้ตารางการค้นหาและใช้บิต RFC ที่เหมาะสมรุ่นนี้จะสูบบุหรี่ทั้งหมด:
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())
ดัดแปลงแล้ว: http://jcward.com/UUID.js -UUID.generate()
สิ่งที่ตลกคือการสร้างข้อมูลแบบสุ่ม 16 ไบต์เป็นส่วนที่ง่าย เคล็ดลับทั้งหมดแสดงไว้ในรูปแบบ String ที่มีการปฏิบัติตาม RFC และสามารถทำได้อย่างแน่นหนาที่สุดด้วยข้อมูลแบบสุ่ม 16 ไบต์ตารางวนรอบและตารางค้นหา
ฉันหวังว่าตรรกะของฉันถูกต้อง - มันง่ายมากที่จะทำผิดพลาดในงานบิตที่น่าเบื่อแบบนี้ แต่ผลลัพธ์ดูดีสำหรับฉัน ฉันหวังว่าคุณจะสนุกไปกับการเพิ่มประสิทธิภาพของรหัส!
ได้รับคำแนะนำ:เป้าหมายหลักของฉันคือการแสดงและสอนกลยุทธ์การเพิ่มประสิทธิภาพที่มีศักยภาพ คำตอบอื่น ๆ ครอบคลุมหัวข้อที่สำคัญเช่นการชนกันและการสุ่มตัวเลขอย่างแท้จริงซึ่งเป็นสิ่งสำคัญสำหรับการสร้าง UUID ที่ดี