كم يكلّف تحميل مجلد lib/ كله فعلًا
السطر 46 من ملف الدخول الوحيد لهذه الخدمة:
foreach (glob(__DIR__ . '/lib/*.php') as $f) { require_once $f; }
هذا يعني 65 ملفًا و1.5 ميغابايت تُحمَّل مع كل طلب. صورة عداد، أو صفحة إحصائيات، أو صفحة 404 — الأمر نفسه. بالأمس حدث ذلك 20,734 مرة.
الرقم المهم هو الصغير
تحميل كل ذلك من الصفر، مع إيقاف ذاكرة opcode المؤقتة، يستغرق نحو 132 ملي ثانية. هذا هو الرقم الذي كان سيجعل الأمر مشكلة، لكنه ليس الرقم الذي يدفعه الخادم.
حين تكون شيفرات opcode مترجمة سلفًا ومحفوظة في الذاكرة المشتركة، لا يبقى سوى سرد محتوى المجلد واستدعاء stat() واحد لكل ملف، لأن التحقق من الطوابع الزمنية مفعّل: 0.33 ملي ثانية. وبحجم حركة الأمس يعني ذلك أقل من سبع ثوانٍ من العمل على مدار اليوم كله.
وبين الاثنين قياس ثالث، 18.2 ملي ثانية، أُخذ وذاكرة opcode المؤقتة تقرأ من القرص بدلًا من الذاكرة. إنه حد أعلى وليس الرقم الحقيقي — فهو ما يحدث حين توجد ذاكرة مؤقتة لكنها أبطأ من الحقيقية. ونذكره لأنه الرقم الوحيد في الحالة الدافئة الذي يمكن قياسه من سطر الأوامر، والتظاهر بغير ذلك سيكون الخطأ السهل.
إذن: الكلفة شبه معدومة، وهي شبه معدومة بفضل شيء آخر بالكامل. أوقِف ذاكرة opcode المؤقتة وسيكلّف السطر نفسه أربعمئة ضعف. هذه تبعية حقيقية، ومن المفيد معرفة وجودها بدلًا من اكتشافها أثناء ترقية.
ما الذي تحتويه الـ 1.5 ميغابايت فعلًا
سبعة وأربعون بالمئة منها — 715 كيلوبايت موزعة على 26 ملفًا — عائلات تصاميم العدادات. ورسم صورة عداد يحتاج إلى واحدة منها فقط. أما الـ 25 الأخرى فتُحمَّل وتُعرَّف ولا تُمَسّ أبدًا.
يلي ذلك: 189 كيلوبايت لبيانات الأعلام، تحتاجها صفحة الجغرافيا؛ و158 كيلوبايت لمكتبة المخططات، يُحتاج إليها حين يرسم شيء ما مخططًا. ولا دور لأي منهما في تقديم صورة عداد، وهو الطلب الذي تجيب عنه هذه الخدمة أكثر من أي طلب آخر.
والكلفة الظاهرة لذلك هي الذاكرة لا الوقت. فذروة استهلاك الذاكرة لكل طلب ترتفع بنحو أربعة ميغابايت، وعلى خلاف شيفرات opcode — المشتركة بين كل العمليات — يُدفع هذا الجزء عن كل طلب، بالتوازي، من كل عملية عاملة في آن واحد.
هناك من انتبه بالفعل
نمط glob هو lib/*.php. وهو ليس تعاوديًا، ويقع تحته مباشرة مجلدان: lib/live/ بحجم 229 كيلوبايت، و lib/recht/ بحجم 182 كيلوبايت. أربعمئة كيلوبايت كانت ضمن مسار التحميل ولم تعد كذلك.
نُقلت هذه الملفات لأنها كبيرة ونادرًا ما يُحتاج إليها: أقسام الإحصائيات والنصوص القانونية. ووجودها في مستوى أعمق بمجلد واحد هو الآلية كلها. لا إعدادات، ولا محمّل كسول، ولا خريطة تحميل تلقائي — فالملف إما داخل نمط glob وإما خارجه.
هذا إصلاح حقيقي ويستحق أن يُذكر، لأن الخطوة التالية البديهية ستكون محمّلًا تلقائيًا (autoloader)، والمحمّل التلقائي هنا تغيير كبير لحل مشكلة سبق أن حلّها أمر mkdir مرتين.
لماذا يبقى
نمط glob واحد هو أبسط ما يمكن أن ينجح، والأدلة تقول إنه ينجح فعلًا: ثلث ملي ثانية، على خادم صغير، في المسار الذي يجب أن يكون سريعًا. أما استبداله بعبارات require لكل ملف فيعني أن كل ملف جديد يحتاج إلى سطر في مكان ما، والسطر الذي يُنسى يُنتج خطأً فادحًا في بيئة الإنتاج بدلًا من حمل أكبر قليلًا.
ما يحتاجه فعلًا هو تدوين الشيء الذي جعله قابلًا للبقاء من الأساس: إذا كبر ملف في lib/ فإنه يُنقل إلى مجلد فرعي ويُستدعى بـ require في موضع استخدامه. هذه هي القاعدة التي كان المجلدان يتبعانها، ولم تكن مكتوبة في أي مكان حتى الآن.
الصيغة العامة أقل إثارة من القياس. «كل شيء يحمّل كل شيء» بنية خاطئة في العادة، ومن المفيد قياس كلفتها قبل إعادة بنائها. هنا تبلغ الكلفة 0.33 ملي ثانية وأربعة ميغابايت، والميغابايتات الأربعة هي الجزء الجدير بالمراقبة، لأنها تتزايد مع التزامن، أما أجزاء الملي ثانية فلا.