تسريع موصلات PHP لـ Tarantool باستخدام Async وSwoole وParallel

تسريع موصلات PHP لـ Tarantool باستخدام Async وSwoole وParallel

يوجد حاليًا في نظام PHP البيئي موصلان للعمل مع خادم Tarantool - وهذا هو امتداد PECL الرسمي Tarantool/tarantool-php، مكتوب في C، و Tarantool-php/client، مكتوب بلغة PHP. أنا مؤلف هذا الأخير.

في هذه المقالة، أود مشاركة نتائج اختبار أداء كلتا المكتبتين وإظهار كيف يمكنك، مع الحد الأدنى من التغييرات في الكود، تحقيق زيادة في الأداء بمقدار 3-5 (في الاختبارات الاصطناعية!).

ماذا سنختبر؟

سنقوم باختبار تلك المذكورة أعلاه متزامن تعمل الموصلات بشكل غير متزامن، بالتوازي، وبالتوازي بشكل غير متزامن. 🙂 لا نريد أيضًا أن نلمس رمز الموصلات نفسها. يوجد حاليًا العديد من الإضافات المتاحة لتحقيق ما تريد:

  • سوول - إطار عمل غير متزامن عالي الأداء لـ PHP. يتم استخدامه من قبل عمالقة الإنترنت مثل علي بابا وبايدو. منذ الإصدار 4.1.0 ظهرت طريقة سحرية SwooleRuntime::enableCoroutine()، والذي يسمح لك "بتحويل مكتبات شبكة PHP المتزامنة إلى مكتبات غير متزامنة باستخدام سطر واحد من التعليمات البرمجية."
  • كان Async حتى وقت قريب امتدادًا واعدًا جدًا للعمل غير المتزامن في PHP. لماذا حتى وقت قريب؟ لسوء الحظ، لسبب غير معروف بالنسبة لي، قام المؤلف بحذف المستودع والمصير المستقبلي للمشروع غير واضح. سأضطر إلى استخدامه ل من الشوك. مثل Swoole، يتيح لك هذا الامتداد ارتداء بنطالك بسهولة بنقرة من معصمك لتمكين التزامن عن طريق استبدال التنفيذ القياسي لتدفقات TCP وTLS بإصداراتها غير المتزامنة. ويتم ذلك من خلال الخيار "غير متزامن.tcp = 1".
  • موازية - امتداد جديد إلى حد ما من جو واتكينز الشهير، مؤلف مكتبات مثل phpdbg، apcu، pthreads، pcov، uopz. يوفر الامتداد واجهة برمجة التطبيقات (API) لمؤشرات الترابط المتعددة في PHP ويتم وضعه كبديل لـ pthreads. أحد القيود المهمة على المكتبة هو أنها تعمل فقط مع إصدار ZTS (Zend Thread Safe) من PHP.

كيف سنختبر؟

لنبدأ تشغيل مثيل Tarantool مع تعطيل تسجيل الكتابة المسبقة (wal_mode = لا شيء) وزيادة المخزن المؤقت للشبكة (القراءة الأمامية = 1*1024*1024). سيؤدي الخيار الأول إلى إلغاء العمل مع القرص، والثاني سيجعل من الممكن قراءة المزيد من الطلبات من المخزن المؤقت لنظام التشغيل وبالتالي تقليل عدد مكالمات النظام.

بالنسبة للمعايير التي تعمل مع البيانات (الإدراج، الحذف، القراءة، وما إلى ذلك)، قبل بدء المعيار، سيتم (إعادة) إنشاء مساحة 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}})

إذا لزم الأمر، قبل تشغيل المعيار، يتم ملء المساحة بـ 10,000 صف من النموذج

{id, "tuplе_<id>"}

يتم الوصول إلى الصفوف باستخدام قيمة مفتاح عشوائي.

المعيار نفسه عبارة عن طلب واحد إلى الخادم، والذي يتم تنفيذه 10,000 مرة (دورات)، والتي بدورها يتم تنفيذها في تكرارات. يتم تكرار التكرارات حتى تصبح الانحرافات الزمنية بين 5 تكرارات ضمن خطأ مقبول قدره 3%*. وبعد ذلك يتم أخذ النتيجة المتوسطة. هناك توقف مؤقت لمدة ثانية واحدة بين التكرارات لمنع المعالج من الاختناق. يتم تعطيل أداة تجميع البيانات المهملة الخاصة بـ Lua قبل كل تكرار ويتم إجبارها على البدء بعد اكتمالها. يتم إطلاق عملية PHP فقط مع الامتدادات اللازمة للمعيار، مع تمكين التخزين المؤقت للمخرجات وتعطيل أداة تجميع البيانات المهملة.

* يمكن تغيير عدد الثورات والتكرارات وعتبة الخطأ في إعدادات المعيار.

بيئة الاختبار

تم إجراء النتائج المنشورة أدناه على جهاز MacBookPro (2015)، نظام التشغيل - Fedora 30 (إصدار النواة 5.3.8-200.fc30.x86_64). تم إطلاق Tarantool في عامل الإرساء باستخدام المعلمة "--network host".

إصدارات الحزمة:

Tarantool: 2.3.0-115-g5ba5ed37e
عامل الإرساء: 19.03.3، الإصدار a872fc2f86
PHP: 7.3.11 (cli) (تاريخ البناء: 22 أكتوبر 2019 الساعة 08:11:04)
تارانتوول/العميل: 0.6.0
ريباكيت/msgpack: 0.6.1
تحويلة Tarantool: 0.3.2 (+ تصحيح لـ 7.3)*
تحويلة-msgpack: 2.0.3
تحويلة غير متزامن: 0.3.0-8c1da46
تحويلة سوول: 4.4.12
الموازي الخارجي: 1.1.3

* لسوء الحظ، الرابط الرسمي لا يعمل مع إصدار PHP > 7.2. لتجميع الامتداد وتشغيله على PHP 7.3، كان عليّ استخدام رقعة.

النتائج

الوضع المتزامن

يستخدم بروتوكول Tarantool تنسيقًا ثنائيًا حزمة الرسائل لتسلسل الرسائل. في موصل PECL، يتم إخفاء التسلسل عميقًا في أعماق المكتبة ويؤثر على عملية التشفير من كود منطقة المستخدم لا يبدو ممكنا. على العكس من ذلك، يوفر موصل PHP النقي القدرة على تخصيص عملية التشفير عن طريق توسيع برنامج التشفير القياسي أو باستخدام التنفيذ الخاص بك. هناك نوعان من برامج التشفير المتاحة خارج الصندوق، أحدهما يعتمد على الآخر msgpack/msgpack-php (الامتداد الرسمي لـMessagePack PECL)، والآخر قيد التشغيل rybakit/msgpack (في PHP النقي).

قبل مقارنة الموصلات، سوف نقوم بقياس أداء برامج ترميز الرسائل الخاصة بموصل PHP وفي اختبارات أخرى سوف نستخدم تلك التي تظهر أفضل نتيجة:

تسريع موصلات PHP لـ Tarantool باستخدام Async وSwoole وParallel
على الرغم من أن إصدار PHP (Pure) أقل سرعة من امتداد PECL، إلا أنني ما زلت أوصي باستخدامه في المشاريع الحقيقية rybakit/msgpack، لأنه في ملحق messagePack الرسمي، يتم تنفيذ مواصفات التنسيق جزئيًا فقط (على سبيل المثال، لا يوجد دعم لأنواع البيانات المخصصة، والتي بدونها لن تتمكن من استخدام Decimal - وهو نوع بيانات جديد تم تقديمه في Tarantool 2.3) وله عدد من الآخرين проблем (بما في ذلك مشكلات التوافق مع PHP 7.4). حسنًا، بشكل عام، يبدو المشروع مهجورًا.

لذلك، دعونا نقيس أداء الموصلات في الوضع المتزامن:

تسريع موصلات PHP لـ Tarantool باستخدام Async وSwoole وParallel
كما يتبين من الرسم البياني، يُظهر موصل PECL (Tarantool) أداءً أفضل مقارنة بموصل PHP (العميل). لكن هذا ليس مفاجئًا، نظرًا لأن الأخير، بالإضافة إلى تنفيذه بلغة أبطأ، يقوم في الواقع بمزيد من العمل: يتم إنشاء كائن جديد مع كل استدعاء أطلب и استجابة (في حالة تحديد - أيضًا المعايير، وفي حالة التحديث/التحديث ― عمليات)، كيانات منفصلة الاتصال, العتال и معالج يضيفون أيضًا النفقات العامة. من الواضح أن المرونة لها ثمن. ومع ذلك، بشكل عام، يُظهر مترجم PHP أداءً جيدًا، على الرغم من وجود اختلاف، إلا أنه غير مهم، وربما سيكون أقل عند استخدام التحميل المسبق في PHP 7.4، ناهيك عن JIT في PHP 8.

هيا لنذهب. أضاف Tarantool 2.0 دعمًا لـ SQL. دعونا نحاول إجراء عمليات التحديد والإدراج والتحديث والحذف باستخدام بروتوكول SQL ومقارنة النتائج مع معادلات noSQL (الثنائية):

تسريع موصلات PHP لـ Tarantool باستخدام Async وSwoole وParallel
نتائج SQL ليست مثيرة للإعجاب للغاية (دعني أذكرك أننا مازلنا نختبر الوضع المتزامن). ومع ذلك، لن أنزعج من هذا في وقت مبكر؛ لا يزال دعم SQL قيد التطوير النشط (مؤخرًا نسبيًا، على سبيل المثال، تمت إضافة الدعم بيانات معدة) و، اذا حكمنا من خلال القائمة مسائل، سيخضع محرك SQL لعدد من التحسينات في المستقبل.

المتزامن

حسنًا، لنرى الآن كيف يمكن أن يساعدنا ملحق Async في تحسين النتائج المذكورة أعلاه. لكتابة برامج غير متزامنة، يوفر الامتداد واجهة برمجة تطبيقات تعتمد على coroutines، والتي سنستخدمها. نكتشف تجريبيًا أن العدد الأمثل من الكوروتينات لبيئتنا هو 25:

تسريع موصلات PHP لـ Tarantool باستخدام Async وSwoole وParallel
"انشر" 10,000 عملية عبر 25 كوروتين وانظر ماذا سيحدث:

تسريع موصلات PHP لـ Tarantool باستخدام Async وSwoole وParallel
زاد عدد العمليات في الثانية بأكثر من 3 مرات Tarantool-php/client!

للأسف، لم يبدأ موصل PECL بالمزامنة الخارجية.

ماذا عن SQL؟

تسريع موصلات PHP لـ Tarantool باستخدام Async وSwoole وParallel
كما ترون، في الوضع غير المتزامن، أصبح الفرق بين البروتوكول الثنائي وSQL ضمن هامش الخطأ.

سوول

مرة أخرى نكتشف العدد الأمثل للكوروتينات، هذه المرة لـ Swoole:
تسريع موصلات PHP لـ Tarantool باستخدام Async وSwoole وParallel
دعونا نتوقف عند 25. دعونا نكرر نفس الخدعة كما هو الحال مع ملحق Async - قم بتوزيع 10,000 عملية بين 25 كوروتين. بالإضافة إلى ذلك، سنضيف اختبارًا آخر سنقسم فيه كل العمل إلى عمليتين (أي أن كل عملية ستؤدي 2 عملية في 5,000 كوروتين). سيتم إنشاء العمليات باستخدام عملية سوول.

النتائج:

تسريع موصلات PHP لـ Tarantool باستخدام Async وSwoole وParallel
يُظهر Swole نتيجة أقل قليلاً مقارنةً بـ Async عند تشغيله في عملية واحدة، ولكن مع عمليتين تتغير الصورة بشكل كبير (لم يتم اختيار الرقم 2 عن طريق الصدفة؛ على جهازي، كانت عمليتان أظهرتا أفضل نتيجة).

بالمناسبة، يحتوي ملحق Async أيضًا على واجهة برمجة التطبيقات للعمل مع العمليات، لكنني لم ألاحظ أي اختلاف عن تشغيل المعايير في عملية واحدة أو أكثر (من الممكن أن أخطأت في مكان ما).

SQL مقابل البروتوكول الثنائي:

تسريع موصلات PHP لـ Tarantool باستخدام Async وSwoole وParallel
كما هو الحال مع Async، يتم التخلص من الفرق بين العمليات الثنائية وعمليات SQL في الوضع غير المتزامن.

موازية

نظرًا لأن الامتداد الموازي لا يتعلق بالكورات، بل يتعلق بالخيوط، فلنقم بقياس العدد الأمثل للخيوط المتوازية:

تسريع موصلات PHP لـ Tarantool باستخدام Async وSwoole وParallel
إنه 16 على جهازي. لنقم بتشغيل معايير الموصل على 16 مؤشر ترابط متوازي:

تسريع موصلات PHP لـ Tarantool باستخدام Async وSwoole وParallel
كما ترون، فإن النتيجة أفضل من الامتدادات غير المتزامنة (بدون احتساب Swoole الذي يعمل على عمليتين). لاحظ أنه بالنسبة لموصل PECL، تكون عمليات التحديث والتحديث فارغة. ويرجع ذلك إلى أن هذه العمليات فشلت بسبب خطأ - لا أعرف ما إذا كان خطأ ext-parallel أو ext-tarantool أو كليهما.

الآن دعونا نقارن أداء SQL:

تسريع موصلات PHP لـ Tarantool باستخدام Async وSwoole وParallel
هل تلاحظ التشابه مع الرسم البياني للموصلات التي تعمل بشكل متزامن؟

معاً

وأخيرًا، دعونا نلخص جميع النتائج في رسم بياني واحد لرؤية الصورة العامة للامتدادات التي تم اختبارها. دعونا نضيف اختبارًا جديدًا واحدًا فقط إلى المخطط، وهو ما لم نقم به بعد - فلنقم بتشغيل Coroutines Async بالتوازي باستخدام Parallel*. فكرة دمج الامتدادات المذكورة أعلاه موجودة بالفعل مناقشة المؤلفين، ولكن لم يتم التوصل إلى توافق في الآراء، سيكون عليك القيام بذلك بنفسك.

* لم يكن من الممكن إطلاق كوروتينات Swoole مع Parallel، ويبدو أن هذه الامتدادات غير متوافقة.

إذن النتائج النهائية:

تسريع موصلات PHP لـ Tarantool باستخدام Async وSwoole وParallel

بدلا من خاتمة

في رأيي، تبين أن النتائج تستحق تماما، ولسبب ما أنا متأكد من أن هذا ليس الحد الأقصى! سواء كنت بحاجة إلى اتخاذ قرار بشأن هذا في مشروع حقيقي لنفسك فقط، سأقول فقط أنه بالنسبة لي كانت تجربة مثيرة للاهتمام تسمح لك بتقييم مقدار ما يمكنك "الضغط عليه" من موصل TCP المتزامن بأقل جهد. إذا كانت لديك أفكار لتحسين المعايير، سأكون سعيدًا بالنظر في طلب السحب الخاص بك. يتم نشر جميع التعليمات البرمجية مع تعليمات الإطلاق والنتائج في ملف منفصل مستودعات.

المصدر: www.habr.com

إضافة تعليق