تعزيز أداء مخططات التداول باستخدام WebAssembly في تطبيقات Deriv

WebAssembly في Deriv: مخططات التداول
تُعتبر المخططات من المكونات الرئيسية لتطبيقات التداول وتُستخدم في عدة تطبيقات لـ Deriv، وهي DTrader وDBot.
هدفنا الرئيسي في المستقبل هو بناء مكتبة مخططات ذات أداء عالٍ وتقديم عرض سلس. نحن حالياً نفكر في استخدام CanvasKit في مخططاتنا لتجاوز حدود الأداء.
CanvasKit هو وحدة WebAssembly تقوم بالرسم على عناصر الكانفاس باستخدام Skia، مما يوفر مجموعة ميزات أكثر تقدمًا من Canvas Web API.
نخطط أيضًا لاستخدام WebAssembly لحساب موضع العلامات في المخطط. تُضاف العلامات إلى المخططات لتحديد وقت الشراء ومستوى السعر الذي قام المستخدم بشراء العقد عنده. يجب إعادة حساب موضع العلامات عند تكبير المخطط وتصغيره.
سنستخدم WebAssembly لحساب موضع العلامات وCanvasKit لرسمها على المخطط لجعل العرض سريعًا وسلسًا.
نجحنا في تطوير إثبات مفهوم لمخططات التداول الحالية باستخدام WebAssembly. في هذه التدوينة، سنشارك الدروس وبعض أفضل الممارسات المتعلقة بـ WebAssembly التي اكتسبناها خلال عملنا على إثبات المفهوم.
ما هو WebAssembly؟
WebAssembly، المختصر Wasm، هي تقنية تتيح تشغيل أكواد منخفضة المستوى وعالية الأداء في المتصفحات. ببساطة، Wasm هو تنسيق لغة برمجة (شجرة تركيب مجردة) وبيئة تنفيذ (آلة مكدس محمولة) يمكن أن يكون هدف تجميع للغات برمجة أخرى. يمكن تضمينه داخل متصفح الويب لتوفير طريقة مشتركة لدعم لغات أخرى كبديل لـ JavaScript.
مواصفات WebAssembly الأساسية تم تسميتها كمعيار رسمي للويب في 5 ديسمبر 2019 من قبل اتحاد الشبكة العالمية (W3C).
لماذا WebAssembly؟
يقدم WebAssembly العديد من المزايا. إنه لغة منخفضة المستوى تشبه اللغة التجميعية مع تنسيق ثنائي مضغوط يعمل بأداء قريب من الأداء الأصلي. يوفر هدفًا للتجميع بحيث يمكن للغات ذات نماذج الذاكرة المنخفضة المستوى مثل C++ وRust العمل على الويب.
يسعى WebAssembly لأن يكون مستقلًا عن العتاد، اللغة، والمنصة. وعليه، يمكنه استهداف جميع البنى الحديثة، سواء أجهزة سطح المكتب أو الأجهزة المحمولة وكذلك الأنظمة المدمجة. يمكن تضمين برامج WebAssembly في المتصفحات، أو تشغيلها كآلة افتراضية مستقلة، أو دمجها في بيئات أخرى.
يمكنك العثور على جميع اللغات والأدوات التي تعمل مع WebAssembly هنا. اللغات البارزة هي C/C++، C#/.NET، Rust، Java، Python، Elixir، وGo.
تصميم WebAssembly يعزز البرامج الآمنة من خلال إزالة الميزات الخطرة من دلالات التنفيذ مع الحفاظ على التوافق مع البرامج المكتوبة لـ C/C++.
يجب على الوحدات الإعلان عن جميع الدوال الممكن الوصول إليها وأنواعها المرتبطة أثناء تحميل الوحدة، حتى عند استخدام الربط الديناميكي.
الأمان
نموذج الأمان في WebAssembly له هدفان مهمان:
- حماية المستخدمين من الوحدات المعيبة أو الخبيثة
- توفير أدوات ومخففات مفيدة للمطورين من أجل تطوير تطبيقات آمنة، ضمن قيود النقطة الأولى أعلاه
الأمان للمستخدمين
تُنفذ كل وحدة WebAssembly داخل بيئة محمية معزولة عن بيئة التشغيل الرئيسية باستخدام تقنيات عزل الأخطاء. ماذا يعني هذا للمستخدم؟
- تعمل التطبيقات بشكل مستقل ولا يمكنها الخروج من البيئة المحمية دون المرور عبر الـ API المناسبة.
- عادة ما تنفذ التطبيقات بشكل حتمي مع بعض الاستثناءات المحدودة.
بالإضافة إلى ذلك، كل وحدة تخضع لسياسات الأمان الخاصة بالإطار الذي تُضمن فيه. داخل متصفح ويب، يشمل ذلك تقييدات على تدفق المعلومات من خلال سياسة الأصل نفسه. على منصة غير الويب، قد يشمل ذلك نموذج أمان POSIX.
الأمان للمطورين
تصميم WebAssembly يعزز البرامج الآمنة من خلال إزالة الميزات الخطرة من دلالات التنفيذ مع الحفاظ على التوافق مع البرامج المكتوبة لـ C/C++.
يجب على الوحدات الإعلان عن جميع الدوال الممكن الوصول إليها وأنواعها المرتبطة أثناء تحميل الوحدة حتى عند استخدام الربط الديناميكي. هذا يسمح بالتنفيذ الضمني ل سلامة تدفق السيطرة
(CFI) من خلال تدفق تحكم منظم. بما أن الكود المجمّع غير قابل للتغيير وغير مرئي أثناء وقت التشغيل، فإن برامج WebAssembly محمية من هجمات اختطاف تدفق السيطرة.
قابلية التدفق
تقوم الدالة WebAssembly.instantiateStreaming()
بتجميع واستنساخ وحدة WebAssembly مباشرة من مصدر يتم تدفقه. هذه هي الطريقة الأكثر كفاءة وتحسينًا لتحميل كود Wasm.
كتابة كود WebAssembly

WebAssembly له تنسيق ثنائي وتنسيق نصي. التنسيق الثنائي (.wasm) هو تنسيق تعليمات ثنائي مضغوط لآلة افتراضية معتمدة على المكدس، وصمم ليكون هدف تجميع محمول للغات عالية المستوى مثل C، C++، Rust، C#، Go، Python، والعديد غيرها. التنسيق النصي (.wat) هو تنسيق مقروء للبشر مصمم لمساعدة المطورين على مشاهدة مصدر وحدة WebAssembly. يمكن أيضًا استخدام التنسيق النصي لكتابة أكواد يمكن تجميعها إلى التنسيق الثنائي.
الكتابة المباشرة لكود Wasm
من الممكن كتابة WebAssembly يدويًا.
(module
(func $print (param $numl i32) (result i32)
get_local $num1
)
(export "print" (func $print)
)
الكود التالي كتب باستخدام WebAssembly. هناك دالة تأخذ معلمة رقم واحد (عدد صحيح 32-بت) وتعيده. ثم تقوم بتصدير الدالة print كـ print
إلى بيئة المضيف.
سيبدو كود WebAssembly المجمّع كما يلي:

الجزء الأول هو كود التجميع الموجه لأجهزة x86 والجزء الثاني هو المخرجات الثنائية للكود.
دعونا نتعمق قليلاً في مثال آخر لـ Wasm. تخيل أن لدينا مشروعًا به هيكل مثل هذا:

كما قد تعلم، هذا مشروع واجهة مستخدم أساسي --- باستثناء ملف wat
الذي سأشرحه قريباً.
مقتطف الكود التالي هو ملف main.wat
:

تأخذ هذه القطعة من WebAssembly معامليْن، تجمعهما معًا، وتُعيد النتيجة.
Wasm هي آلة مكدس تعتمد على هيكل بيانات FILO (الأخير يدخل أولاً يخرج). في المثال أعلاه، يحتوي المكدس على قيمة i32 واحدة بالضبط --- نتيجة التعبير ($lhs + $rhs) --- والتي يتم التعامل معها بواسطة i32.add
. قيمة الإرجاع للدالة هي ببساطة القيمة النهائية المتبقية على المكدس.
وهكذا يمكننا استيراد واستخدام كود Wasm داخل ملف main.js
الخاص بنا:

هذا المثال يستخدم طريقة غير التدفقية لـ Wasm. هذه الطريقة لا تصل مباشرة إلى الكود الثنائي، لذلك تتطلب خطوة إضافية لتحويل الاستجابة إلى ArrayBuffer قبل التجميع/الاستنساخ لوحدة Wasm.
أخيرًا، نضع قيمة add(1,1)
في عنصر DOM الذي يحمل معرّف container
.
رموز العمليات في WebAssembly
رموز العمليات هي تعليمات لغة الجهاز المحددة للعملية المطلوب تنفيذها. أحد أسباب صغر حجم Wasm هو أن جميع تعليمات عمليات اللغة التي يدعمها تتسع في صفحة واحدة. ألقِ نظرة على هذه الصفحة لرؤية قائمة برموز عمليات Wasm.
LLVM

في هذا القسم، سنتحدث عن كيفية تحقيق التعقيدات المستقلة لهذه المنصة.
LLVM هو مجموعة من تقنيات المجمّع وسلسلة الأدوات، يمكن استخدامها لتطوير واجهة أمامية لأي لغة برمجة وواجهة خلفية لأي بنية تعليمات.
يدعم LLVM حاليًا تجميع Ada وC وC++ وD وDelphi وFortran وHaskell وJulia وObjective-C وRust وSwift.
تصميم مجمّع LLVM

الواجهة الأمامية
تحلل الواجهة الأمامية الكود المصدر لبناء تمثيل داخلي للبرنامج يُسمى التمثيل الوسيط (IR).
التمثيل الوسيط هو هيكل بيانات أو كود يُستخدم داخليًا بواسطة المجمّع أو الآلة الافتراضية لتمثيل الكود المصدر. صمم ليكون مناسبًا للمعالجة اللاحقة، مثل التحسين والترجمة. تدير الواجهة الأمامية أيضًا جدول الرموز.
جدول الرموز هو هيكل بيانات يستخدمه المترجم اللغوي. تخزن مدخلات جدول الرموز المعلومات المتعلقة بالمعرّف المقابل (أو الرموز) والثوابت والإجراءات والدوال.
المرحلة الوسطى
المرحلة الوسطى، المعروفة أيضًا باسم المُحسّن، تقوم بإجراء تحسينات على التمثيل الوسيط لتحسين الأداء وجودة كود الآلة الناتج.
تشمل المراحل الرئيسية للمرحلة الوسطى ما يلي:
- التحليل --- يتم جمع معلومات البرنامج من التمثيل اللغوي الوسيط المستمد من الواجهة الأمامية. تحليل تدفق البيانات، تحليل التبعيات، تحليل الرموز الموازية، تحليل المؤشرات، وتحليل الهروب هي بعض مهامه.
- التحسين --- يتم تحويل التمثيل الوسيط إلى أشكال مكافئة وظيفياً ولكنها أسرع (أو أصغر). التحسينات الشائعة تشمل التوسيع المضمّن، إزالة الكود الميت، انتشار الثوابت، تحويل الحلقات وحتى التوازي التلقائي.
الواجهة الخلفية
الواجهة الخلفية مسؤولة عن تحسينات خاصة ببنية معالج CPU وتوليد الكود ليعمل على CPU.
تشمل المراحل الرئيسية للواجهة الخلفية ما يلي:
- تحسينات تعتمد على الآلة: تحسينات تعتمد على تفاصيل بنية CPU التي يستهدفها المترجم.
- توليد الكود: يتم تحويل اللغة الوسيطة المحولة إلى لغة الإخراج، عادةً ما تكون اللغة الأصلية لنظام التشغيل.
Emscripten

Emscripten هو مجموعة أدوات تجميع مفتوحة المصدر كاملة لـ WebAssembly.
باستخدام Emscripten يمكنك:
- تجميع كود C وC++، أو أي لغة أخرى تستخدم LLVM، إلى WebAssembly، وتشغيله على Web، Node.js، أو بيئات تشغيل Wasm أخرى.
- تجميع بيئات تشغيل C/C++ للغات أخرى إلى WebAssembly، ثم تشغيل الأكواد في تلك اللغات بطريقة غير مباشرة
يمكن تجميع أي قاعدة كود محمولة لـ C أو C++ إلى WebAssembly باستخدام Emscripten، بدءًا من الألعاب عالية الأداء التي تحتاج إلى عرض الرسومات وتشغيل الصوت وتحميل ومعالجة الملفات وصولًا إلى أُطُر التطبيقات مثل Qt. وقد تم استخدام Emscripten بالفعل لتحويل قائمة طويلة من قواعد الكود الواقعية إلى WebAssembly، بما في ذلك قواعد الكود التجارية مثل Unreal Engine 4 وUnity engine.
لرؤية الأدوات التي تستخدم Emscripten تحت الغطاء، يمكننا الاطلاع على Pyodide، وهي أداة توزيع Python للمتصفح وNode.js تعتمد على WebAssembly، وهناك أيضًا sql.js التي سنناقشها بمزيد من التفصيل.
sql.js

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

كما ترى في لقطة الكود، قمنا ببساطة بحقن ملف الحزمة sql-wasm.js
داخل ملف HTML. نظرًا لأن لدينا وصولًا عالميًا إلى الدالة initSqlJs، كل ما يتطلبه الأمر هو كائن تكوين للحصول على الوصول إلى كائن SQL داخل نطاقه. ثم يمكننا ببساطة إنشاء مثيل جديد وتشغيل أي استعلام SQL هناك.
يمكنك العثور على المزيد من الأمثلة أدناه:
https://sql.js.org/examples/GUI/
https://react-sqljs-demo.ophir.dev/
واجهة نظام WebAssembly (WASI)

حان الوقت الآن لمعرفة كيف يمكن تشغيل Wasm خارج المتصفح. يمكن تشغيله على خادم، blockchain، أو أي بنية مختلفة أخرى. في الوقت الحالي، سنرى كيف يمكن تشغيله على خادم.
WASI هي واجهة نظام للغة WebAssembly. تسمح لك بتشغيل WebAssembly خارج متصفحك.
هي مجموعة من APIs موحدة للتواصل مع نظام التشغيل
WASI اختصار لـ WebAssembly System Interface. إنها API صممها مشروع Wasmtime توفر الوصول إلى عدة ميزات تشبه نظام التشغيل، بما في ذلك الملفات وأنظمة الملفات، مآخذ Berleley، الساعات، والأرقام العشوائية التي سنقترحها للتوحيد.
صممت لتكون مستقلة عن المتصفحات، لذا لا تعتمد على Web APIs أو JS، وليست مقيدة بالحاجة إلى التوافق مع JS. وتحتوي على أمان قائم على القدرات مدمج، لذا توسع عزل WebAssembly النموذجي ليشمل الإدخال/الإخراج.
راجع نظرة عامة على WASI لمزيد من المعلومات التفصيلية، والدليل التعليمي لـ WASI لشرح كيفية عمل الأجزاء المختلفة معًا.
لاحظ أن كل شيء هنا هو نموذج أولي، وبالرغم من أن الكثير يعمل، هناك العديد من الميزات المفقودة وبعض النقاط غير المكتملة. على سبيل المثال، دعم الشبكات غير مكتمل.
هناك بعض بيئات التشغيل التي تنفذ هذا المعيار API:
لماذا WASI
1. قابل للنقل: يمكنك تجميعه مرة واحدة ثم تشغيله على بنى CPU مختلفة.
2. آمن: يستخدم WASI صندوق رمل خفيف الوزن، لذلك له وصول محدود إلى موارد النظام.
3. قابل للتشغيل البيني: على سبيل المثال، يمكن لكود مكتوب بـ RustLang التحدث إلى كود مكتوب بـ GoLang أو C++.
قابلية النقل
يمكن لأداة Clang تحديد معالج CPU المستهدف وسيتم توجيه الكود المصدر لتلك البنية المحددة.

من ناحية أخرى، مع WebAssembly، يمكننا تجميع الكود مرة واحدة وتشغيله على عدد كبير من الأجهزة المختلفة. الملفات الثنائية قابلة للنقل.

الأمان

طريقة WebAssembly في معالجة الأمان مختلفة. WebAssembly يعمل في صندوق رمل.
هذا يعني أن الكود لا يمكنه التحدث مباشرةً إلى نظام التشغيل. ولكن كيف يتعامل مع موارد النظام؟ المضيف (قد يكون متصفحًا أو بيئة تشغيل Wasm) يضع دوال داخل صندوق الرمل التي يمكن للكود استخدامها.
هذا يعني أن المضيف يمكنه تحديد ما يمكن للبرنامج فعله على أساس كل برنامج على حدة. لا يسمح للبرنامج بالعمل نيابةً عن المستخدم، من خلال اتخاذ أي نداء نظام بصلاحيات المستخدم الكاملة.
وجود آلية للصندوق الرمل لا يجعل النظام آمنًا في حد ذاته --- المضيف يمكنه دائمًا وضع كل الصلاحيات في الصندوق، وفي تلك الحالة لن نكون أفضل حالاً --- ولكنه على الأقل يمنح المضيفين خيار إنشاء نظام أكثر أمانًا.
لنرَ مثالًا يستخدم Wasm.
تخيل أن لدينا هذه الدالة المكتوبة باستخدام Rust التي تأخذ قيمتيْ عدد، تجمعهما، وتُعيد النتيجة.

يمكننا محاولة تشغيل هذا الكود في برنامج Python. ما نحتاجه هو تثبيت حزمة wasm-pack
على جهازنا. ثم يمكننا ببساطة تشغيل هذا الأمر على الملف أعلاه:
wasm-pack build --target bundler
ينتج تمثيلًا نصيًا لكود Rust في Wasm --- وفي حالتنا، يقع هذا في /pkg/webassembly_bg.wasm
--- الذي يمكننا تضمينه في كود Python كما يلي:

تشغيل برنامج مع وبدون Wasm

سيتم استدعاء هذه الدالة مئة مليون مرة. هي فقط تضيف 2 إلى كل رقم من 0 إلى 100,000,000 وتجمع الناتج.
في هذه الحالة على جهازي، استغرق تنفيذ هذه الدالة حوالي 5 ثوانٍ.
من ناحية أخرى، لدينا هذا المقتطف البرمجي في Python الذي يستخدم التمثيل الثنائي لـ Wasm الخاص بـ Rust، وينفذ نفس العملية.

الفارق كبير من حيث السرعة. تم إعطائي النتيجة في أقل من 4 ميلي ثانية! وهذا رائع.
الحالة الحالية لـ Wasm
حاليًا، تستخدم العديد من المشاريع الكبيرة WebAssembly تحت الغطاء. بعض الأمثلة الجيدة هي Figma وGoogle Earth وShopify وPhotoshop.
انظر القائمة الكاملة للمشاريع الكبرى التي تستخدم WebAssembly.
مثال مذهل على استخدام Wasm على الويب هو نقل كود AutoCad التراثي الذي يبلغ عمره 35 سنة ليعمل بسلاسة على المتصفح.
إذا كنت تحب ألعاب الكمبيوتر، تفقد Doom3. تم نقل اللعبة كاملة لتعمل في المتصفح، وتعمل بشكل ممتاز!
قراءات إضافية