ملاحظة المترجم: اعتقدت أيضًا أن وقت المقالات هو "ما هو أسرع - اقتباسات مزدوجة أو مفردة؟" استغرق الأمر 10 سنوات أخرى. ولكن هنا مقال مشابه ("ما هي حيل الأداء التي تعمل في الواقع") ، حصل مؤخرًا على تصنيف مرتفع نسبيًا على Reddit وحتى الدخول في ملخص PHP على Habré. وبناءً على ذلك ، قررت ترجمة المقالة بتحليل نقدي لهذه "الاختبارات" المماثلة.
هناك العديد من المقالات (وحتى المواقع بأكملها) مخصصة لإطلاق اختبارات مختلفة تقارن أداء الهياكل النحوية المختلفة وتوضح على هذا الأساس أن أحدهما أسرع من الآخر.
المشكلة الرئيسية
هذه الاختبارات غير صحيحة لأسباب عديدة ، من طرح سؤال إلى أخطاء التنفيذ. ولكن الأهم من ذلك - أن مثل هذه الاختبارات لا معنى لها وفي نفس الوقت ضارة.
- إنها لا معنى لها لأنها لا تحمل قيمة عملية. لم يتم تسريع أي مشروع حقيقي باستخدام الأساليب الواردة في هذه المقالات. ليس فقط لأن الاختلافات في بناء الجملة تهم الأداء ، ولكن معالجة البيانات.
- فهي ضارة لأنها تؤدي إلى ظهور أكثر الخرافات وحشية - والأسوأ من ذلك - تشجيع القراء غير المشتبه فيهم على كتابة كود سيئ ، معتقدين أنهم "يحسنونه".
يجب أن يكون ذلك كافيا لإغلاق السؤال. ولكن حتى لو قبلت قواعد اللعبة وتظاهر بأن هذه "الاختبارات" لها بعض المعنى على الأقل ، فقد اتضح أن نتائجها يتم تقليلها فقط إلى عرض لنقص معرفة المختبر وافتقاره إلى أي خبرة.
مفردة مقابل مزدوجة
خذ الاقتباسات سيئة السمعة ، "مفردة مقابل مزدوجة". بالطبع ، لا توجد اقتباسات أسرع. أولاً ، يوجد شيء مثل ذاكرة التخزين المؤقت لرمز التشغيل ، والذي يخزن نتيجة تحليل نص PHP النصي في ذاكرة التخزين المؤقت. في هذه الحالة ، يتم حفظ كود PHP بتنسيق opcode ، حيث يتم تخزين نفس السلسلة الحرفية ككيانات متطابقة تمامًا ، بغض النظر عن الاقتباسات التي تم استخدامها في البرنامج النصي PHP. مما يعني عدم وجود اختلاف نظري في الأداء.
ولكن حتى إذا لم نستخدم ذاكرة التخزين المؤقت opcode (على الرغم من أننا يجب أن ، إذا كانت مهمتنا هي زيادة الأداء حقًا) ، فسوف نجد أن الاختلاف في رمز التحليل صغير جدًا (العديد من التحولات الشرطية التي تقارن الأحرف أحادية البايت ، حرفيا العديد من تعليمات المعالج) أنه سيكون على الإطلاق غير قابل للكشف. هذا يعني أن أي نتائج تم الحصول عليها سوف تظهر فقط مشاكل في بيئة الاختبار. هناك مقالة مفصلة للغاية ، دحض أسطورة أداء الاقتباسات الفردية من مطور PHP الأساسي نيكيتا بوبوف ، والتي تحلل هذه المشكلة بالتفصيل. ومع ذلك ، يظهر جهاز اختبار نشط كل شهر تقريبًا ليكشف للمجتمع "اختلافًا" خياليًا في الأداء.
التناقضات المنطقية
بعض الاختبارات لا معنى لها بشكل عام ، ببساطة من وجهة نظر طرح السؤال: على سبيل المثال ، الاختبار المعنون "هل الرمية عملية مكلفة للغاية؟" هذا هو السؤال الأساسي "هل حقا أن معالجة خطأ سيكون أكثر تكلفة من عدم المعالجة؟". هل انت جاد؟ بالطبع ، إضافة بعض الوظائف الأساسية إلى الكود ستجعله "أبطأ". لكن هذا لا يعني أن الوظيفة الجديدة لا تحتاج إلى أي إضافة على الإطلاق ، تحت مثل هذه الذريعة السخيفة. إذا كنت تتحدث بهذه الطريقة ، فإن أسرع برنامج هو برنامج لا يفعل شيئًا على الإطلاق! يجب أن يكون البرنامج مفيدًا ويعمل بدون أخطاء في المقام الأول. وفقط بعد تحقيق ذلك ، وفقط إذا كان يعمل ببطء ، يجب تحسينه. ولكن إذا كان السؤال نفسه لا معنى له ، فلماذا يزعج اختبار الأداء؟ من المضحك أن المختبر لم يتمكن من تنفيذ هذا الاختبار غير المنطقي بشكل صحيح ، والذي سيتم عرضه في القسم التالي.
أو مثال آخر ، اختبار بعنوان "هل $row[id]
سيكون أبطأ حقًا من $row['id']
؟" هذا هو السؤال الأساسي "أي رمز أسرع - الرمز الذي يعمل مع الأخطاء ، أو بدونها؟" (نظرًا لأن كتابة id
بدون علامات اقتباس في هذه الحالة خطأ من مستوى E_NOTICE
، وسيتم تجاهل هذه الكتابة في الإصدارات المستقبلية من PHP). WTF؟ ما الهدف من قياس أداء رمز الخطأ بشكل عام؟ يجب إصلاح الخطأ ببساطة لأنه خطأ ، وليس لأنه سيجعل الرمز يعمل بشكل أبطأ. من المضحك أن المختبر لم يتمكن من تنفيذ هذا الاختبار غير المنطقي بشكل صحيح ، والذي سيتم عرضه في القسم التالي.
اختبار الجودة
ومرة أخرى - حتى الاختبار الذي لا فائدة منه عن علم يجب أن يكون متسقًا ومتسقًا - أي قياس القيم القابلة للمقارنة. ولكن ، كقاعدة عامة ، يتم إجراء هذه الاختبارات بالكعب الأيسر ، ونتيجة لذلك ، فإن النتائج التي يتم الحصول عليها لا معنى لها ولا تتعلق بالمهمة.
على سبيل المثال ، قام try..catch
الغبي بقياس "الاستخدام المفرط try..catch
". لكن في الاختبار الحالي ، قام بقياس ليس فقط try catch
، ولكن أيضًا throw
، ورمي استثناءً في كل تكرار للحلقة. لكن مثل هذا الاختبار غير صحيح ببساطة ، لأن الأخطاء في الحياة الواقعية لا تحدث مع كل تنفيذ نصي.
بالطبع ، لا ينبغي إجراء الاختبارات على إصدارات بيتا من PHP ويجب ألا تقارن الحلول السائدة بالحلول التجريبية. وإذا تعهد المختبر بمقارنة "سرعة تحليل json و xml" ، فلا يجب عليه استخدام الوظيفة التجريبية في الاختبارات.
تظهر بعض الاختبارات ببساطة سوء فهم كامل من قبل مختبِر المهمة التي حددها. تم ذكر مثال مشابه من مقالة تم نشرها مؤخرًا أعلاه: حاول مؤلف الاختبار معرفة ما إذا كان الرمز الذي تسبب في الخطأ ("استخدام ثابت غير محدد") أبطأ من الرمز بدون أخطاء (والذي يستخدم سلسلة حرفية صحيحة نحويًا) ، لكنه فشل حتى مع هذا الاختبار الواضح الذي لا معنى له ، مقارنة أداء رقم مقتبس بأداء رقم مكتوب بدون علامات اقتباس. بالطبع ، يمكنك كتابة أرقام بدون علامات اقتباس في PHP (على عكس السلاسل) ، ونتيجة لذلك ، اختبر المؤلف وظيفة مختلفة تمامًا ، حيث حصل على نتائج غير صحيحة.
هناك قضايا أخرى يجب وضعها في الاعتبار ، مثل بيئة الاختبار. هناك امتدادات لـ PHP مثل XDebug يمكن أن يكون لها تأثير كبير جدًا على نتائج الاختبار. أو ذاكرة التخزين المؤقت opcode المذكورة مسبقًا ، والتي يجب تضمينها أثناء اختبارات الأداء بحيث تكون نتائج الاختبار منطقية على الأقل.
كيفية إجراء الاختبار مهمة أيضًا. نظرًا لأن عملية PHP تموت تمامًا بعد كل طلب ، فمن المنطقي اختبار أداء دورة الحياة بأكملها ، بدءًا من إنشاء اتصال بخادم الويب وانتهاءً بإغلاق هذا الاتصال. هناك أدوات مثل مقياس Apache أو Siege تتيح لك القيام بذلك.
تحسين الأداء الحقيقي
كل هذا جيد ، ولكن ما الاستنتاج الذي يجب أن يتخذه القارئ من هذه المقالة؟ ما هي اختبارات الأداء التي لا جدوى منها بحكم التعريف؟ بالطبع لا. ولكن ما يهم حقًا هو السبب الذي يجعلهم يبدأون. الاختبار من الصفر مضيعة للوقت. يجب أن يكون هناك دائمًا سبب محدد لإجراء اختبارات الأداء. وهذا السبب يسمى "التنميط" . عندما يبدأ التطبيق الخاص بك في العمل ببطء ، تحتاج إلى عمل ملف تعريف ، مما يعني قياس سرعة أقسام مختلفة من التعليمات البرمجية للعثور على أبطأ. بعد العثور على هذا الموقع ، يجب علينا تحديد السبب. في أغلب الأحيان ، يكون هذا إما أكبر بكثير مما هو مطلوب ، أو كمية البيانات المعالجة ، أو طلب لمصدر بيانات خارجي. بالنسبة للحالة الأولى ، سيتألف التحسين من تقليل كمية البيانات المعالجة ، وللحالة الثانية ، تخزين نتائج الاستعلام مؤقتًا.
على سبيل المثال ، من حيث الأداء ، لا فرق في ما إذا كنا نستخدم حلقة موصوفة صراحة أو وظيفة PHP المضمنة لمعالجة المصفوفات (والتي هي في الأساس مجرد سكر نحوي). ما يهم حقًا هو كمية البيانات التي نرسلها للمعالجة. إذا كانت كبيرة بشكل غير معقول ، يجب علينا قطعها ، أو نقل المعالجة في مكان آخر (إلى قاعدة البيانات). سيعطينا هذا دفعة أداء ضخمة ستكون حقيقية . في حين أنه من غير المحتمل أن يكون الفرق بين طرق استدعاء الحلقة لمعالجة البيانات ملحوظًا على الإطلاق.
فقط بعد إجراء مثل هذه التحسينات الإلزامية في الأداء ، أو إذا لم نتمكن من تقليل كمية البيانات المعالجة ، يمكننا بدء اختبارات الأداء. ولكن مرة أخرى ، لا ينبغي إجراء مثل هذه الاختبارات من الصفر. من أجل البدء في مقارنة أداء حلقة صريحة ووظيفة مضمنة ، يجب أن نتأكد من أن الحلقة هي سبب المشكلة ، وليس محتوياتها (المفسد: بالطبع ، هذا هو المحتوى).
مثال حديث من ممارستي: في الكود كان هناك استعلام باستخدام Doctrine Query Builder ، والذي كان من المفترض أن يأخذ عدة آلاف من المعلمات. الاستعلام نفسه سريع بما يكفي ، لكن العقيدة تستغرق بعض الوقت لهضم عدة آلاف من المعلمات. ونتيجة لذلك ، تمت إعادة كتابة الاستعلام في SQL خالص ، وتم نقل المعلمات إلى طريقة التنفيذ () لمكتبة PDO ، والتي تتواءم مع العديد من المعلمات على الفور تقريبًا.
هل هذا يعني أنني لن أستخدم مطلقًا Doctrine Query Builder؟ بالطبع لا. إنها مثالية لـ 99٪ من المهام ، وسأواصل استخدامها لجميع الاستفسارات. وفقط في حالات استثنائية ، يجدر استخدام طريقة أقل ملاءمة ، ولكن أكثر إنتاجية.
تم إنشاء الاستعلام والمعلمات لهذا التحديد في حلقة. إذا كانت لدي فكرة غبية للتعامل مع كيفية استدعاء الدورة ، فعندئذ ببساطة سأضيع الوقت دون أي نتيجة إيجابية. وهذا هو جوهر كل تحسينات الأداء: لتحسين الرمز الذي يعمل ببطء في حالتك الخاصة فقط. وليس الكود الذي كان يعتبر بطيئًا منذ زمن طويل ، في مجرة بعيدة بعيدة ، أو الكود الذي حدث لشخص ما ليطلق عليه اسم بطيء بناءً على اختبارات لا معنى لها.