उन्होंने ट्रांसलेटरेटर 64 के ट्रेसलेट्स को नए हिपहॉप इंटरमीडिएट रिप्रेजेंटेशन (हिर) के साथ बदल दिया, और एक नई इनडायरेक्शन लेयर जिसमें हिरन जेनरेट करने के लिए लॉजिक रहता है, जिसे वास्तव में उसी नाम से जाना जाता है, हिर।
उच्च स्तर से, यह 6 निर्देशों का उपयोग कर रहा है जो पहले आवश्यक 9 निर्देशों को करने के लिए किया गया था, जैसा कि यहां उल्लेख किया गया है: "यह एक ही टाइपेकिट्स के साथ शुरू होता है लेकिन अनुवाद का शरीर 6 निर्देश है, ट्रांसलेटरएक्स 64 से 9 की तुलना में काफी बेहतर है"
यह ज्यादातर इस बात की एक कलाकृति है कि कैसे सिस्टम को डिज़ाइन किया गया है और कुछ ऐसा है जिसे हम अंततः साफ करने की योजना बनाते हैं। TranslatorX64 में छोड़े गए सभी कोडों को कोड से बाहर निकालने और एक साथ अनुवाद करने के लिए आवश्यक मशीनरी है; कोड जो यह समझता है कि अलग-अलग बायोटेक का अनुवाद कैसे किया जाता है, TranslatorX64 से चला गया है।
जब हिर ने TranslatorX64 को बदल दिया, तो यह कोड उत्पन्न कर रहा था जो लगभग 5% तेज था और मैन्युअल निरीक्षण पर काफी बेहतर था। हमने एक और मिनी-लॉकडाउन के साथ इसके प्रोडक्शन की शुरुआत की और इसके बाद प्रदर्शन में 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.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
पहली चार पंक्तियाँ यह सत्यापित करती हैं कि $ एलएम में मूल्य और स्टैक के शीर्ष पर मूल्य वे प्रकार हैं जिनकी हम अपेक्षा करते हैं। यदि दोनों में से कोई भी विफल हो जाता है, तो हम कोड के लिए कूदेंगे जो ट्रेसलेट के एक प्रतिगमन को ट्रिगर करता है, नए प्रकारों का उपयोग करके मशीन कोड का एक विशेष रूप से विशिष्ट चंक उत्पन्न करता है। अनुवाद का मांस निम्नानुसार है, और कोड में सुधार के लिए बहुत जगह है। लाइन 8 पर एक मृत भार, लाइन 12 पर आसानी से रजिस्टर करने के लिए आसानी से परिहार्य रजिस्टर है, और लाइनों 10 और 16 के बीच निरंतर प्रसार के लिए एक अवसर है। ये सभी ट्रांसलेटर-ए-टाइम-ट्रांसलेटर द्वारा TranslatorX64 द्वारा उपयोग किए जाने वाले परिणाम हैं। कोई भी सम्मानजनक संकलक कभी भी इस तरह के कोड का उत्सर्जन नहीं करेगा, लेकिन इससे बचने के लिए आवश्यक सरल अनुकूलन केवल ट्रांसलेटरएक्स 64 मॉडल में फिट नहीं होते हैं।
अब एक ही hhvm संशोधन में hir का उपयोग करके अनुवादित एक ही ट्रेसलेट देखें:
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 निर्देश है, ट्रांसलेटरएक्स 64 से 9 की तुलना में काफी बेहतर है। ध्यान दें कि चालों को दर्ज करने के लिए कोई मृत भार या रजिस्टर नहीं हैं, और Int 0 बाइटकोड से तत्काल 0 को लाइन 12 पर सीएमपी तक प्रचारित किया गया था। यहां वह हिरन है जो ट्रेसलेट और उस अनुवाद के बीच उत्पन्न हुआ था:
(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
बाइटकोड निर्देश छोटे, सरल ऑपरेशन में टूट गए हैं। कुछ बाइटकोड के व्यवहार में छिपे हुए कई ऑपरेशनों को स्पष्ट रूप से hir में दर्शाया गया है, जैसे कि लाइन 6 पर LdStack जो कि SetL का हिस्सा है। मानों के प्रवाह का प्रतिनिधित्व करने के लिए भौतिक रजिस्टरों के बजाय अनाम अस्थायी (t1, t2, आदि) का उपयोग करके, हम आसानी से प्रत्येक मान की परिभाषा और उपयोग (ओं) को ट्रैक कर सकते हैं। यह यह देखने के लिए तुच्छ बनाता है कि क्या लोड का गंतव्य वास्तव में उपयोग किया जाता है, या यदि किसी निर्देश के इनपुट में से एक 3 बाइटकोड से एक निरंतर मूल्य है। हिर क्या है और यह कैसे काम करता है, इसकी अधिक गहन व्याख्या के लिए ir.specification पर एक नज़र डालें।
इस उदाहरण ने TranslatorX64 पर किए गए कुछ सुधारों को दिखाया। मई 2013 में TranslatorX64 को प्रोडक्शन और रिटायर करने के लिए hir मिलना, हिट करने के लिए एक बेहतरीन मील का पत्थर था, लेकिन यह सिर्फ शुरुआत थी। तब से, हमने कई और अनुकूलन किए हैं जो लगभग TranslatorX64 में असंभव होगा, इस प्रक्रिया में लगभग दो बार hhvm को सक्षम बनाया गया है। यह हमारे प्रयासों में भी महत्वपूर्ण रहा है कि एआरएम प्रोसेसर पर एचएचवीएम को अलग-थलग करने और आर्किटेक्चर-विशिष्ट कोड की मात्रा को कम करके हमें फिर से लागू करने की आवश्यकता है। अधिक जानकारी के लिए हमारे ARM पोर्ट को समर्पित आगामी पोस्ट के लिए देखें! "