أول مقدمة إلى AssemblyScript

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

يمكن أن يكون إتقان WebAssembly مهمة شاقة لمطوري الويب. ومع ذلك ، يمكن المحول البرمجي AssemblyScript تحسين الموقف.


يقدم مؤلف المقال ، الذي ننشره ترجمة اليوم ، الحديث أولاً عن سبب كون WebAssembly تقنية واعدة للغاية ، ثم نلقي نظرة على كيف يمكن لـ AssemblyScript المساعدة في فتح إمكانات Wasm.

WebAssembly


يمكن أن يسمى WebAssembly لغة منخفضة المستوى للمتصفحات. إنه يمنح المطورين القدرة على إنشاء تعليمات برمجية لتجميعها إلى شيء آخر غير JavaScript. يتيح ذلك للبرامج المضمّنة في صفحات الويب أن تعمل بالسرعة نفسها التي تعمل بها التطبيقات الأصلية لمختلف المنصات. تعمل مثل هذه البرامج في بيئة محدودة وآمنة.

شارك ممثلو الفرق المسؤولة عن تطوير جميع المتصفحات الرائدة (Chrome و Firefox و Safari و Edge) في إنشاء معيار WebAssembly. واتفقوا على بنية النظام في أوائل عام 2017. الآن جميع المتصفحات المذكورة أعلاه تدعم WebAssembly. في الواقع ، يمكن استخدام هذه التكنولوجيا في حوالي 87 ٪ من المتصفحات.

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

عندما ظهر معيار WebAssembly لأول مرة ، ظن بعض المطورين أنه يمكن أن يحل محل JavaScript ويصبح اللغة الرئيسية للويب. ولكن يُنظر إلى WebAssembly كأداة جديدة تتكامل بشكل جيد مع النظام الأساسي الحالي للويب. هذا هو أحد أهدافه ذات الأولوية .

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

  • ألعاب.
  • الحسابات العلمية ، المرئيات ، المحاكاة.
  • تطبيقات CAD.
  • تحرير الصور ومقاطع الفيديو.

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

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

قد ترغب في محاولة استخدام WebAssembly في مشاريعك الخاصة. يمكن تعلم هذه اللغة وكتابتها على الفور . ولكن ، مع ذلك ، تم تطوير WebAssembly أصلاً كهدف تجميع للغات أخرى. تم تصميمه بدعم جيد لـ C و C ++. ظهر دعم تجريبي لـ Was في Go 1.11. يتم بذل الكثير من الجهد في كتابة تطبيقات Wasm في Rust .

لكن من الممكن تمامًا ألا يرغب مطورو الويب في تعلم C أو C ++ أو Go أو Rust فقط لاستخدام WebAssembly. ماذا يفعلون؟ يمكن أن تعطي الإجابة على هذا السؤال AssemblyScript.

AssemblyScript


AssemblyScript هو مترجم يحول كود TypeScript إلى كود WebAssembly. TypeScript هي لغة تم تطويرها بواسطة Microsoft. هذه مجموعة شاملة من جافا سكريبت ، تتميز بدعم محسّن من النوع وبعض الميزات الأخرى. أصبح TypeScript لغة شعبية إلى حد ما. تجدر الإشارة إلى أن AssemblyScript قادر على التحويل إلى Wasm فقط مجموعة محدودة من بنيات TypeScript. هذا يعني أنه حتى أي شخص غير مطلع على TypeScript يمكنه أن يتعلم بسرعة هذه اللغة بمستوى يكفي لكتابة التعليمات البرمجية التي يفهمها AssemblyScript.

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

مثال


دعنا نكتب أول وحدة AssemblyScript الخاصة بنا. يمكن العثور على جميع الكود الذي سنناقشه الآن على جيثب . لدعم WebAssembly ، نحتاج إلى Node.js 8 على الأقل.

إنشاء دليل جديد ، تهيئة مشروع npm وتثبيت AssemblyScript:

mkdir assemblyscript-demo cd assemblyscript-demo npm init npm install --save-dev github:AssemblyScript/assemblyscript 

لاحظ أنه يجب تثبيت AssemblyScript مباشرة من مستودع GitHub للمشروع. لم يتم نشر AssemblyScript بعد في npm ، لأن المطورين لا يعتبرونه جاهزًا للاستخدام على نطاق واسع.

asinit ملفات مساعدة باستخدام الأمر asinit المتضمن في AssemblyScript:

 npx asinit . 

الآن قسم scripts لدينا package.json يجب أن تأخذ الشكل التالي:

 {  "scripts": {    "asbuild:untouched": "asc assembly/index.ts -b build/untouched.wasm -t build/untouched.wat --sourceMap --validate --debug",    "asbuild:optimized": "asc assembly/index.ts -b build/optimized.wasm -t build/optimized.wat --sourceMap --validate --optimize",    "asbuild": "npm run asbuild:untouched && npm run asbuild:optimized"  } } 

index.js ملف index.js الموجود في المجلد الجذر للمشروع كما يلي:

 const fs = require("fs"); const compiled = new WebAssembly.Module(fs.readFileSync(__dirname + "/build/optimized.wasm")); const imports = {  env: {    abort(_msg, _file, line, column) {       console.error("abort called at index.ts:" + line + ":" + column);    }  } }; Object.defineProperty(module, "exports", {  get: () => new WebAssembly.Instance(compiled, imports).exports }); 

يتيح لك هذا تضمين وحدات WebAssembly في التعليمات البرمجية الخاصة بك باستخدام الأمر requ. هذا - بنفس طريقة توصيل وحدات JavaScript المعتادة.

يحتوي مجلد assembly على ملف index.ts . لديها شفرة المصدر مكتوبة وفقا لقواعد AssemblyScript. شفرة الغليان التي يتم إنشاؤها تلقائيًا هي وظيفة بسيطة لإضافة رقمين:

 export function add(a: i32, b: i32): i32 {  return a + b; } 

ربما كنت تتوقع توقيع دالة مماثلة لتبدو وكأنها add(a: number, b: number): number . لذلك سيبدو إذا كان مكتوبًا في برنامج TypeScript العادي. ولكن هنا ، بدلاً من نوع number ، يتم i32 نوع i32 . يحدث هذا لأن كود AssemblyScript يستخدم أنواع WebAssembly محددة للأعداد الصحيحة وأرقام الفاصلة العائمة ، بدلاً من نوع الرقم العام من TypeScript.

لنقم بتجميع المشروع:

 npm run asbuild 

يجب أن تظهر الملفات التالية في مجلد الإنشاء:

 optimized.wasm optimized.wasm.map optimized.wat untouched.wasm untouched.wasm.map untouched.wat 

هناك إصدارات محسنة ومنتظمة للتجميع. يعطينا كل إصدار من التجميع ملفًا .wasm ثنائيًا ، وخريطة خريطة .wasm.map ، وتمثيلًا نصيًا للرمز الثنائي في ملف .wat. العرض التقديمي لاختبار رمز Wasm مخصص للمبرمج ، لكننا لن ننظر حتى إلى هذا الملف. في الواقع ، أحد أسباب استخدام AssemblyScript هو أنه يلغي الحاجة للعمل مع رمز Wasm.

الآن لنقم بتشغيل Node.js في وضع REPL وتأكد من أن وحدة Wasm المترجمة يمكن استخدامها بنفس طريقة استخدام أي وحدة JS عادية:

 $ node Welcome to Node.js v12.10.0. Type ".help" for more information. > const add = require('./index').add; undefined > add(3, 5) 8 

بشكل عام ، هذا هو كل ما يلزم لاستخدام تقنية WebAssembly في Node.js.

تجهيز مشروع مع برنامج نصي مراقب


لإعادة إنشاء الوحدة النمطية تلقائيًا أثناء التطوير عند إجراء تغييرات عليها ، أوصي باستخدام الحزمة onchange . الحقيقة هي أن AssemblyScript ليس لديه بعد نظام خاص به لمراقبة تغييرات الملفات. تثبيت الحزمة onchange:

 npm install --save-dev onchange 

إضافة asbuild:watch البرنامج النصي إلى package.json . يتم تضمين علامة -i في الأمر بحيث تبدأ عملية الإنشاء مرة واحدة عندما يتم استدعاء البرنامج النصي ، قبل حدوث أي أحداث.

 {  "scripts": {    "asbuild:untouched": "asc assembly/index.ts -b build/untouched.wasm -t build/untouched.wat --sourceMap --validate --debug",    "asbuild:optimized": "asc assembly/index.ts -b build/optimized.wasm -t build/optimized.wat --sourceMap --validate --optimize",    "asbuild": "npm run asbuild:untouched && npm run asbuild:optimized",    "asbuild:watch": "onchange -i 'assembly/**/*' -- npm run asbuild"  } } 

الآن ، بدلاً من تشغيل الأمر asbuild باستمرار ، ما asbuild:watch سوى تشغيل asbuild:watch مرة واحدة.

إنتاجية


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

يظهر تنفيذ JS الأساسي لوظيفة مماثلة أدناه. يتم ترتيبها بكل بساطة ، حيث تقوم بالتحقق من الرقم بالقوة الغاشمة ، ولكن لأغراضنا ، يكون هذا مناسبًا ، نظرًا لأنه يؤدي مبالغ كبيرة من الحسابات.

 function isPrime(x) {    if (x < 2) {        return false;    }    for (let i = 2; i < x; i++) {        if (x % i === 0) {            return false;        }    }    return true; } 

وظيفة مماثلة ، مكتوبة من أجل مترجم AssemblyScript ، تبدو هي نفسها تقريبا. الفرق الرئيسي هو وجود تعليقات توضيحية في الكود:

 function isPrime(x: u32): bool {    if (x < 2) {        return false;    }    for (let i: u32 = 2; i < x; i++) {        if (x % i === 0) {            return false;        }    }    return true; } 

لتحليل أداء الكود ، سنستخدم حزمة Benchmark.js . تثبيته:

 npm install --save-dev benchmark 

قم benchmark.js ملف benchmark.js :

 const Benchmark = require('benchmark'); const assemblyScriptIsPrime = require('./index').isPrime; function isPrime(x) {    for (let i = 2; i < x; i++) {        if (x % i === 0) {            return false;        }    }    return true; } const suite = new Benchmark.Suite; const startNumber = 2; const stopNumber = 10000; suite.add('AssemblyScript isPrime', function () {    for (let i = startNumber; i < stopNumber; i++) {        assemblyScriptIsPrime(i);    } }).add('JavaScript isPrime', function () {    for (let i = startNumber; i < stopNumber; i++) {        isPrime(i);    } }).on('cycle', function (event) {    console.log(String(event.target)); }).on('complete', function () {    const fastest = this.filter('fastest');    const slowest = this.filter('slowest');    const difference = (fastest.map('hz') - slowest.map('hz')) / slowest.map('hz') * 100;    console.log(`${fastest.map('name')} is ~${difference.toFixed(1)}% faster.`); }).run(); 

إليك ما تمكنت من الحصول عليه بعد تشغيل الأمر node benchmark العقدي على جهاز الكمبيوتر الخاص بي:

 AssemblyScript isPrime x 74.00 ops/sec ±0.43% (76 runs sampled) JavaScript isPrime x 61.56 ops/sec ±0.30% (64 runs sampled) AssemblyScript isPrime is ~20.2% faster. 

كما ترون ، كان تطبيق AssemblyScript للخوارزمية أسرع بنسبة 20٪ من تطبيق JS. ومع ذلك ، لاحظ أن هذا الاختبار هو علامة تجارية صغيرة . لا تعتمد كثيرا على نتائجها.

من أجل العثور على نتائج أكثر موثوقية لبحث أداء مشاريع AssemblyScript - أوصي بإلقاء نظرة على هذه المعايير وهذه .

باستخدام وحدة Wasm على صفحة ويب


دعنا نستخدم وحدة Wasm الخاصة بنا على صفحة الويب. نبدأ بإنشاء ملف index.html بالمحتويات التالية:

 <!DOCTYPE html> <html>    <head>        <meta charset="utf-8" />        <title>AssemblyScript isPrime demo</title>    </head>    <body>        <form id="prime-checker">            <label for="number">Enter a number to check if it is prime:</label>            <input name="number" type="number" />            <button type="submit">Submit</button>        </form>        <p id="result"></p>        <script src="demo.js"></script>    </body> </html> 

الآن قم بإنشاء ملف demo.js الذي يظهر demo.js أدناه. هناك العديد من الطرق لتحميل وحدات WebAssembly. الأكثر كفاءة هو تجميع وتهيئة لهم في وضع التدفق باستخدام وظيفة WebAssembly.instantiateStreaming . يرجى ملاحظة أننا سنحتاج إلى إعادة تعريف وظيفة الإجهاض ، والتي تسمى إذا لم يتم تنفيذ عبارة ما .

 (async () => {    const importObject = {        env: {            abort(_msg, _file, line, column) {                console.error("abort called at index.ts:" + line + ":" + column);            }        }    };    const module = await WebAssembly.instantiateStreaming(        fetch("build/optimized.wasm"),        importObject    );    const isPrime = module.instance.exports.isPrime;    const result = document.querySelector("#result");    document.querySelector("#prime-checker").addEventListener("submit", event => {        event.preventDefault();        result.innerText = "";        const number = event.target.elements.number.value;        result.innerText = `${number} is ${isPrime(number) ? '' : 'not '}prime.`;    }); })(); 

بعد ذلك ، قم بتثبيت حزمة خادم ثابت . نحتاج إلى خادم نظرًا لأنه من أجل استخدام وظيفة WebAssembly.instantiateStreaming ، يجب صيانة الوحدة النمطية باستخدام نوع MIME application/wasm .

 npm install --save-dev static-server 

أضف البرنامج النصي المناسب إلى package.json :

 {  "scripts": {    "serve-demo": "static-server"  } } 

الآن npm run serve-demo الأمر npm run serve-demo وافتح عنوان URL الخاص بالمضيف المحلي في المتصفح. إذا قمت بإدخال رقم معين في النموذج ، يمكنك معرفة ما إذا كان بسيطًا أم لا. الآن ، في تطوير AssemblyScript ، قطعنا شوطًا طويلاً من كتابة التعليمات البرمجية إلى استخدامه في Node.js وعلى صفحة ويب.

النتائج


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

شيء مماثل يمكن أن يقال عن AssemblyScript. تعمل هذه التقنية على تسهيل الوصول إلى WebAssembly لعدد كبير من المطورين. يسمح ، عند إنشاء تعليمات برمجية بلغة قريبة من JavaScript ، باستخدام إمكانيات WebAssembly لحل المشكلات الحسابية المعقدة.

أعزائي القراء! كيف يمكنك تقييم احتمالات استخدام AssemblyScript في مشاريعك؟


Source: https://habr.com/ru/post/ar477272/


All Articles