واسم ام لا واسم؟

نحن في Linkurious نعمل على Linkurious Enterprise. إنها منصة على شبكة الإنترنت تستخدم قوة الرسوم البيانية وأدوات التصور الخاصة بها لمساعدة الشركات والسلطات في جميع أنحاء العالم على مكافحة الجرائم المالية.



إحدى الميزات الرئيسية لـ Linkurious Enterprise هي واجهة تصور الرسوم البيانية سهلة التعلم والاستخدام والمصممة للأشخاص العاديين. في عام 2015 ، بدأنا في تطوير مكتبتنا الخاصة - Ogma ، بسبب إحباطنا من إمكانيات مكتبات JavaScript الحالية لتصور الرسم البياني.











Ogma هي مكتبة JS للعرض عالية الأداء والحاسوبية تهدف إلى تقديم هياكل الشبكة. ربما تكون قد رأيت كيف يتم عرض هياكل الشبكة باستخدام أدوات JavaScript أخرى مثل D3.js أو Sigma.js. كنا نفتقر إلى قدرات هذه الأدوات. كان من المهم بالنسبة لنا أن يكون للحل الذي نستخدمه بعض القدرات المحددة بحيث يلبي متطلبات أداء معينة. لم نعثر على أحدهما أو الآخر في مكتبات الجهات الخارجية. لذلك قررنا تطوير مكتبتنا الخاصة من الصفر.



مهمة





تصور بنية الشبكة



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



لقد وعدت تقنية WebAssembly (Wasm) ، منذ لحظة الإبلاغ عنها لأول مرة ، مطوري الويب بمستوى من الأداء يقارن بشكل إيجابي مع ما كان متاحًا لهم سابقًا. في الوقت نفسه ، لم يحتاج المطورون أنفسهم إلى بذل جهود مفرطة لاستخدام التكنولوجيا الجديدة. كان عليهم فقط كتابة تعليمات برمجية بلغة عالية الأداء يعرفونها ، والتي ، بعد تجميعها ، يمكن تشغيلها في متصفح.



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



ما جذبنا إلى Wasm هو أن هذه التقنية يمكن أن تحل المشكلات التي تواجهنا جيدًا ، باستخدام موارد الذاكرة والمعالج بشكل أكثر كفاءة من JavaScript.



ابحاثنا



بدأ بحثنا بالبحث عن خوارزمية تهدف إلى العمل مع الرسوم البيانية التي يمكن نقلها بسهولة إلى لغات مختلفة باستخدام هياكل بيانات مماثلة.



اخترنا خوارزمية n-body. غالبًا ما يستخدم كخط أساس لمقارنة خوارزميات تصور الرسم البياني للقوة. الحسابات التي يتم إجراؤها وفقًا لهذه الخوارزمية هي الجزء الأكثر كثافة في استخدام الموارد في أنظمة التصور. إذا كان من الممكن تنفيذ هذا الجزء من عمل هذه الأنظمة بكفاءة أكبر من ذي قبل ، فسيكون لهذا تأثير إيجابي للغاية على جميع خوارزميات تصور الرسم البياني للقوة المطبقة في Ogma.



المعيار



هناك أكاذيب وأكاذيب فادحة ومعايير.



ماكس دي مارزي



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



في حالتنا ، كان الهدف من المعيار هو حل مشكلة واحدة محددة جيدًا ، في فحص أداء تنفيذ خوارزمية n-body.



إنها خوارزمية واضحة وموثقة جيدًا تستخدمها المؤسسات المحترمة لقياس الأداء.لغات البرمجة.



كما هو الحال مع أي اختبار عادل ، حددنا مسبقًا بعض القواعد للغات المختلفة التي ندرسها:



  • يجب أن تستخدم التطبيقات المختلفة للخوارزمية هياكل رمز متشابهة.
  • يحظر استخدام عمليات متعددة أو خيوط متعددة.
  • يحظر استخدام SIMD .
  • يتم اختبار الإصدارات المستقرة فقط من المترجمات. يحظر استخدام إصدارات مثل ليلا ، بيتا ، ألفا ، ما قبل ألفا.
  • يتم استخدام أحدث إصدارات المترجم فقط لكل لغة.


بعد أن قررنا القواعد ، يمكننا بالفعل المضي قدمًا في تنفيذ الخوارزمية. ولكن قبل القيام بذلك ، كان علينا أيضًا اختيار اللغات التي سنبحث في أدائها.



منافسي JS



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





تم تنفيذ خوارزمية n-body بهذه اللغات الثلاث. تمت مقارنة أداء التطبيقات المختلفة بأداء تنفيذ JavaScript الأساسي.



في كل تطبيق من تطبيقات الخوارزمية ، استخدمنا 1000 نقطة وقمنا بتشغيل الخوارزمية بعدد مختلف من التكرارات. قمنا بقياس الوقت المستغرق لإكمال كل جلسة برنامج.



تم إجراء الاختبارات باستخدام البرامج والأجهزة التالية:



  • NodeJS 12.9.1
  • Chrome 79.0.3945.130 (الإصدار الرسمي) (64 بت)
  • clang 10.0.0 - لإصدار الخوارزمية المطبقة في لغة C.
  • emcc 1.39.6 - الواجهة الأمامية لاستدعاء مترجم Emscripten من سطر الأوامر ، وهو بديل لـ gcc / clang ، بالإضافة إلى رابط
  • البضائع 1.40.0
  • حزمة الوصم 0.8.1
  • أسيمبلي سكريبت 0.9.0
  • MacOS 10.15.2
  • ماك بوك برو 2017 شبكية العين
  • Intel Dual Core i5 2،3 GHz، 8GB DDR3، 256GB SSD


كما ترون ، بالنسبة للاختبارات ، اخترنا ليس أسرع كمبيوتر ، لكننا نختبر Wasm ، أي الكود الذي سيتم تنفيذه في سياق المتصفح. وعلى أي حال ، لا يستطيع المتصفح عادةً الوصول إلى جميع نوى المعالجات المتوفرة في النظام وجميع ذاكرة الوصول العشوائي.



لجعلها أكثر إثارة للاهتمام ، أنشأنا عدة إصدارات من كل تطبيق للخوارزمية. في مرحلة ما من نظام n-body كان لديهم تمثيل رقمي للإحداثيات 64 بت ، في الآخر - 32 بت.



ومن الجدير بالذكر أيضًا أننا استخدمنا تطبيقًا "مزدوجًا" للخوارزمية في Rust. أولاً ، بدون استخدام أي أدوات Wasm ، تمت كتابة تطبيق Rust "محلي" و "غير آمن". لاحقًا ، باستخدام wasm-pack ، تم إنشاء تطبيق "آمن" إضافي ضد الصدأ. كان من المتوقع أن يكون تنفيذ الخوارزمية هذا أسهل للتكامل مع JS ، وأنه سيكون قادرًا على إدارة الذاكرة بشكل أفضل في Wasm.



اختبارات



أجرينا تجاربنا في بيئتين رئيسيتين. هذا هو Node.js والمتصفح (Chrome).



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



بناءً على الكود المصدري المكتوب في AssemblyScript ، تم إنشاء ما يلي:



  • تنفيذ JS الأساسي للخوارزمية.
  • وحدة Wasm.
  • وحدة Asm.js.


من المثير للاهتمام ملاحظة أن وحدة asm.js لم تكن متوافقة تمامًا مع asm.js. حاولنا إضافة توجيه "use asm" إلى الجزء العلوي من الوحدة ، لكن المستعرض لم يقبل هذا التحسين. اكتشفنا لاحقًا أن مترجم binaryen الذي استخدمناه لم يكن يحاول حقًا جعل الكود متوافقًا تمامًا مع asm.js. بدلاً من ذلك ، كان يركز على تشكيل نوع من إصدار JS الفعال من Wasm.



أجرينا الاختبارات أولاً في Node.js.





تشغيل الكود في بيئة Node.js



ثم قمنا بقياس أداء نفس الكود في المتصفح.





تشغيل الكود في متصفح



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





الاختلافات بين التطبيقات الأخرى للخوارزمية وتنفيذ JS (إصدار معياري مع إحداثيات 64 بت من النقاط)





الاختلافات بين التطبيقات الأخرى للخوارزمية وتنفيذ JS (الإصدار المعياري مع إحداثيات نقطة 32 بت)



تختلف الإصدارات المعيارية مع إحداثيات نقطة 64 بت و 32 بت بشكل ملحوظ. قد يقودنا هذا إلى الاعتقاد أنه في JavaScript ، يمكن أن تكون الأرقام كلاهما. الحقيقة هي أن الأرقام في JS ، في تنفيذ الخوارزمية ، التي تؤخذ كأساس للمقارنة ، هي دائمًا 64 بت ، لكن المجمعين الذين يحولون الكود من لغات أخرى إلى Wasm يعملون مع هذه الأرقام بطرق مختلفة.



على وجه الخصوص ، له تأثير كبير على إصدار asm.js للاختبار. إصداره الذي يحتوي على إحداثيات 32 بت هو أدنى بكثير في الأداء من كل من تطبيق JS الأساسي وإصدار asm.js ، الذي يستخدم أرقام 64 بت.



في المخططات السابقة ، من الصعب فهم كيفية ارتباط أداء متغيرات الكود الأخرى بمتغير JS. هذا لأن مقاييس AssemblyScript مختلفة جدًا عن البقية. من أجل فهم هذا ، قمنا ببناء مخطط آخر ، وإزالة نتائج asm.js.





الاختلافات بين تطبيقات الخوارزمية الأخرى من تنفيذ JS (إصدار معياري مع إحداثيات 64 بت من النقاط ، بدون asm.js)





الاختلافات بين التطبيقات الأخرى للخوارزمية وتنفيذ JS (إصدار معياري مع إحداثيات 32 بت من النقاط ، بدون asm.js)



يبدو أن التمثيلات المختلفة للأرقام قد أثرت على الإصدارات الأخرى من الاختبار. لكنهم تأثروا بطرق مختلفة. على سبيل المثال ، المتغير C ، الذي يستخدم أرقام 32 بت (عوامات) ، أصبح أبطأ من المتغير C ، الذي يستخدم أرقام 64 بت (مزدوجة). أصبح كلا الإصدارين Rust من الاختبار بأرقام 32 بت (f32) أسرع من إصداراتهما بأرقام 64 بت (f64).



تنفيذ ضعيف للخوارزمية؟



قد يوحي تحليل البيانات أعلاه بالفكرة التالية. نظرًا لأن جميع تجميعات Wasm المختبرة قريبة جدًا في الأداء من تنفيذ JavaScript ، فهل من الممكن أن تعكس تطبيقات Wasm فقط ميزات أداء تطبيقات الخوارزمية الأصلية؟





مقارنة عمليات التنفيذ الأصلية للخوارزمية مع تنفيذ JavaScript



، دائمًا ما تكون الإصدارات الأصلية من تطبيقات الخوارزمية أسرع من تنفيذ JavaScript.



لاحظنا أيضًا أن تجميعات Wasm أبطأ من الإصدارات الأصلية من الكود المستخدم لإنشاء مثل هذه التجميعات. فرق الأداء هو 20-50٪. لقد اكتشفنا ذلك في نسخة مختصرة من المعيار مع 1000 تكرار.





ج- التنفيذ وما يقابله من الجمعية الواسم





تنفيذ الصدأ وما يقابله من بناء الواسم





تطبيق Rust ، الذي تم إنشاؤه باستخدام wasm-pack ، وتجميع Wasm المقابل.



عند قياس وقت تنفيذ الكود الأصلي ، تم أخذ الوقت المطلوب لتحميل البرنامج في الاعتبار ، وفي حالة متغيرات Wasm ، لم يتم أخذ هذه المرة في الاعتبار.



النتيجة



في المتوسط ​​، كان مكاسب أداء تطبيقين من Rust للخوارزمية ، مقارنة بتنفيذ JS الأساسي ، 20٪. ربما يكون هذا مفيدًا لصورة Rust ، لكنه مكسب أداء ضئيل للغاية مقارنة بالجهد الذي تم الحصول عليه للحصول عليه.



ما هي الاستنتاجات التي يمكننا استخلاصها من هذه الاختبارات؟ ولكن ماذا عن: تتيح لك الكتابة المدروسة لرمز JS الحصول على أداء عالٍ إلى حد ما ولا تتطلب التبديل إلى لغات برمجة أخرى.



تعلم لغات البرمجة الجديدة أمر جيد دائمًا. ولكن يجب أن تكون هناك أسباب وجيهة لتعلم لغات جديدة. غالبًا ما يكون الأداء هو السبب "الخاطئ" ، حيث أن بنية التصميم عالية المستوى أكثر أهمية للأداء من المجمعين أو التحسينات الصغيرة.



كتجربة ، قمنا بتغيير JavaScript إلى TypeScript لكتابة تطبيق لخوارزمية تصور الرسم البياني للقوة. نتيجة لذلك ، قمنا بتحسين جودة قاعدة التعليمات البرمجية ، ولكن ليس الأداء. قمنا بقياس الأداء على وجه التحديد ، واتضح أنه بعد الانتقال ، ارتفع بشكل طفيف ، بنسبة 5٪. ربما يكون السبب هو إعادة هيكلة الكود.



إذا كنت مهتمًا بقضايا تطوير وأداء JavaScript ، فقد تكون مهتمًا بمشاهدة هذا الحديث ، والذي بدا نتائج مشابهة لتلك التي حصلنا عليها.



كيف تتعامل مع تطوير الأجزاء "الثقيلة" من مشاريع الويب؟






All Articles