شروع کار با WebAssembly

ترجمه و تالیف : ارسطو عباسی
تاریخ انتشار : 13 خرداد 98
خواندن در 3 دقیقه
دسته بندی ها : آموزشی

WebAssembly یک تکنولوژی جدید هست که باعث میشه کدهایی با عملکرد بالا و سطح پایین رو در مرورگرها اجرا کنید. این میتونه برای اجرای اپ های بزرگ برپایه C و ++C مثل بازی ها, موتورهای فیزیکی و حتی اپلیکیشن های دسکتاپ روی پلتفرم وب استفاده بشه.

درحال حاضر, WebAssembly میتونه روی Chrome و Firefox اجرا بشه, درمورد Edge و Safari هم تقریبا کامل پشتیبانی میشه. این یعنی به زودی شما میتونید wasm رو روی هر مرورگر محبوبی اجرا کنید.

در این آموزش ما می خواهیم نشان بدیم که چطور یک فایل ساده C رو در wasm کامپایل کنیم و اون رو در یک صفحه وب شامل کنیم. 

WebAssembly چطور کار می کند

اول از همه یک نگاه کلی بر تکنولوژی WebAssembly خواهیم داشت. سعی می کنیم در این بخش خیلی وارد مسائل فنی نشیم.

در مرورگرهای امروزی, جاوا اسکریپت در یک ماشین مجازی (VM) اجرا میشه که کد شما رو بهینه سازی میکنه و کارایی رو تحت تاثیر قرار میده. همینطور که میدونید جاوا اسکریپت یکی از سریع ترین زبان های پویای موجود هست. با وجود این سرعت بالا هم هنوز نمیتونه با کد خام C و ++C رقابت کنه. اینجاست که WebAssembly جلوه پیدا می کنه.

Wasm در همان ماشین مجازی جاوا اسکریپت اجرا میشه اما کارایی خیلی بهتری داره. هر دو میتوانند آزادانه ارتباط برقررا کنند و ناسازگاری ندارند. از طریق این روش شما میتونید بهترین های هردو جهان رو داشته باشید, اکوسیستم بزرگ و syntax آشنای جاوا اسکریپت که با کارایی فوق العاده ی WebAssembly ترکیب شده.

اکثر افراد ماژول های WebAssembly رو در C می نویسند و اونها رو به فایل های wasm. کامپایل می کنند. این فایل های wasm بصورت مستقیم توسط مرورگرها شناسایی نمی شوند, پس اونها به چیزی به نام JavaScript glue code احتیاج دارند تا بارگذاری بشه.

در آینده این فرآیند توسط فریمورک های WebAssembly و پشتیبانی ماژول بومی wasm کوتاه تر خواهد شد.

شروع کار با WebAssembly

آنچه شما برای این درس نیاز دارید

برای نوشتن کدهای WebAssembly نیاز به تعدادی ابزار دارید. به هرحال اکثر ابزارهای زیر معمول هستند و احتمالا اونها رو از قبل دارید.

  • یک مرورگر که از WebAssembly پشتیبانی میکنه, نسخه ی آپدیت شده ی Chrome یا Firefox مناسب هست.
  • کامپایلر C to WebAssembly, که ما از Emscripten portable استفاده می کنیم.
  • یک کامپایلر برای C (برای لینوکس GCC و برای ویندوز هم Visual Studio).
  • یک سرور وب محلی برای اجرای مثال (مثلا python -m SimpleHTTPServer 9000 یا هرچیز دیگری که استفاده می کنید).

نصب Emscripten کمی طول میکشه و قطعا جالب نیست, اما این بهترین کامپایلری هست که درحال حاضر میتونیم پیشنهاد کنیم. همچنین مطمئن بشید حافظه کافی برای این کار دارید, حدود 1 گیگابایت کافی هست.

قدم اول: نوشتن کد C

ما برای نمونه یک کد بسیار ساده C می نویسیم که یک عدد تصادفی بین 1 و 6 انتخاب می کنه. در مسیر کاری خودتون یک فایل dice-roll.c بسازید.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <emscripten/emscripten.h>

int main(int argc, char ** argv) {
    printf("WebAssembly module loaded\n");
}

int EMSCRIPTEN_KEEPALIVE roll_dice() {
    srand ( time(NULL) );
    return rand() % 6 + 1;
}

وقتی ما به wasm کامپایل می کنیم و کد رو در مرورگر بارگذاری می کنیم, main بصورت خودکار اجرا میشه. printf داخل به console.log تبدیل میشه و صدا زده خواهد شد.

ما می خواهیم هروقت لازم بشه تابع roll_dice در جاوا اسکریپت موجود باشه, پس برای این کار باید EMSCRIPTEN_KEEPALIVE رو اضافه کنیم.

قدم دوم: کامپایل C به WebAssembly

حالا که برنامه ساده C رو داریم, باید اون رو به wasm کامپایل کنیم. همچنین باید کد JavaScript glue رو تولید کنیم تا برای اجرایش کمکمون کنه.

اینجا ما باید از کامپایلر Emscripten استفاده کنیم. تعداد زیادی آپشن CLI وجود داره و رویکردهای مختلفی دارند. ما ترکیب زیر رو کاربرپسندتر دیدیم :

emcc dice-roll.c -s WASM=1 -O3 -o index.js

توضیحات دستور :

  • emcc : کامپایلر Emscripten
  • dice-roll.c : فایل ورودی شامل کد C ما
  • s WASM=1- : مشخص میکنه که ما با WebAssembly کار می کنیم.
  • -03 : سطح بهینه سازی کدی که می خواهیم, 3 بسیار بالاست.
  • o index.js- : به emcc میگه که یک فایل js تولید کنه با تمام کد glue که برای ماژول wasm نیاز هست.

بعد از اجرای دستور, دو فایل در مسیر کاری مون اضافه میشه : index.wasm و index.js. اینها به ترتیب ماژول WebAssembly و glue code اون رو نگهداری می کنند.

نکته : گرچه این آپشن های emcc برای مثال ما به خوبی کار می کنند, برای شرایط پیچیده تر, یک رویکرد دیگر میتونه بهتر باشه. مطالعه بیشتر.

قدم سوم: بارگذاری کد WebAssembly در مرورگر

اکنون که ما با مفاهیم کلی وب آشنا هستیم. یک فایل پایه index.html می سازیم که شامل کد glue جاوا اسکریپت در یک تگ اسکریپت هست.

<!DOCTYPE html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>WebAssembly Example</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>
  <body>

    <!-- Include the JavaScript glue code. -->
    <!-- This will load the WebAssembly module and run its main. --> 
    <script src="index.js"></script>

  </body>
</html> 

برای اجرای پروژه نیاز به یک سرور وب محلی داریم. در سیستم لینوکس, شما میتونید از این دستور در مسیر پروژه استفاده کنید :

python -m SimpleHTTPServer 9000

حال در مرورگر به localhost:9000 مراجعه کنید تا اپ قابل مشاهده بشه. اگر وارد کنسول مرورگر بشید پیامی که ما در کد اصلی در printf نوشته بودیم قابل مشاهدست.

شروع کار با WebAssembly

قدم چهارم: فراخوانی توابع WebAssembly

آخرین قدم اینه که جاوا اسکریپت و WebAssembly رو بهم متصل کنیم. یک API قدرتمند برای کار کردن با WebAssembly در مرورگر وجود داره. ما وارد جزئیاتش نمیشیم و در محدوده ی این آموزش صحبت می کنیم. بخش هایی که ما نیاز داریم یکی Module interface و یکی متد ccall هست. این متد به ما اجازه میده یک تابع رو با استفاده از نام در کد C صدا بزنیم و ازش به عنوان تابع جاوا اسکریپت استفاده کنیم.

var result = Module.ccall(
    'funcName',     // name of C function 
    'number',       // return type
    ['number'],     // argument types
    [42]);          // arguments

بعد از ccall, در result ما هرچیزی رو که تابع متناظر C اون برگشته باشه رو داریم. تمام arg ها به جز اولی اختیاری هستند.

نسخه کوتاه شده :

// Call a C function by adding an underscore in front of its name:
var result = _funcName();

تابع roll_dice ما در زبان C هیچ پارامتری نیاز نداره. صدا زدنش در کد جاوا اسکریپت سادست :

// When a HTML dice is clicked, it's value will change.
var dice = document.querySelector('.dice');

dice.addEventListener('click', function(){

    // Make a call to the roll_dice function from the C code.
    var result = _roll_dice();
    dice.className = "dice dice-" + result;                   

});

منبع

برای مطالعه بیشتر به منابع زیر مراجعه بفرمایید :

گردآوری و تالیف ارسطو عباسی

من ارسطو‌ام :) کافی نیست؟! :)