تحسين أداء تطبيقات PHP
أكبر فائدة للبرمجة بلغة PHP هي مدى سهولة تعلم لغة البرمجة هذه ومكتباتها الغنية. حتى لو لم نكن نعرف الكثير عن الوظائف التي نحتاج إلى استخدامها، يمكننا تخمين كيفية إنجاز مهمة محددة.
على الرغم من أن لغة PHP بسيطة جدًا وسهلة التعلم، إلا أننا مازلنا بحاجة إلى قضاء بعض الوقت في تعلم بعض مهارات برمجة PHP، خاصة تلك المتعلقة بالأداء واستخدام الذاكرة. في PHP، هناك العديد من الحيل التي تسمح لنا بتقليل استخدام الذاكرة وتحسين أداء التطبيق. في هذه المقالة، سنقدم بإيجاز تحليل تطبيقات PHP، وكيفية تغيير كود البرنامج النصي، ومقارنة قيم المعلمات المختلفة قبل وبعد التحسين.
ومن خلال ضبط إجراءات التوقيت في البرنامج وتنفيذ هذه الرموز بشكل متكرر، يمكننا الحصول على مجموعة من البيانات حول سرعة تنفيذ البرنامج، ويمكن استخدام هذه البيانات لاكتشاف الاختناقات في البرنامج وكيفية تحسينها لتحسين أداء البرنامج طلب.
ربما سمع القراء عن مكتبة PEAR. سوف نستخدم مكتبة PEAR لإنشاء الأمثلة التي نحتاج إلى استخدامها أثناء التحليل. وهذه أيضًا هي الطريقة الأسهل لتحليل الكود الموجود، فهي تتيح لنا تحليل الكود دون استخدام المنتجات التجارية.
اسم المكتبة التي سنستخدمها هو PEAR::Benchmark وهو مفيد جدًا في تحديد مواصفات الكود واختبار الأداء. توفر هذه المكتبة فئة تسمى Benchmark_Timer()، والتي يمكنها تسجيل الوقت بين استدعاء دالة واستدعاء الوظيفة التالي. عند اختبار أداء الكود، يمكننا الحصول على نتيجة تنفيذ البرنامج النصي التفصيلية، وهي بسيطة جدًا، كما يلي:
include_once("Benchmark/Timer.php");
$bench = new Benchmark_Timer;
$bench->start();
$bench-> setMarker('بداية البرنامج النصي');
// الآن في حالة السكون لبضع دقائق
النوم(5);
$bench->stop();
// احصل على معلومات التحليل من المؤقت
print_r($bench->getProfiling());
?>
الإخراج بعد تنفيذ الكود أعلاه هو كما يلي:
صفيف
(
[0] => المصفوفة
(
[الاسم] => ابدأ
[الوقت] => 1013214253.05751200
[فرق] => -
[الإجمالي] => 0
)
[1] => المصفوفة
(
[الاسم] => بداية البرنامج النصي
[الوقت] => 1013214253.05761100
[فرق] => 9.8943710327148E-05
[الإجمالي] => 9.8943710327148E-05
)
[2] => المصفوفة
(
[الاسم] => توقف
[الوقت] => 1013214258.04920700
[فرق] => 4.9915959835052
[الإجمالي] => 4.9916949272156
)
)
قد تبدو الأرقام أعلاه وكأنها مجموعة مفككة من الأرقام، ولكن إذا كان حجم البرنامج أكبر، فمن الممكن أن تكون هذه الأرقام مفيدة للغاية.
ربما يمكن لغالبية القراء أيضًا تخمين أن الإدخال الأول في المصفوفة هو الطريقة الفعلية لاستدعاء فئة Benchmark_Timer()، على سبيل المثال
$bench->start() و$bench->setMarker() و$bench->stop() الأرقام المرتبطة بهذه الإدخالات بسيطة للغاية الآن دعونا نلقي نظرة فاحصة على هذه الأرقام:
[0] => المصفوفة
(
[الاسم] => ابدأ
[الوقت] => 1013214253.05751200
[فرق] => -
[الإجمالي] => 0
)
يشير إدخال الوقت إلى الطابع الزمني لـ UNIX عند استدعاء طريقة start() لـ Benchmark_Timer(). يشير إدخال الفرق إلى الفاصل الزمني بين هذه المكالمة والمكالمة الأخيرة نظرًا لعدم وجود مكالمة سابقة هنا، وهو Dash، وهو الإدخال الإجمالي يشير إلى إجمالي الوقت الذي تم فيه تشغيل الكود منذ بداية الاختبار حتى هذه المكالمة المحددة. دعونا نلقي نظرة على إخراج المصفوفة التالية:
[1] => المصفوفة
(
[الاسم] => بداية البرنامج النصي
[الوقت] => 1013214253.05761100
[فرق] => 9.8943710327148E-05
[الإجمالي] => 9.8943710327148E-05
)
من الأرقام المذكورة أعلاه يمكننا أن نرى أنه بعد استدعاء $bench->start()، يعمل البرنامج لمدة 9.8943710327148E-05 ثانية (أي 0.0000989 ثانية) قبل استدعاء $bench->setMarker(....).
تجربة اختبار الأداء الحقيقي
على الرغم من أن المثال أعلاه جيد، إلا أنه ليس مثالًا جيدًا لتحديد كيفية تحسين تصميم التعليمات البرمجية لموقعك. سأستخدم أدناه تجربتي الشخصية كفني موقع ويب لتوضيح كيفية حل مشكلات الأداء.
لا أفهم حقًا الكود الذي يستخدمه موقع الويب، لأنه تم تطويره على مدار سنوات عديدة بناءً على احتياجات محددة - تحتوي إحدى الوحدات على رمز تحويل موقع الويب، ووحدة أخرى تسجل استخدام موقع الويب، والوحدات الأخرى لها الخاصة بها دور كل منهما. أدركنا أنا والمطور الرئيسي للموقع أن كود الموقع بحاجة إلى التحسين، لكننا لم نعرف ما هي المشكلة.
من أجل إكمال المهمة في أسرع وقت ممكن، بدأت في دراسة كود البرنامج النصي الرئيسي للموقع، وأضفت بعض أوامر $bench->setMarker() إلى جميع أكواد البرنامج النصي وملفاتها المضمنة، ثم قمت بتحليل مخرجات $bench ->getProfiling()، وتفاجأت بالنتائج. وتبين أن المشكلة تكمن في استدعاء دالة تتعلق برمز التحويل للحصول على اسم لغة معين (مثل en للغة الإنجليزية)، والذي تم استخدامه مئات المرات. في كل صفحة. في كل مرة يتم استدعاء هذه الوظيفة، يقوم البرنامج النصي بالاستعلام عن قاعدة بيانات MySQL للحصول على اسم اللغة الفعلي من جدول قاعدة البيانات.
لذلك قمنا بإنشاء نظام تخزين مؤقت لهذا النوع من المعلومات. وبعد يومين فقط من العمل، قمنا بتحسين أداء النظام بشكل كبير، وزاد عدد مشاهدات الصفحة بنسبة 40% في الأسبوع الأول. بالطبع، هذا مجرد مثال واحد لكيفية تحسين تحليل التعليمات البرمجية لأداء تطبيق الإنترنت أو موقع الويب.
استدعاء وظيفة اختبار الأداء
على الرغم من أن Benchmark_Timer() مفيد بشكل خاص عند تحليل البرنامج النصي أو صفحة الويب (والملفات التي تحتوي عليها)، إلا أنه ليس علميًا لأنه يجب علينا تحميل البرنامج النصي عدة مرات للحصول على البيانات التي تم تحليلها، كما أنه ليس خاصًا بفئة أو وظيفة معينة . مُسَمًّى.
هناك فئة أخرى في مكتبة PEAR::Benchmark تسمى Benchmark_Iterator يمكنها حل هذه المشكلة بشكل جيد للغاية، ويمكنها عرض معلومات التحليل لوظيفة أو طريقة فئة معينة. والغرض منه هو أن نكون قادرين على الحصول على نتائج متسقة من الاختبارات لأننا نعلم أنه إذا قمنا بتشغيل برنامج نصي مرة واحدة واستغرق تشغيله 10 ثوانٍ، فهذا لا يعني أنه سيستغرق دائمًا 10 ثوانٍ للتشغيل في كل مرة.
وعلى أية حال، دعونا نرى بعض الأمثلة:
// كود الاتصال بقاعدة البيانات
include_once("DB.php");
$dsn = صفيف(
'phptype' => 'mysql',
'hostspec' => 'مضيف محلي',
'قاعدة البيانات' => 'اسم_قاعدة البيانات',
'اسم المستخدم' => 'اسم_المستخدم'،
'كلمة المرور' => 'كلمة المرور'
);
$dbh = DB::connect($dsn);
الدالة getCreatedDate($id)
{
عالمي $dbh;
> $stmt = "حدد تاريخ الإنشاء من المستخدمين حيث المعرف=$id";
// استخدم PEAR::DB هنا
$created_date = $dbh-> getOne($stmt);
إذا ((PEAR::isError($created_date)) ||
(فارغ($created_date))) {
عودة كاذبة.
} آخر {
إرجاع تاريخ الإنشاء $؛
}
}
include_once 'Benchmark/Iterate.php';
$bench = new Benchmark_Itrate;
// قم بتشغيل وظيفة getDate 10 مرات
$bench-> run(10, 'getCreatedDate', 1);
// طباعة معلومات التحليل
print_r($bench->get());
?>
يؤدي تشغيل الكود أعلاه إلى نتائج مشابهة لما يلي:
صفيف
(
[1] => 0.055413007736206
[2] => 0.0012860298156738
[3] => 0.0010279417037964
[4] => 0.00093603134155273
[5] => 0.00094103813171387
[6] => 0.00092899799346924
[7] => 0.0010659694671631
[8] => 0.00096404552459717
[9] => 0.0010690689086914
[10] => 0.00093603134155273
[يعني] => 0.0064568161964417
[التكرارات] => 10
)
من السهل فهم الأرقام المذكورة أعلاه. يمثل الإدخال المتوسط متوسط الوقت الذي يستغرقه تشغيل الدالة getCreatedDate(). في الاختبار الفعلي، يجب عليك تشغيله 1000 مرة على الأقل، لكن نتائج هذا المثال كافية لتوضيح المشكلة.