يتأثر كود التشغيل (opcode) الذي ينتجه محرك PHP بشكل كبير بطريقة كتابة الكود الخاص بك، ليس فقط من حيث عدد الجمل لإنجاز مهمة معينة. من الواضح أن هذا الأمر مهم جداً، وأعتقد أنك تدرك ذلك.
ما قد يكون أقل وضوحاً هو أن حتى بناء جملة الكود يمكن أن يغير كود التشغيل الناتج تماماً مما يسبب الكثير من الحمل الزائد لوحدة المعالجة المركزية في الآلة لتنفيذ نفس الكود بالضبط.
في السنوات القليلة الماضية، نما منتج SaaS الخاص بي بشكل كبير، وقد أتاح لي الفرصة للتعمق أكثر في تقنيات التحسين لتشغيل حملي الوظيفي بأكبر قدر ممكن من الكفاءة.
النتائج التي رأيتها مثيرة للإعجاب وساعدتني كثيراً في فتح تدفق نقدي مجاني لمواصلة تطوير رحلتي في SaaS.
في هذه المرحلة، يقوم عملية PHP داخل منتج SaaS الخاص بي بمعالجة أكثر من 1.2 مليار (مع “ب”) حزمة بيانات كل يوم على آلة تحتوي على 2vCPU و 8GB من الذاكرة. أستخدم مجموعة توسيع تلقائي من AWS للحصول على مزيد من المرونة في حالة الارتفاعات غير المتوقعة، ولكن نادراً ما تضيف آلة ثانية (مرة/مرتين في الأسبوع).
دعونا ننتقل إلى موضوع المقالة. أعتقد أنك ستجدها مثيرة جداً للاهتمام.
ما هو كود التشغيل PHP؟
كود التشغيل PHP هو اختصار لـ operation code، ويشير إلى التعليمات منخفضة المستوى التي يتم تنفيذها بواسطة محرك PHP بعد أن يتم تجميع كود المصدر PHP الذي تكتبه.
في PHP، يحدث تجميع الكود أثناء وقت التشغيل: أساساً، في المرة الأولى التي يتم فيها أخذ الكود الخاص بك بواسطة محرك PHP، سيتم تجميعه إلى هذا الكود المناسب للآلة، وتخزينه في الذاكرة (حتى لا يقوم المحرك بتجميع نفس الكود مرة أخرى)، ثم يتم تنفيذه.
هذا تمثيل بسيط للعملية:
تخزين كود العمليات في PHP
يسمح تخزين كود العمليات في PHP بتوفير ثلاث خطوات في عملية تنفيذ الكود: تحليل كود PHP الخام، تحويله إلى رموز، والتجميع.
بمجرد أن يتم إنشاء كود العمليات للمرة الأولى، يتم تخزينه في الذاكرة بحيث يمكن إعادة استخدامه في الطلبات اللاحقة. هذا يقلل من الحاجة إلى إعادة تجميع نفس كود PHP في كل مرة يتم تنفيذه فيها، مما يوفر الكثير من استهلاك وحدة المعالجة المركزية والذاكرة.
أكثر ذاكرة كود العمليات استخدامًا في PHP هي OPCache، وهي مضمنة بشكل افتراضي منذ PHP 5.5 وحتى الإصدارات الحديثة. إنها فعالة للغاية ومدعومة على نطاق واسع.
يتطلب تخزين بايت كود السكربت المسبق التجميع إبطال الذاكرة المؤقتة بعد كل نشر. ذلك لأن الملفات التي تم تغييرها تحتوي على إصدار بايت كود في الذاكرة المؤقتة، سيستمر PHP في تشغيل الإصدار القديم من الكود حتى تقوم بإزالة ذاكرة كود العمليات، وبالتالي سيتم تجميع الكود الجديد مرة أخرى مما يولد عنصر ذاكرة مؤقتة جديد.
كيفية التحقيق في كود عمليات PHP
لفهم كيف يمكن أن تؤثر الصياغات المختلفة على كود العمليات الخاص بالسكربت، نحتاج إلى وسيلة للحصول على الكود المجمّع الذي تم إنشاؤه بواسطة محرك PHP.
هناك طريقتان للحصول على كود العمليات.
وظائف OPCache الأصلية
إذا كان لديك امتداد OPCache مفعل على جهازك، يمكنك استخدام وظائفه الأصلية للحصول على كود العمليات لملف PHP معين:
// Force compilation of a script
opcache_compile_file(__DIR__.'/yourscript.php');
// Get OPcache status
$status = opcache_get_status();
// Inspect the script's entry in the cache
print_r($status['scripts'][__DIR__.'/yourscript.php']);
VLD (فك تشفير المنطق الفلكاني) امتداد PHP
VLD هي إضافة PHP شعبية تفك ترميز الشفرة المصدرية المترجمة لـ PHP وتعرض الأوبكود. إنها أداة قوية لفهم كيفية تفسير PHP وتنفيذ الشفرة الخاصة بك. بمجرد تثبيته، يمكنك تشغيل نص PHP بتمكين VLD باستخدام أمر php
مع خيارات -d
:
php -d vld.active=1 -d vld.execute=0 yourscript.php
سيتضمن الإخراج معلومات مفصلة حول الأوبكود المترجم، بما في ذلك كل عملية والسطر المرتبط بها وما إلى ذلك.
استخدم 3v4l (الاختصار لـ EVAL)
3v4l هو أداة مفيدة جدًا عبر الإنترنت تتيح لك عرض الأوبكود الذي تولده الشفرة الخاصة بك في محرر PHP. في الأساس، إنه خادم PHP مع VLD مثبتة بحيث يمكنه الحصول على الإخراج الخاص بـ VLD وعرض الأوبكود في المتصفح.
نظرًا لأنه مجاني، سنستخدم هذه الأداة عبر الإنترنت للتحليلات التالية.
كيفية إنشاء أوبكود PHP فعال
3v4l مثالي لفهم كيف يؤثر بناء الشفرة الخاص بنا على الأوبكود الناتج بطريقة جيدة أو سيئة. لنبدأ بلصق الشفرة أدناه في 3v4l. احتفظ بتكوين “جميع الإصدارات المدعومة” وانقر على “تقييم”.
namespace App;
strlen('ciao');
بعد تنفيذ الشفرة، ستظهر قائمة تبويب في الأسفل. انتقل إلى علامة التبويب VLD لتصور الأوبكود المقابل.
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------
5 0 E > INIT_NS_FCALL_BY_NAME 'App%5CSpace%5Cstrlen'
1 SEND_VAL_EX 'ciao'
2 DO_FCALL 0
3 > RETURN 1
لاحظ أن أول عملية هي INIT_NS_FCALL_BY_NAME
. يقوم المترجم ببناء اسم الدالة باستخدام مساحة الأسماء للملف الحالي، ولكنها لا توجد في مساحة الأسماء App\Example
– فكيف يعمل؟
سيقوم المترجم بالتحقق مما إذا كانت الدالة موجودة في مساحة الأسماء الحالية. إذا لم تكن موجودة، فإنه يحاول استدعاء الدالة الأساسية المقابلة.
هنا لدينا الفرصة لإبلاغ المترجم بتجنب هذا التحقق المزدوج وتنفيذ الدالة الأساسية مباشرة.
حاول إضافة علامة الشرطة العكسية (\
) قبل strlen
وانقر على “تقييم” (eval):
namespace App;
\strlen('ciao');
في علامة التبويب VLD، يمكنك الآن رؤية الشيفرة البرمجية بعبارة واحدة فقط.
line #* E I O op fetch ext return operands
------------------------------------------------------------------------------------- 5 0 E > > RETURN 1
نظرًا لأنك قمت بتحديد المكان الدقيق للدالة، فإنها لا تحتاج إلى النظر إلى أي احتياطي.
إذا لم يعجبك استخدام علامة الشرطة العكسية، يمكنك استيراد الدالة مثل أي صنف آخر من مساحة الأسماء الجذر:
namespace App;
use function strlen;
strlen('ciao');
استغلال أوتوماتيكي للتحسينات في شيفرة العمليات
هناك أيضًا العديد من الآليات الداخلية لمحرك PHP لتوليد شيفرة عملية محسنة تقوم بتقييم التعابير الثابتة مسبقًا. كانت هذه واحدة من أهم الأسباب لتحسين أداء PHP منذ الإصدار 7.x.
التعرف على هذه الديناميات يمكن أن يساعدك حقًا في تقليل استهلاك الموارد وتقليل التكاليف. بمجرد أن قمت بهذا البحث، بدأت استخدام هذه الحيل في جميع أنحاء الشيفرة.
اسمحوا لي أن أعرض لك مثالًا باستخدام الثوابت في PHP. قم بتشغيل هذا البرنامج النصي في 3v4l:
namespace App;
if (PHP_OS === 'Linux') {
echo "Linux";
}
شاهد السطرين الأولين من شيفرة PHP:
line #* E I O op fetch ext return operands
------------------------------------------------------------------------------------- 5 0 E > FETCH_CONSTANT ~0 'App%5CPHP_OS' 1 IS_IDENTICAL ~0, 'Linux' 2 > JMPZ ~1, ->4 6 3 > ECHO 'Linux' 7 4 > > RETURN 1
حمل_معاملة
يحاول أخذ قيمة PHP_OS
من ال命名空间 الحالي وسيبحث في ال命名空间 العالمي لأنها غير موجودة هنا. بعد ذلك، تنفيذa IS_IDENTICAL
تنفيذ العبارة IF
.
حالياً ما إذا أضفت خط رجل للمعاملة:
namespace App;
if (\PHP_OS === 'Linux') {
echo "Linux";
}
وكما ترون في الأوبكود، لا يتوجب على المحرك محاولة حمل المعاملة لأنه بالفعل واضح أين يوجد، ولأنها قيمة ستائكة فقد لديها بالفعل في الذاكرة.
أيضًا، تختفي العبارة IF
لأن جهة أخرى من العبارة IS_IDENTICAL
قيمة ستائكة (‘Linux
’) لذا الIF
يمكن تسميته كـ “true
” دون تحمل التكاليف من ترجمته في كل تنفيذ.
لهذا السبب لديك قوة كبيرة للتأثير على أداء برمجياتك الPHP النهائية.
خلاصة
أتمنى أن كان هذا الموضوع مثيرًا للاهتمام. كما ذكرت في بداية المقالة أني أحصل على الكثير من المنافع من استخدام هذه التكتبة وفي الواقع يستخدم في بعض من برامجنا.
يمكنك رؤية مثال هنا لكيف استخدمت هذه المؤشرات في برنامجنا الPHP لتحسين أداءه.
إذا كنت تريد تعلم المزيد عن تحديات بناء شركة تهدف للمطورين، يمكنك متابعة السيارتي على LinkedIn.
Source:
https://dzone.com/articles/php-opcode-improve-application-performance