हिप हॉप वर्चुअल मशीन (HHVM) सैद्धांतिक रूप से PHP रनटाइम प्रदर्शन को कैसे बेहतर बनाता है?


9

एक उच्च स्तर से, फेसबुक, एट कैसे करता है। हिप हॉप वर्चुअल मशीन के साथ PHP के प्रदर्शन में सुधार करने के लिए अल उपयोग?

पारंपरिक ज़ेंड इंजन का उपयोग करके कोड निष्पादित करने से यह कैसे भिन्न होता है? क्या ऐसा इसलिए है क्योंकि प्रकारों को वैकल्पिक रूप से हैक के साथ परिभाषित किया जाता है जो पूर्व-अनुकूलन तकनीकों के लिए अनुमति देते हैं?

इस लेख, HHVM को अपनाने के बाद मेरी उत्सुकता पैदा हुई ।

जवाबों:


7

उन्होंने ट्रांसलेटरेटर 64 के ट्रेसलेट्स को नए हिपहॉप इंटरमीडिएट रिप्रेजेंटेशन (हिर) के साथ बदल दिया, और एक नई इनडायरेक्शन लेयर जिसमें हिरन जेनरेट करने के लिए लॉजिक रहता है, जिसे वास्तव में उसी नाम से जाना जाता है, हिर।

उच्च स्तर से, यह 6 निर्देशों का उपयोग कर रहा है जो पहले आवश्यक 9 निर्देशों को करने के लिए किया गया था, जैसा कि यहां उल्लेख किया गया है: "यह एक ही टाइपेकिट्स के साथ शुरू होता है लेकिन अनुवाद का शरीर 6 निर्देश है, ट्रांसलेटरएक्स 64 से 9 की तुलना में काफी बेहतर है"

http://hhvm.com/blog/2027/faster-and-cheaper-the-evolution-of-the-hhvm-jit

यह ज्यादातर इस बात की एक कलाकृति है कि कैसे सिस्टम को डिज़ाइन किया गया है और कुछ ऐसा है जिसे हम अंततः साफ करने की योजना बनाते हैं। 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 पोर्ट को समर्पित आगामी पोस्ट के लिए देखें! "


1

संक्षेप में: वे यादृच्छिक मेमोरी एक्सेस को कम करने की कोशिश करते हैं और सीपीयू कैश के साथ अच्छी तरह से खेलने के लिए कोड के टुकड़ों के बीच कूदते हैं।

HHVM के प्रदर्शन की स्थिति के अनुसार, वे यादृच्छिक मेमोरी एक्सेस को कम करने के लिए, अक्सर उपयोग किए जाने वाले डेटा प्रकारों, जो स्ट्रिंग्स और सरणियों को अनुकूलित करते हैं। यह विचार एक साथ उपयोग किए जाने वाले डेटा टुकड़ों (एक सरणी में आइटम) को स्मृति में यथासंभव एक दूसरे के करीब रखने के लिए है, आदर्श रूप से एक रैखिक फैशन में। इस तरह, यदि डेटा CPU L2 / L3 कैश में फिट बैठता है, तो यह रैम में होने की तुलना में तेजी से परिमाण के आदेशों को संसाधित कर सकता है।

उल्लेखित एक अन्य तकनीक एक कोड में सबसे अधिक बार उपयोग किए जाने वाले पथों को इस तरह से संकलित कर रही है कि संकलित संस्करण जितना संभव हो उतना रैखिक (ei में कम से कम "कूदता है") है और डेटा को स्मृति से बाहर / जितना संभव हो उतना कम लोड करता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.