
في نظام PHP البيئي ، يوجد حاليًا موصلان لخادم Tarantool: ملحق PECL الرسمي ل
tarantool / tarantool-php المكتوب بلغة C ، و
tarantool-php / client مكتوب بلغة PHP. أنا مؤلف هذا الأخير.
في هذه المقالة ، أود مشاركة نتائج اختبار الأداء لكلتا المكتبات وإظهار كيف يمكنك تحقيق تحسين أداء 3 × 5x (
في الاختبارات الاصطناعية! ) مع الحد الأدنى من التغييرات في التعليمات البرمجية.
ما الذي سنختبره؟
سنختبر الموصلات المتزامنة المذكورة أعلاه التي تم إطلاقها بشكل غير متزامن ومتوازي وغير متزامن. أيضًا ، لا نريد أي تغييرات في شفرة مصدر الروابط. في الوقت الحالي ، هناك العديد من الملحقات المتاحة التي يمكن أن تؤدي المهمة:
- Swoole ، إطار عمل غير متزامن عالي الأداء لـ PHP. المستخدمة من قبل عمالقة الإنترنت مثل علي بابا وبايدو. منذ الإصدار 4.1.0 ، ظهر رابط التشغيل المذهل Swoole \ Runtime :: enableCoroutine () ، مما يسمح "بتحويل مكتبات شبكة PHP المتزامنة إلى مكتبات مشتركة ذات روتين باستخدام سطر واحد من التعليمات البرمجية".
- Async ، امتداد واعد جدًا للعمل غير المتزامن في PHP حتى وقت قريب. لماذا حتى وقت قريب؟ لسوء الحظ ، لأسباب لا أعرفها ، قام المؤلف بحذف المستودع ومستقبل المشروع مشكوك فيه. سأستخدم أحد الشوك. مثل Swoole ، يجعل هذا الملحق من السهل تنشيط الوضع غير المتزامن من خلال استبدال تطبيقات الدفق الافتراضية لـ PHP بنظيرها غير المتزامن. يتم ذلك من خلال الخيار " async.tcp = 1 ".
- بالتوازي مع ذلك ، امتداد جديد تمامًا من جو واتكينز المشهور ، مؤلف مكتبات مثل phpdbg ، apcu ، pthreads ، pcov ، uopz. يوفر الملحق واجهة برمجة تطبيقات متعددة الخيوط لـ PHP ويتم وضعه كبديل لل pthreads. يتمثل أحد القيود المهمة للمكتبة في أنه يعمل فقط مع إصدار ZTS (Zend Thread Safe) من PHP.
كيف سنختبر؟
سنقوم بتشغيل مثيل Tarantool مع تعطيل تسجيل الكتابة المسبق (
wal_mode = لا شيء )
ومخزن مؤقت لشبكة موسعة (
readahead = 1 * 1024 * 1024 ). سيمنع الخيار الأول عمليات الإدخال / الإخراج (IO) على محرك الأقراص ، بينما يسمح الخيار الثاني بقراءة المزيد من الطلبات من المخزن المؤقت لنظام التشغيل وبالتالي تقليل عدد مكالمات النظام.
بالنسبة للمعايير التي تعمل مع البيانات (الإدراج ، الحذف ، القراءة ، إلخ) ، سيتم إنشاء (إعادة) مساحة memtx قبل بدء الاختبار ، وسيتم إنشاء قيم الفهرس الأولية لهذه المساحة بواسطة منشئ التسلسل.
DDL من الفضاء على النحو التالي:
space = box.schema.space.create(config.space_name, { id = config.space_id, temporary = true }) space:create_index('primary', { type = 'tree', parts = {1, 'unsigned'}, sequence = true }) space:format({ {name = 'id', type = 'unsigned'}, {name = 'name', type = 'string', is_nullable = false} })
إذا لزم الأمر ، قبل بدء الاختبار ، يتم ملء المساحة بـ 10000 نقطة من النموذج التالي:
{id, 'tuple_' .. id}
يتم الوصول إلى Tuples باستخدام قيمة المفتاح العشوائي.
المعيار هو طلب واحد للخادم الذي يتم تنفيذه 10000 مرة (الثورات) ، والتي بدورها يتم تنفيذها في التكرار. يتم تكرار التكرارات حتى تكون كل الانحرافات الزمنية بين 5 تكرارات ضمن هامش الخطأ 3٪ *. بعد ذلك ، يتم أخذ النتيجة المتوسطة. بين التكرارات ، هناك توقف لمدة ثانية واحدة لمنع وحدة المعالجة المركزية من الاختناق. يتم تعطيل أداة تجميع مجمعي البيانات المهملة Lua قبل كل تكرار ويتم إجباره على البدء بعد انتهاء التكرار. يتم إطلاق عملية PHP فقط مع الملحقات المطلوبة للمعايير ، مع تمكين التخزين المؤقت للإخراج وتعطيل أداة تجميع مجمعي البيانات المهملة.
* عدد الثورات ، التكرار وعتبة الخطأ يمكن تغييرها في الإعدادات القياسية.بيئة الاختبار
تم إجراء النتائج المنشورة أدناه على MacBookPro (منتصف 2015) مع Fedora 30 (إصدار kernel 5.3.8-200.fc30.x86_64). تم إطلاق Tarantool في عامل ميناء مع إعداد "-
المضيف الشبكة ".
إصدارات الحزمة:Tarantool: 2.3.0-115-g5ba5ed37e
عامل الميناء: 03/19/3 ، بناء a872fc2f86
PHP: 7.3.11 (cli) (بني: 22 أكتوبر 2019 08:11:04)
التارانتول / العميل: 0.6.0
rybakit / msgpack: 0.6.1
ext-tarantool: 0.3.2 (patched) *
تحويلة msgpack: 2.0.3
تحويلة غير متزامن: 0.3.0-8c1da46
تحويلة swoole: 4.4.12
تحويلة موازية: 1.1.3
* لسوء الحظ ، لا يعمل الرابط الرسمي مع PHP> 7.2. لتجميع الامتداد وتشغيله على PHP 7.3 ، اضطررت إلى استخدام تصحيح .النتائج
المزامنة (افتراضي)
يستخدم بروتوكول Tarantool التنسيق الثنائي لـ
MessagePack لتسلسل الرسائل. في موصل PECL ، يتم إخفاء التسلسل في عمق المكتبة ، لذلك
يبدو من المستحيل التأثير على عملية الترميز من كود المستخدم. في المقابل ، يوفر موصل PHP الخالص القدرة على تخصيص عملية الترميز ، إما عن طريق تمديد أحد برامج التشفير القياسية ، أو عن طريق استخدام التطبيق الخاص بك. يتوفر
تشفيران خارج الصندوق: يعتمد
أحدهما على
msgpack / msgpack-php (ملحق MessagePack PECL الرسمي) والآخر يعتمد على
rybakit / msgpack (PHP خالص).
قبل أن ننتقل إلى مقارنة الموصلات ، دعونا نقيس أداء تشفير MessagePack لموصل PHP ، حتى نستخدم أفضل أداء في اختباراتنا:
على الرغم من أن إصدار PHP (Pure) ليس بنفس سرعة امتداد PECL ، إلا أنني أوصي باستخدام
rybakit / msgpack في المشروعات الحقيقية ، لأن ملحق PECL الرسمي ينفذ مواصفات MessagePack جزئيًا فقط (على سبيل المثال ، لا يوجد دعم لأنواع البيانات المخصصة ، وبدون ذلك لا يمكنك استخدام Decimal - نوع بيانات جديد تم تقديمه في Tarantool 2.3) ولديه عدد من
المشكلات الأخرى (بما في ذلك مشكلات التوافق مع PHP 7.4). والمشروع يبدو مهجورا بشكل عام.
لذلك ، دعونا نقيس أداء الموصلات في الوضع المتزامن:
كما ترى من الرسم البياني ، يعمل موصل PECL (Tarantool) بشكل أفضل من موصل PHP (العميل). هذا ليس مفاجئًا ، بالنظر إلى أن هذا الأخير ، بالإضافة إلى تنفيذه بلغة أبطأ ، يعمل فعليًا أكثر: يتم إنشاء كائن
طلب واستجابة جديد مع كل طلب (في حالة تحديد هناك أيضًا
معايير ، وفي حالة التحديث / Upsert هناك
عمليات ) ،
اتصال ،
باكر ومعالج أيضا إضافة بعض النفقات العامة. لا حاجة للقول إن المرونة العالية تأتي بتكلفة. ومع ذلك ، فإن مترجم PHP يظهر الأداء الجيد بشكل عام. على الرغم من وجود اختلاف ، إلا أنه غير مهم وقد يكون أقل مع استخدام التحميل المسبق في PHP 7.4 ، ناهيك عن JIT في PHP 8.
الانتقال الآن. قدم Tarantool 2.0 دعم SQL. دعونا نحاول إجراء عمليات تحديد وإدراج وتحديث وحذف باستخدام بروتوكول SQL ومقارنة النتائج بمكافئات noSQL (ثنائية):
نتائج SQL ليست مثيرة للإعجاب (دعني أذكرك أننا لا نزال نختبر الوضع المتزامن). ومع ذلك ، لن أشعر بالضيق من ذلك قبل الوقت: لا يزال دعم SQL قيد التطوير النشط (على سبيل المثال ، تمت إضافة دعم
البيانات المعدة منذ فترة ليست بطويلة) ، ووفقًا لقائمة
المشكلات ، فإن محرك SQL سوف الحصول على عدد من التحسينات في المستقبل.
المتزامن
حسنًا ، دعنا نرى الآن كيف يمكن أن يساعدنا ملحق Async في تحسين النتائج أعلاه. بالنسبة للبرمجة غير المتزامنة ، يوفر الملحق واجهة برمجة تطبيقات قائمة على أساس coroutines ، والتي سنستخدمها هنا. أولاً ، كما اكتشفنا من خلال الاختبار ، العدد الأمثل للكوروتينات لبيئتنا هو 25:
ثم نشرنا 10000 عملية على أكثر من 25 coroutines ونتحقق من النتيجة:
زاد عدد العمليات في الثانية أكثر من 3 مرات للموصل PHP! للأسف ، فشل موصل PECL في التشغيل مع ext-async.
وماذا عن SQL؟
كما ترى ، في الوضع غير المتزامن ، يقع الاختلاف بين البروتوكول الثنائي و SQL ضمن هامش الخطأ.
Swoole
مرة أخرى ، دعنا نحدد العدد الأمثل للكوروتينات ، هذه المرة لـ Swoole:
لنأخذ 25. الآن ، كرر نفس الخدعة كما في ملحق Async: قم بتوزيع 10،000 عملية بين 25 coroutines. بصرف النظر عن ذلك ، دعنا نضيف اختبارًا آخر ، حيث نقوم بتقسيم كل شيء إلى عمليتين (أي ستنفذ كل عملية 5000 عملية في 25 coroutines). سيتم إنشاء العمليات بمساعدة
Swoole \ Process .
النتائج:
يُظهر Swoole أداءً أقل قليلاً مقارنةً بـ Async عند التشغيل في عملية واحدة ، ولكن مع عمليتين ، تتغير الصورة بشكل كبير (لم يتم اختيار 2 عن طريق الصدفة ، وعلى هذا الجهاز أظهر أفضل عدد من العمليات أفضل نتيجة).
بالمناسبة ، هناك أيضًا واجهة برمجة تطبيقات للتعامل مع العمليات في ملحق Async ، لكنني لاحظت عدم وجود فرق بين إطلاق المعايير في عملية واحدة أو في عدة عمليات (من الممكن أنني ارتكبت بعض الأخطاء).
مزود مقابل البروتوكول الثنائي:
كما هو الحال مع Async ، يتم تسوية الفرق بين العمليات الثنائية وعمليات SQL في الوضع غير المتزامن.
مواز
نظرًا لأن الامتداد الموازي يدور حول الخيوط ، وليس coroutines ، سنقوم بقياس العدد الأمثل من الخيوط المتوازية:
انها 16 على الجهاز الخاص بي. الآن دعنا نقيس الروابط في 16 موضوعًا متوازيًا:
كما ترون ، النتيجة أفضل من الملحقات غير المتزامنة (باستثناء Swoole التي تم إطلاقها باستخدام عمليتين). لاحظ أنه فيما يتعلق بموصل PECL ، لا تتضمن عمليات التحديث و Upsert أي شريط. وذلك لأن هذه العمليات تعطلت بسبب خطأ ، ولست متأكدًا مما يجب إلقاء اللوم عليه: ext-parallel أو ext-tarantool أو كليهما.
الآن دعونا نضيف أداء SQL إلى المقارنة:
هل لاحظت أوجه التشابه مع الرسم البياني للموصلات التي تم إطلاقها بشكل متزامن؟
الكل في واحد
أخيرًا ، دعنا نجمع كل النتائج في رسم بياني واحد لرؤية الصورة الكاملة للملحقات قيد الاختبار. سنضيف اختبارًا جديدًا واحدًا فقط إلى الرسم البياني ، وهو ما لم نقم به بعد: بدء تشغيل تصميمات Async بالتوازي باستخدام Parallel *. لقد
ناقش المؤلفون بالفعل فكرة دمج الامتدادات المذكورة أعلاه ، لكن لم يتم التوصل إلى توافق في الآراء ، لذلك سيتعين علينا القيام بذلك بأنفسنا.
* لقد فشلت في إطلاق كوول coroutines مع Parallel ؛ يبدو أن هذه الملحقات غير متوافقة.الآن ، النتائج النهائية:
استنتاج
في رأيي ، النتائج جيدة إلى حد ما ، ولكن هناك شيء يجعلني أعتقد أننا لم نصل بعد! إذا كانت لديك أية أفكار حول كيفية تحسين المعايير ، فسوف يسعدني مراجعة طلب السحب. يتم نشر جميع التعليمات البرمجية مع تعليمات التشغيل والنتائج في
مستودع مخصص.
أترك الأمر لك لتقرير ما إذا كنت ستحتاج إلى ذلك في مشروع حقيقي ، أود فقط أن أقول إنها كانت تجربة مثيرة سمحت لي بتقدير مقدار ما يمكن للمرء أن يحققه من موصل TCP متزامن بأقل جهد ممكن.