فهم JIT في PHP 8

تم إعداد ترجمة المقال عشية بدء دورة "Backend-developer in PHP"








TL ؛ د



يتم تنفيذ برنامج التحويل البرمجي Just In Time في PHP 8 كجزء من ملحق Opcache وهو مصمم لتجميع كود التشغيل في تعليمات المعالج في وقت التشغيل.



هذا يعني أنه مع JIT ، لا يجب أن يتم تفسير بعض رموز التشغيل بواسطة Zend VM ، سيتم تنفيذ هذه التعليمات مباشرة كتعليمات على مستوى المعالج.



JIT في PHP 8



واحدة من أكثر الميزات التي تم التعليق عليها في PHP 8 هي مترجم Just In Time (JIT). يسمع في العديد من المدونات والمجتمعات - هناك الكثير من الضجيج حوله ، ولكن حتى الآن لم أجد الكثير من التفاصيل حول كيفية عمل JIT بالتفصيل.



بعد محاولات وإحباطات عديدة للعثور على معلومات مفيدة ، قررت دراسة شفرة مصدر PHP. من خلال الجمع بين معرفتي الصغيرة بـ C وجميع المعلومات المتناثرة التي تمكنت من جمعها حتى الآن ، تمكنت من إعداد هذه المقالة وآمل أن تساعدك على فهم PHP JIT بشكل أفضل.



لتبسيط الأمور: عندما تعمل JIT كما هو متوقع ، لن يتم تنفيذ التعليمات البرمجية الخاصة بك من خلال Zend VM ، بدلاً من ذلك سيتم تنفيذها مباشرة كمجموعة من التعليمات على مستوى المعالج.



هذه هي الفكرة كلها.



ولكن لفهم هذا بشكل أفضل ، نحتاج إلى التفكير في كيفية عمل php داخليًا. الأمر ليس صعبًا للغاية ، ولكنه يتطلب بعض المقدمة.



لقد كتبت بالفعل مقالة مع لمحة سريعة عن كيفية عمل php . إذا كنت تعتقد أن هذه المقالة معقدة للغاية ، فما عليك سوى قراءة سلفها والعودة. هذا من شأنه أن يجعل الأمور أسهل قليلاً.



كيف يتم تنفيذ كود PHP؟



نعلم جميعًا أن php هي لغة مترجمة. ولكن ماذا يعني هذا في الواقع؟



عندما تريد تنفيذ كود PHP ، سواء كان مقتطفًا أو تطبيق ويب كامل ، عليك أن تذهب من خلال مترجم php. الأكثر استخدامًا هي PHP FPM ومترجم CLI. وظيفتهم بسيطة للغاية: احصل على كود php ، وقم بتفسيره ، وأرجع النتيجة.



هذه صورة شائعة لكل لغة مترجمة. قد تختلف بعض الخطوات ، ولكن الفكرة العامة هي نفسها. في PHP يعمل مثل هذا:



  1. تتم قراءة كود PHP وتحويله إلى مجموعة من الكلمات الرئيسية المعروفة باسم Tokens. تتيح هذه العملية للمترجم أن يفهم في أي جزء من البرنامج يتم كتابة كل جزء من التعليمات البرمجية. تسمى هذه الخطوة الأولى Lexing أو Tokenizing .
  2. , PHP . (Abstract Syntax Tree — AST) , (parsing). AST , , . , «echo 1 + 1» « 1 + 1» , , « , — 1 + 1».
  3. AST, , . -, , (Intermediate Representation IR), PHP (Opcode). AST .
  4. الآن بعد أن أصبح لدينا opcodes ، يأتي الأكثر إثارة للاهتمام: تنفيذ الكود! يحتوي PHP على محرك يسمى Zend VM قادر على الحصول على قائمة برموز opcodes وتنفيذها. بعد أن تم تنفيذ جميع الشفرات ، ينتهي البرنامج.




لجعلها أكثر وضوحا قليلا ، قمت بعمل رسم بياني:





مخطط مبسط لعملية تفسير PHP.



جميلة جدا كما ترون. ولكن هناك أيضًا اختناق هنا: ما هو الهدف من lexing و تحليل التعليمات البرمجية الخاصة بك في كل مرة تقوم بتنفيذها إذا كان كود php الخاص بك قد لا يتغير حتى في كثير من الأحيان؟



بعد كل شيء ، نحن مهتمون فقط بالرموز ، أليس كذلك؟ حق! هذا هو السبب في وجود ملحق Opcache .



ملحق Opcache



يأتي ملحق Opcache مع PHP ولا يوجد عادة سبب معين لإلغاء تنشيطه. إذا كنت تستخدم PHP ، فمن المحتمل أن تقوم بتمكين Opcache.



ما يفعله هو إضافة طبقة التخزين المؤقت لرمز التشغيل المشترك عبر الإنترنت. وتتمثل مهمتها في جلب رموز opc التي تم إنشاؤها مؤخرًا من AST وتخزينها مؤقتًا حتى تتمكن عمليات الإعدام اللاحقة من تخطي مراحل lexing و parsing بسهولة.



هنا رسم تخطيطي لنفس العملية مع وضع امتداد Opcache في الاعتبار:





تدفق تفسير PHP مع Opcache. إذا كان الملف قد تم تحليله بالفعل ، فإن php يستخرج كود التشغيل المخزن له ، بدلاً من إعادة تحليله.



إنه يفتن فقط كيف يتم تخطي خطوات lexing والتحليل والتجميع بشكل جميل.

ملاحظة : هذا هو المكان الذي تكون فيه ميزة التحميل المسبق PHP 7.4 مفيدة ! يتيح لك هذا إخبار PHP FPM بتحليل قاعدة التعليمات البرمجية الخاصة بك ، وتحويلها إلى رموز تشغيل ، وتخزينها مؤقتًا حتى قبل القيام بأي شيء.


قد تبدأ في التساؤل أين يمكنك لصق JIT هنا ، أليس كذلك ؟! آمل ذلك على الأقل ، ولهذا السبب أكتب هذا المقال ...



ماذا يفعل المترجم Just In Time؟



بعد الاستماع إلى شرح لـ Ziva في حلقة بودكاست PHP و JIT من PHP Internals News ، تمكنت من الحصول على فكرة عما يفترض فعلاً القيام به JIT ...



إذا كان Opcache يسمح برمز تشغيل أسرع بحيث يمكنه الانتقال مباشرة إلى Zend VM ، JIT تهدف إلى جعلها تعمل بدون Zend VM على الإطلاق.



Zend VM هو برنامج C يعمل كطبقة بين كود التشغيل والمعالج نفسه. يقوم JIT بإنشاء التعليمات البرمجية المترجمة في وقت التشغيل ، بحيث يمكن لـ php تخطي Zend VM والقفز مباشرة إلى المعالج . من الناحية النظرية ، يجب أن نستفيد من هذا من حيث الأداء.



بدا الأمر غريباً في البداية ، لأنه لتجميع كود الآلة ، عليك كتابة تنفيذ محدد للغاية لكل نوع من أنواع الهندسة المعمارية. لكن في الحقيقة هذا حقيقي.



يستخدم تطبيق JIT في PHP مكتبة DynASM (Dynamic Assembler) ، التي تعين مجموعة من تعليمات وحدة المعالجة المركزية بتنسيق محدد لتجميع التعليمات البرمجية للعديد من أنواع وحدات المعالجة المركزية المختلفة. وبالتالي ، يقوم المترجم Just In Time بتحويل كود التشغيل إلى كود آلة خاص بالعمارة باستخدام DynASM.



على الرغم من أن فكرة واحدة لا تزال تطاردني ...



إذا كان التحميل المسبق قادرًا على تحليل كود php للتشغيل قبل التنفيذ ، ويمكن لـ DynASM تجميع كود التشغيل إلى رمز الآلة (تجميع في الوقت المناسب) ، فلماذا لا نقوم بتجميع PHP في مكانه على الفور باستخدام تجميع Ahead of Time؟!



كانت إحدى الأفكار التي تلقيتها من حلقة البودكاست هي أن PHP تمت كتابتها بشكل ضعيف ، مما يعني أن PHP غالبًا لا تعرف نوع المتغير حتى يحاول Zend VM تنفيذ رمز تشغيل محدد.



يمكنك فهم ذلك من خلال النظر إلى نوع اتحاد zend_value ، الذي يحتوي على العديد من المؤشرات إلى تمثيلات نوع مختلفة لمتغير. عندما يحاول Zend VM جلب قيمة من zend_value ، فإنه يستخدم وحدات ماكرو مثل ZSTR_VALالتي تحاول الوصول إلى مؤشر السلسلة من تسلسل القيمة.



على سبيل المثال ، يجب أن يعالج معالج Zend VM هذا التعبير (<=) أو أقل منه. انظر كيف تتفرع إلى العديد من مسارات الكود المختلفة لتخمين أنواع المعاملات.



إن تكرار منطق الاستدلال من هذا النوع برمز الآلة غير ممكن ويمكن أن يجعل الأمور أبطأ.



التجميع النهائي بعد تقييم الأنواع ليس أيضًا خيارًا جيدًا لأن التحويل إلى كود الآلة هو مهمة تستهلك وحدة المعالجة المركزية. لذا فإن تجميع كل شيء في وقت التشغيل فكرة سيئة.



كيف يتصرف المترجم Just In Time؟



نحن نعلم الآن أنه لا يمكننا استنتاج أنواع لإنشاء تجميع مسبق جيد بما فيه الكفاية. نحن نعلم أيضًا أن عملية التجميع في وقت التشغيل مكلفة. كيف يمكن أن يكون JIT مفيدًا لـ PHP؟



لموازنة هذه المعادلة ، تحاول PHP JIT تجميع عدد قليل من رموز opcodes التي تعتقد أنها تستحق ذلك. للقيام بذلك ، فإنه يميز ملفات opcodes التي تنفذها آلة Zend الافتراضية ويتحقق من تلك التي يمكن ترجمتها. (حسب التكوين الخاص بك) .



عندما يتم تجميع كود تشغيل معين ، فإنه يقوم بعد ذلك بتفويض التنفيذ إلى تلك التعليمات البرمجية المترجمة بدلاً من التفويض إلى Zend VM. يبدو أن الرسم البياني أدناه:





تدفق تفسير PHP مع JIT. إذا تم تجميعها بالفعل ، فلن يتم تنفيذ الشفرات من خلال Zend VM.



وبالتالي ، هناك زوجان من التعليمات في ملحق Opcache تحدد ما إذا كان يجب ترجمة كود تشغيل معين أم لا. إذا كان الأمر كذلك ، فإن المحول البرمجي يحولها إلى رمز الجهاز باستخدام DynASM وينفذ رمز الجهاز الذي تم إنشاؤه حديثًا.



من المثير للاهتمام ، نظرًا لأن التطبيق الحالي يحتوي على حد ميجابايت للشفرة المترجمة (قابل للتهيئة أيضًا) ، يجب أن يكون تنفيذ التعليمات البرمجية قادرًا على التبديل بسلاسة بين JIT والتعليمات البرمجية المفسرة.



بالمناسبة ، هذا الحديث من بينوا جاكيمونت حول JIT من php ساعدني كثيرًا في معرفة ذلك.



ما زلت غير متأكد من الحالات المحددة التي يتم فيها التجميع ، ولكن أعتقد أنني لا أريد حقًا معرفة ذلك بعد.



لذا فإن مكاسب الإنتاجية الخاصة بك ربما لن تكون هائلة



آمل أن يكون الأمر أكثر وضوحًا الآن لماذا يقول الجميع أن معظم تطبيقات php لن تحصل على فائدة أداء كبيرة من استخدام مترجم Just In Time. ولماذا تعتبر توصية Ziv للتوصيف والتجريب مع تكوينات JIT المختلفة لتطبيقك هي أفضل طريقة للذهاب.



عادةً ما تنتشر شفرات opc المترجمة عبر طلبات متعددة إذا كنت تستخدم PHP FPM ، ولكن لا يزال هذا لا يغير قواعد اللعبة.



هذا لأن JIT يحسن عمليات وحدة المعالجة المركزية ، وفي الوقت الحاضر فإن معظم تطبيقات php مرتبطة بإدخال / إخراج أكثر من أي شيء آخر. لا يهم إذا تم تجميع عمليات المعالجة إذا كان عليك الوصول إلى القرص أو الشبكة على أي حال. ستكون المواعيد متشابهة للغاية.



فقط لو...



أنت تفعل شيئًا غير I / O ، مثل معالجة الصور أو التعلم الآلي. أي شيء غير I / O سيستفيد من مترجم Just In Time. هذا هو أيضًا السبب الذي يجعل الناس يقولون الآن أنهم يميلون أكثر نحو كتابة وظائف PHP الأصلية المكتوبة بلغة PHP بدلاً من C. لن يكون الحمل العام مختلفًا بشكل كبير إذا تم تجميع هذه الوظائف على أي حال.



وقت مثير للاهتمام أن أكون مبرمج PHP ...



آمل أن تكون هذه المقالة مفيدة لك وأن تكون قد فهمت بشكل أفضل ما هو JIT في PHP 8. لا تتردد في إرسال تغريدة لي إذا كنت ترغب في إضافة شيء ربما نسيته هنا ، ولا تنس مشاركة هذا مع زملائك من المطورين ، فمن المؤكد أنه سيضيف القليل من القيمة إلى محادثاتك!-- @nawarian






PHP:







All Articles