พวกเขาแทนที่ผู้ติดตามของ TranslatorX64 ด้วยการเป็นตัวแทน HipHop Intermediate (hhir) ใหม่และเลเยอร์ทางอ้อมใหม่ซึ่งอยู่ในตรรกะเพื่อสร้าง hhir ซึ่งจริง ๆ แล้วเรียกตามชื่อเดียวกันว่า hhir
จากระดับสูงมีการใช้ 6 คำสั่งในการทำสิ่งที่จำเป็นต้องใช้ 9 คำสั่งก่อนดังที่ระบุไว้ที่นี่: "มันเริ่มต้นด้วยตัวตรวจสอบชนิดเดียวกัน แต่เนื้อหาของการแปลคือ 6 คำสั่งดีกว่า 9 จาก TranslatorX64 อย่างมีนัยสำคัญ"
ส่วนใหญ่เป็นสิ่งประดิษฐ์ของวิธีการออกแบบระบบและเป็นสิ่งที่เราวางแผนที่จะทำความสะอาดในที่สุด รหัสทั้งหมดที่เหลืออยู่ใน TranslatorX64 นั้นเป็นเครื่องจักรที่จำเป็นในการปล่อยโค้ดและแปลลิงค์ด้วยกัน รหัสที่เข้าใจวิธีการแปลไบต์ส่วนบุคคลนั้นหายไปจาก TranslatorX64
เมื่อ hhir แทนที่ TranslatorX64 มันเป็นการสร้างรหัสที่เร็วขึ้นประมาณ 5% และดูดีขึ้นอย่างมากเมื่อตรวจสอบด้วยตนเอง เราติดตามการเปิดตัวการผลิตด้วย mini-lockdown อีกครั้งและรับเพิ่ม 10% ในด้านประสิทธิภาพที่เพิ่มขึ้นจากนั้น หากต้องการดูการดำเนินการปรับปรุงเหล่านี้ลองดูฟังก์ชั่น addPositive และส่วนหนึ่งของการแปล
function addPositive($arr) {
$n = count($arr);
$sum = 0;
for ($i = 0; $i < $n; $i++) {
$elem = $arr[$i];
if ($elem > 0) {
$sum = $sum + $elem;
}
}
return $sum;
}
ฟังก์ชั่นนี้ดูเหมือนโค้ด PHP จำนวนมาก: มันวนรอบอาร์เรย์และทำบางอย่างกับแต่ละองค์ประกอบ ลองเน้นไปที่บรรทัดที่ 5 และ 6 ในตอนนี้พร้อมกับรหัสไบต์:
$elem = $arr[$i];
if ($elem > 0) {
// line 5
85: CGetM <L:0 EL:3>
98: SetL 4
100: PopC
// line 6
101: Int 0
110: CGetL2 4
112: Gt
113: JmpZ 13 (126)
สองบรรทัดเหล่านี้โหลดองค์ประกอบจากอาร์เรย์เก็บไว้ในตัวแปรท้องถิ่นจากนั้นเปรียบเทียบค่าของโลคัลนั้นด้วย 0 และกระโดดตามเงื่อนไขตามผลลัพธ์ หากคุณสนใจรายละเอียดเพิ่มเติมเกี่ยวกับสิ่งที่เกิดขึ้นใน bytecode คุณสามารถอ่านผ่าน bytecode.specification JIT ทั้งในขณะนี้และย้อนกลับไปใน TranslatorX64 วันแบ่งรหัสนี้ออกเป็นสองชุด: หนึ่งอันมีเพียง CGetM และอีกส่วนหนึ่งพร้อมกับคำแนะนำที่เหลือ (คำอธิบายที่สมบูรณ์ว่าทำไมสิ่งนี้ถึงไม่เกี่ยวข้องกัน ส่วนใหญ่เป็นเพราะเราไม่ทราบว่าในเวลารวบรวมสิ่งที่ประเภทขององค์ประกอบอาร์เรย์จะเป็น) การแปล CGetM ทำให้การโทรไปที่ฟังก์ชันผู้ช่วย C ++ นั้นไม่น่าสนใจมากดังนั้นเราจะดูที่การโยงที่สอง ความมุ่งมั่นนี้คือการเกษียณอย่างเป็นทางการของ TranslatorX64
cmpl $0xa, 0xc(%rbx)
jnz 0x276004b2
cmpl $0xc, -0x44(%rbp)
jnle 0x276004b2
101: SetL 4
103: PopC
movq (%rbx), %rax
movq -0x50(%rbp), %r13
104: Int 0
xor %ecx, %ecx
113: CGetL2 4
mov %rax, %rdx
movl $0xa, -0x44(%rbp)
movq %rax, -0x50(%rbp)
add $0x10, %rbx
cmp %rcx, %rdx
115: Gt
116: JmpZ 13 (129)
jle 0x7608200
สี่บรรทัดแรกเป็นประเภทตรวจสอบว่าค่าใน $ elem และค่าที่ด้านบนของสแต็กเป็นประเภทที่เราคาดหวัง หากสิ่งใดสิ่งหนึ่งล้มเหลวเราจะข้ามไปยังรหัสที่ทำให้เกิดการส่งสัญญาณการสืบค้นกลับโดยใช้ประเภทใหม่เพื่อสร้างรหัสเฉพาะของเครื่องที่แตกต่างกัน เนื้อของการแปลตามมาและรหัสมีพื้นที่มากมายสำหรับการปรับปรุง มีการโหลดที่ตายแล้วในบรรทัดที่ 8 การลงทะเบียนที่หลีกเลี่ยงได้อย่างง่ายดายเพื่อลงทะเบียนการย้ายที่บรรทัดที่ 12 และโอกาสสำหรับการแพร่กระจายอย่างต่อเนื่องระหว่างบรรทัดที่ 10 และ 16 สิ่งเหล่านี้เป็นผลสืบเนื่องมาจากวิธี bytecode ไม่มีคอมไพเลอร์ที่มีหน้ามีตาจะปล่อยโค้ดเช่นนี้ แต่การเพิ่มประสิทธิภาพอย่างง่ายที่จำเป็นในการหลีกเลี่ยงมันก็ไม่เหมาะกับรุ่น TranslatorX64
ตอนนี้เราจะเห็น tracelet เดียวกันที่แปลโดยใช้ hhir ในการแก้ไข hhvm เดียวกัน:
cmpl $0xa, 0xc(%rbx)
jnz 0x276004bf
cmpl $0xc, -0x44(%rbp)
jnle 0x276004bf
101: SetL 4
movq (%rbx), %rcx
movl $0xa, -0x44(%rbp)
movq %rcx, -0x50(%rbp)
115: Gt
116: JmpZ 13 (129)
add $0x10, %rbx
cmp $0x0, %rcx
jle 0x76081c0
มันเริ่มต้นด้วยตัวตรวจสอบชนิดเดียวกัน แต่เนื้อความของการแปลคือ 6 คำสั่งดีกว่า 9 จาก TranslatorX64 อย่างมีนัยสำคัญ โปรดสังเกตว่าไม่มีการโหลดที่ตายแล้วหรือลงทะเบียนเพื่อลงทะเบียนการเคลื่อนไหวและ 0 ทันทีจากไบต์ 0 Inttecode ถูกแพร่กระจายลงไปที่ cmp ในบรรทัดที่ 12 นี่คือ hhir ที่ถูกสร้างขึ้นระหว่าง tracelet และการแปลที่:
(00) DefLabel
(02) t1:FramePtr = DefFP
(03) t2:StkPtr = DefSP<6> t1:FramePtr
(05) t3:StkPtr = GuardStk<Int,0> t2:StkPtr
(06) GuardLoc<Uncounted,4> t1:FramePtr
(11) t4:Int = LdStack<Int,0> t3:StkPtr
(13) StLoc<4> t1:FramePtr, t4:Int
(27) t10:StkPtr = SpillStack t3:StkPtr, 1
(35) SyncABIRegs t1:FramePtr, t10:StkPtr
(36) ReqBindJmpLte<129,121> t4:Int, 0
คำสั่ง bytecode ถูกแบ่งย่อยออกเป็นการดำเนินงานที่เล็กและง่ายขึ้น การดำเนินการหลายอย่างที่ซ่อนอยู่ในพฤติกรรมของบางไบต์จะแสดงอย่างชัดเจนใน hhir เช่น LdStack ในบรรทัดที่ 6 ซึ่งเป็นส่วนหนึ่งของ SetL โดยการใช้ชื่อชั่วคราว (t1, t2, ฯลฯ ... ) แทนการลงทะเบียนทางกายภาพเพื่อเป็นตัวแทนของการไหลของค่าเราสามารถติดตามความหมายและการใช้งานของแต่ละค่า สิ่งนี้ทำให้ดูเล็กน้อยว่าปลายทางของการโหลดนั้นถูกใช้จริงหรือถ้าหนึ่งในอินพุตของคำสั่งนั้นเป็นค่าคงที่จาก 3 ไบต์ที่ผ่านมา สำหรับคำอธิบายที่ละเอียดยิ่งขึ้นว่า hhir คืออะไรและทำงานอย่างไรให้ดูที่ ir.specification
ตัวอย่างนี้แสดงการปรับปรุงเพียงเล็กน้อยที่ทำผ่าน TranslatorX64 การเริ่มใช้งาน hhir กับการผลิตและการเลิกจ้างนักแปล TranslateX64 ในเดือนพฤษภาคม 2556 ถือเป็นความสำเร็จครั้งยิ่งใหญ่ที่เกิดขึ้น แต่มันเป็นเพียงการเริ่มต้น ตั้งแต่นั้นมาเราได้ดำเนินการเพิ่มประสิทธิภาพอื่น ๆ อีกมากมายซึ่งเกือบเป็นไปไม่ได้ใน TranslatorX64 ทำให้ hhvm มีประสิทธิภาพเกือบสองเท่าในกระบวนการ สิ่งสำคัญในความพยายามของเราในการทำให้ hhvm ทำงานบนโปรเซสเซอร์ ARM โดยการแยกและลดจำนวนรหัสเฉพาะสถาปัตยกรรมที่เราจำเป็นต้องนำมาใช้ใหม่ ดูการโพสต์ที่กำลังจะมาถึงที่ทุ่มเทเพื่อพอร์ต ARM ของเราสำหรับรายละเอียดเพิ่มเติม!