لغة الأسس لغة برمجة متعددة الاستخدامات تجمع بين أداء اللغات المنخفضة ومرونة اللغات المرتفعة وتتيح للمستخدم توسيعها ضمن نطاق برنامجه حسب الحاجة، وقد قدمنا في مقالات سابقة تعريفًا بها وكيفية استخدامها لبناء تطبيقات الويب ويبقى سؤال مهم لم نتطرق له حتى الآن، وهو أداء اللغة مقارنة باللغات الشائعة، وهو ما سنطرق له في هذا المقال الذي سنقارن فيه آداء لغة الأسس مع لغتي بايثون وجافاسكريبت في مجال تطبيقات الويب على مستوى الخادم وواجهة المستخدم.
سنبدأ بتقييم أداء الخادم من خلال تنفيذ برنامج بسيط ومتطابق الوظائف مكتوب باللغات الثلاث، ثم نسلط حملا عليه باستخدام أداة الاختبار JMeter لقياس معدل الاستجابة واستهلاك الموارد. بعد ذلك، ننتقل إلى مقارنة أداء واجهة المستخدم، حيث ستقتصر المقارنة هنا على لغتي جافاسكريبت والأسس نظراً لأن بايثون لا تعمل داخل المتصفح. سنبني واجهة مستخدم باستخدام مكتبة React من جهة، وباستخدام إطار عمل منصة_ويب القائم على لغة الأسس من جهة أخرى، ثم سنحلل أداء التطبيقين من حيث والسلاسة واستهلاك الموارد باستخدام أدوات مراقبة الأداء المدمجة في متصفح فايرفوكس.
وقع اختيارنا على جافاسكريبت وبايثون كونهما الأكثر انتشارا بين المبرمجين فيما يتعلق بتطبيقات الويب، وكذلك الأمر بالنسبة لمكتبة ريآكت التي اخترناها لكونها الأكثر انتشارا بين مكتبات واجهة المستخدم.
مادة المقارنة
التطبيق الذي سنستخدمه للمقارنة تطبيق بسيط يحمل بيانات من قاعدة بيانات ثم يعرضها على المتصفح بصيغة جدول. قاعدة بياناتنا ستحمل بيانات ضحايا سفينة تايتانك، ويمكن تحميل هذه البيانات من هذا الرابط:
لتطبيقنا هذا منفذ بيانات واحد (backend endpoint) يحمل البيانات كاملة من الجدول الذي فيه ما يزيد على 1300 قيد، ويرجعها كاملة للمنادي بصيغة جيسون. أما واجهة المستخدم فستعرض البيانات المحملة من المنفذ البياني بصيغة جدول. لن يوفر برنامجنا إمكانية تعديل البيانات على الخادم، لكن واجهة المستخدم ستظهر أيضًا حقول إدخال تتيح للمستخدم إضافة وتعديل بيانات ضمن واجهة المستخدم فقط ردون إرسال البيانات إلى الخادم، والغرض من هذا مقارنة أداء لغة الأسس ومـنصة_ويب في واجهة المستخدم.
سنستخدم قاعدة بيانات بوستغرس وسنكتب شفرة الخادم باللغات الثلاث، أما واجهة المستخدم فسنكتبها بلغة الأسس مستخدمين مـنصة_ويب وسنكتب النسخة الأخرى من واجهة المستخدم باستخدام جافاسكريبت وريآكت.
يمكن إيجاد الشفرة المصدرية كاملة وباللغات الثلاث في هذا المستودع: GitHub - sarmadka/TitanicExample: An example for comparing Alusus/WebPlatform against Javascript/React.
بيئة المقارنة
سنجري المقارنة على حاسوب محمول بالمواصفات التالية:
| نوع الحاسوب | Asus Zenbook S16 |
| المعالج | AMD Ryzen AI 9 HX 370 |
| الذاكرة | 32 جيجا بايت LPDDR5X 7500 |
| التخزين | Western Digital 1TB NVMe Gen 4 x4 |
| نظام التشغيل | أوبونتو 24.04 |
وسنستخدم الإصدارات التالية فيما يتعلق بجافاسكريبت:
| جافاسكريبت (NodeJS) | 20.12.2 |
| مكتبة express | 4.19.2 |
| مكتبة sequelize | 6.37.3 |
وهذه إصدارات أدوات بايثون:
| بايثون | 3.12.3 |
| مكتبة Flask | 3.1.2 |
| خادم gunicorn | 25.1.0 |
أما إصدارات الأسس فهي كالتالي:
| الأسس | 0.14.2 |
| مكتبة مـنصة_ويب | 0.7.1 |
| مكتبة صـفوف | 0.3.3 |
مقارنة أداء الخادم
سنبدأ أول مقارنة للخادم باستخدام مسلك واحد (thread) نشغله لمدة 10 ثواني نحسب بعدها عدد الطلبات المكتملة في الثانية.
في المسلك الواحد من JMeter يُرسل الطلب وتُنتظر النتيجة قبل إرسال الطلب التالي، وبالتالي نحن نضمن أن عدد الطلبات على الخادم في أي وقت لن يزيد عن واحد، أي لا تُشغل الطلبات بالتوازي وهذا يضمن لنا مقارنة كفاءة اللغة نفسها دون تأثير عدد المسالك. كانت نتيجة هذا الاختبار كالتالي:
جافاسكريبت: 143.4 طلب \ ثانية
بايثون: 109.5 طلب \ ثانية
الأسس: 313.2 طلب \ ثانية
نلاحظ من هذه النتيجة أن لغة الأسس كانت أسرع من جافاسكريبت بحوالي الضعف، وأسرع من بايثون بثلاثة أضعاف تقريبا. بما أن الطلبات تُرسل بشكل متسلسل وليس متوازي فإن هذه النتيجة تُشير إلى أن مدة إنجاز طلب واحد في لغة الأسس كانت نصف مدة الإنجاز في جافاسكريبت، وثلث مدة الإنجاز في بايثون. أي أن أداء لغة الأسس لم يتفوق فقط في عدد المستخدمين الذين يمكن للخادم تحملهم، وإنما أيضا في سرعة الاستجابة.
سرعة الأداء عامل مهم، لكنه ليس العامل الوحيد المهم، وإنما من المهم أيضًا معرفة استهلاك الذاكرة، خصوصا في حالة الخادم الذي قد يحتاج لخدمة المئات من المستخدمين في نفس الوقت أو ربما أكثر. لقياس استهلاك الذاكرة استخدمنا الأمر time في لينكس الذي يتيح أيضا حساب الحد الأقصى لاستهلاك الذاكرة (high water mark). يتم الأمر عبر تشغيل الخادم باستخدام الأمر time، كما يلي:
/bin/time -v <executable>
بعد تشغيل الخادم باستخدام الأمر time أعدنا تشغيل نفس الاختبار باستخدام JMeter، ثم بعد انتهاء الاختبار أوقفنا تشغيل الخادم وقرأنا نتائج الأمر time. النتيجة التي تهمنا هي أعلى استهلاك لذاكرة RSS. وقد كانت كالتالي للغات الثلاث:
جافاسكريبت: 110.62 ميجا بايت
بايثون: 63.13 ميجا بايت
الأسس: 20.15 ميجا بايت
مرة أخرى تتفوق لغة الأسس على جافاسكريبت وبايثون، وهذا أمر متوقع كونها لغة منخفضة المستوى وتعتمد في إدارة الذاكرة على عدادات السندات (reference counting) بدل الgarbage collection. جدير بالذكر أن جزءا من استهلاك الذاكرة في حالتي الجافاسكريبت والبايثون يعود إلى المترجم نفسه وهو ما لا ينطبق على الأسس التي تتيح الترجمة المسبقة وقد استخدمت في هذا الاختبار الترجمة المسبقة في حالة خادم الأسس. قد يعتقد البعض أن هذه المقارنة ليست عادلة، لكنها كذلك، فهذه ليست مسابقة بين فِرق تطوير اللغات الثلاث، وإنما مقارنة من منظور المستخدم الذي سيحرص عند استخدام الأسس على الترجمة المسبقة لتوفير الموارد، خصوصا أن الضبط المبدئي لمكتبات الأسس تعتمد الترجمة المسبقة في حالة النشر. تجدر الإشارة أيضًا أن استخدام لغة الأسس بنمط JIT يعطي أداءًا مطابقًا لاستخدامها بنمط الترجمة المسبقة، والفرق هنا فقط في الاستهلاك الإضافي للذاكرة الذي يفرضه وجود المترجم نفسه.
في الاختبار الثاني رفعنا عدد المسالك في JMeter إلى 8 بدلا من واحد مع الإبقاء على وقت التنفيذ عند 10 ثواني. في هذه الحالة سُلّط على الخادم 8 طلبات بالتوازي، وكانت النتيجة كالتالي:
جافاسكريبت: 152.3 طلب \ ثانية
بايثون: 116.4 طلب \ ثانية
الأسس: 1566.5 طلب \ ثانية
لاحظ هنا اتساع الفارق بين الأسس ومنافسيها بشكل كبير، وهذا يرجع بالدرجة الأساس لكون الأسس متعددة المسالك (multithreaded) وهو ما لا ينطبق على جافاسكريبت وبايثون. هناك طرق غير مباشرة في جافاسكريبت، وربما بايثون أيضًا، لتشغيل مسالك متعددة في الخادم تعتمد على تشغيل عدة نسخ من الخادم بالتوازي. هذا بالطبع سيرفع الأداء بشكل كبير لكن ستبقى الأسس أسرع إذا تطابقت عدد المسالك المتوفرة، بالإضافة لذلك فإن تشغيل عدة نسخ من الخادم سيكون على حساب رفع استهلاك الذاكرة. وبالحديث عن الذاكرة فقد كان استهلاك الذاكرة كالتالي في حالة 8 مسالك:
جافاسكريبت: 113.94 ميجا بايت
بايثون: 62.84 ميجا بايت
الأسس: 20.34 ميجا بايت
مع رفع عدد المسالك لم يتغير استهلاك الذاكرة كثيرا فما زالت الأسس متصدرة بفارق كبير رغم رفع عدد المسالك، ولو أردنا تشغيل نسخ متعددة من خادم جافاسكريبت وبايثون لدعم عدد أكبر من المستخدمين الآنيين فإن فارق استهلاك الذاكرة سيتسع أكثر مما هو عليه الآن. الغريب في هذه النتيجة أن استهلاك الذاكرة في حالة البايثون كان أقل هذه المرة من حالة المسلك الواحد. يرجع هذا باعتقادي إلى معامل خطأ في قراءة النتائج، أو بعض العشوائية في توقيت تشغيل محرر الذاكرة أو ما شابه.
مقارنة أداء واجهة المستخدم
مقارنة أداء واجهة المستخدم ستعتمد على أمرين: استهلاك الذاكرة وسرعة استجابة واجهة المستخدم في الحالات غير البسيطة. واجهتنا بسيطة جدا لأنها مجرد جدول ونحن نريد محاكاة واجهة أكثر تعقيدا كما أننا نريد تضخيم الحمل على الواجهة لأجل تضخيم الفارق في الأداء إلى حد يسهل ملاحظته باستخدام أدوات مراقبة الأداء التي يقدمها فايرفوكس، لذلك سنعمد إلى زيادة حجم البيانات إلى 8 أضعاف عبر تكرارها من جهة الخادم 8 مرات، أي إرجاع 8 نسخ مكررة من البيانات بدل نسخة واحدة. بعد تحميل الصفحة، سنجري عمليتين في واجهة المستخدم ونقيس سرعة استجابة الواجهة لهاتين العمليتين. العملية الأولى هي إضافة قيد جديد إلى الجدول، والعملية الثانية هي تبديل قيمة حقل في الجدول.
لقياس الأداء اتبعنا الخطوات التالية:
- فتحنا لسان الأداء في أدوات المطور في فايرفوكس، ثم كبسنا على Start recording.
- في موقعنا أدخلنا اسما في حقل الإدخال الثاني في أعلى الصفحة، فصارت قيمة الحقل Sarmad، ثم كبسنا زر Add.
- في نفس حقل الإدخال، أضفنا علامة = بعد الاسم الذي أدخلناه ثم اسمًا جديدا، فصارت قيمة الحقل Sarmad=Sarmad Abdullah، ثم كبسنا على زر Update. يؤدي هذا إلى تبديل القيمة ما قبل علامة = بالقيمة التي بعدها.
- في أدوات المطور في فايرفوكس كبسنا على زر Capture recording.
كررنا الخطوات أعلاه للموقعين وكانت النتائج كما في الصورتين التاليتين. الصورة الأولى لنتيجة أداء الأسس.
في نتيجة الأسس نلاحظ أن معظم الوقت (74%) قُضي في مهمة Refresh Driver tick، وهي العمليات التي تجري داخل محرك فايرفوكس لتحديث الواجهة. نُلاحظ أيضًا أن مقياس relative memory at this time وهي مقدار الزيادة في الذاكرة مقارنة مع وقت بدء التسجيل كان 7.17 ميجا بايت.
فكيف تبدو نتيجة جافاسكريبت + ريآكت بالمقارنة؟ دعونا نرى:
نلاحظ هنا عدة أمور. أول ما يشد الانتباه في هذه اللقطة وجود خطان أحمران في الاعلى وقد أشرنا إليهما بسهمين أخضرين. الخط الأحمر في الخط الزمني يشير إلى الفترة التي تتأخر فيها معالجة الأحداث نتيجة انشغال المتصفح بتنفيذ شفرة الموقع. بمعنى آخر، هذه هي الفترة التي يكون فيها الموقع متجمدا لا يستجب لأوامر المستخدم، وهذا ما لا تجده في نتيجة الأسس. الأمر الثاني الجدير بالملاحظة هو ما أشرنا إليه بسهم أزرق، وهو أن الجزء الأكبر من وقت المعالج قُضي في تنفيذ شفرة الموقع، أي شفرة جافاسكريبت ضمن مكتبة ريآكت بينما في حالة الأسس قُضي الجزء الأكبر في محرك المتصفح. أما الأمر الثالث الذي تجب ملاحظته وقد أشرنا إليه بسهم أصفر فهو مقدار relative memory at this time والذي قيمته هنا 336 ميجا بايت بينما كانت قيمته في حالة الأسس 7.17 ميجا بايت!
ماذا عن الاستهلاك الكلي للذاكرة؟ يمكن قياس ذلك باستخدام خيارات الذاكرة (في لسان Memory) في أدوات المطور في فايرفوكس عبر كبس زر Take snapshot لالتقاط صورة لاستخدام الذاكرة ومقارنتها بين الموقعين. في كل موقع قسنا استهلاك الذاكرة في مرحلتين، الأولى مباشرة بعد تحميل الموقع، والثانية بعد إجراء العمليتين المذكورتين أعلاه، أي إضافة قيد، ثم تبديل قيمته. وقد كانت النتائج كالتالي:
| الأداة | الوصف | استهلاك الذاكرة (ميجا بايت) |
|---|---|---|
| الأسس | عند تحميل الموقع | 468.98 |
| الأسس | بعد إجراء العمليتين | 469.16 |
| جافاسكريبت | عند تحميل الموقع | 496.14 |
| جافاسكريبت | بعد إجراء العمليتين | 560.46 |
نلاحظ هنا أن استهلاك الذاكرة في حالة الأسس ابتداءا كان أقل من جافاسكريبت بقليل (468 مقارنة بـ 496)، وهذا غير مستغرب فمعظم الاستهلاك عائد لحجم البيانات الضخم نسبيا، والبيانات متطابقة في الحالتين. لكن، الوضع اختلف بعد إجراء العمليتين. في حالة الأسس لم يرتفع الاستهلاك كثيرا (468.98 مقارنة بـ469.16) وهذا متوقع، فالعمليتين بسيطتين ولا تستوجبان استهلاكا كبيرا في الذاكرة، بينما في حالة الجافاسكريبت والريآكت ارتفع الاستهلاك من 496.14 إلى 560.46، أي زيادة بمقدار 64 ميجا بايت تقريبا، وهذه زيادة كبيرة نسبة إلى بساطة العملية.
كما كان الحال في شفرة الخادم، تغلبت الأسس في واجهة المستخدم أيضًا، وبكلا المقياسين، الأداء واستهلاك الذاكرة. تجدر الإشارة هنا أن الفارق في الأداء لا يعود فقط إلى الفارق ما بين اللغتين، وإنما أيضًا إلى الفارق ما بين مكتبتي واجهة المستخدم. مكتبة ريآكت تعتمد تصميما معقدا وثقيلا على الموارد، بينما مكتبة مـنصة_ويب التي تعتمدها الأسس تتبع تصميما يراعي استهلاك الموارد.
خاتمة
رغم أن هذه المقارنات ليست شاملة، إلا أنها تعطي لمحة جلية عن الفائدة التي تقدمها لغة الأسس من ناحية الأداء واستهلاك الموارد، وفي هذا الزمن الذي تتصاعد فيه أسعار أشباه الموصلات نجد أن لغة الأسس تقدم فائدة مهمة للمبرمجين تساعدهم على خفض التكاليف عبر تقليل حجم الموارد المطلوبة مثل الذاكرة وسرعة المعالج. لغة الأسس لم تكتفِ بتقديم أداء مذهل، بل قدمت هذا الأداء مرفقا بسهولة استخدام توازي تلك التي تقدمها اللغات المرتفعة المستوى، بل لا أظننا نبالغ بالقول أن الأسس تفوقت على الجافاسكريبت والبايثون في مجال السهولة والوضوح أيضًا، على الأقل في بعض المواضع.
انتظرونا في مقالات قادمة نتطرق فيها للمزيد من الجوانب التي تفعلها لغة الأسس بشكل صحيح وربما فريد أيضًا.








