العمل مع WebAssembly وJavaScript في Node.js: تقنيات متقدمة

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة في العمل المتقدم مع WebAssembly وJavaScript في Node.js

\n\n

لم يعد WebAssembly (Wasm) تقنية غريبة للمتصفحات. في نظام Node.js، يفتح إمكانيات فريدة لتنفيذ العمليات الحسابية عالية الأداء المكتوبة بلغة C/C++ أو Rust أو Go، بأقل تكاليف إضافية. على المستوى المتقدم، يتطلب العمل مع Wasm في Node.js فهماً عميقاً لإدارة الذاكرة، والاستدعاءات غير المتزامنة، وتحسين نقل البيانات بين بيئات التشغيل. في هذه المقالة، سنستعرض أمثلة عملية لدمج WebAssembly مع JavaScript باستخدام Node.js.

\n\n

1. تجميع وتحميل وحدات Wasm في Node.js

\n\n

تحضير ملف Wasm

\n

للبدء، نحتاج إلى وحدة Wasm مُجمَّعة. لنفترض أن لدينا دالة بسيطة بلغة C تجمع رقمين:

\n\n
// sum.c\nint add(int a, int b) {\n    return a + b;\n}
\n\n

نقوم بالتجميع باستخدام Emscripten أو wasm-pack:

\n
emcc sum.c -O3 -o sum.wasm --no-entry -s EXPORTED_FUNCTIONS="['_add']"
\n\n

تحميل الوحدة في Node.js

\n

في Node.js، يمكننا تحميل وحدة Wasm بطريقتين: عبر readFileSync المتزامن أو WebAssembly.instantiate غير المتزامن. لننظر إلى الخيار المتقدم غير المتزامن مع الاستيراد:

\n\n
const fs = require('fs');\nconst path = require('path');\n\nasync function loadWasmModule(filePath, imports = {}) {\n    const wasmBuffer = fs.readFileSync(path.resolve(filePath));\n    const result = await WebAssembly.instantiate(wasmBuffer, imports);\n    return result.instance;\n}\n\n(async () => {\n    const instance = await loadWasmModule('./sum.wasm');\n    const { add } = instance.exports;\n    console.log('النتيجة:', add(5, 7)); // 12\n})();
\n\n

هنا imports هو كائن يمكن أن يحتوي على دوال JavaScript يتم استدعاؤها من Wasm (مثل التسجيل أو العمل مع الذاكرة).

\n\n

2. إدارة الذاكرة: العمل مع الذاكرة الخطية لـ Wasm

\n\n

أحد التحديات الرئيسية في العمل المتقدم مع Wasm هو إدارة الذاكرة الخطية. وحدات Wasm لها ذاكرة معزولة خاصة بها (ArrayBuffer) يمكن لـ JavaScript الوصول إليها مباشرة عبر instance.exports.memory. لننظر إلى مثال حيث نمرر مصفوفة أرقام من JS إلى Wasm، ونعالجها ونعيد النتيجة.

\n\n

مثال: جمع مصفوفة بلغة C

\n
// sum_array.c\nint sum_array(int* arr, int len) {\n    int sum = 0;\n    for(int i = 0; i < len; i++) {\n        sum += arr[i];\n    }\n    return sum;\n}
\n\n

نقوم بالتجميع مع تصدير الذاكرة:

\n
emcc sum_array.c -O3 -o sum_array.wasm --no-entry \\\n  -s EXPORTED_FUNCTIONS="['_sum_array']" \\\n  -s EXPORTED_RUNTIME_METHODS="['cwrap', 'getValue']"
\n\n

كود JavaScript لنقل البيانات

\n
async function sumArrayWithWasm(numbers) {\n    const instance = await loadWasmModule('./sum_array.wasm');\n    const memory = instance.exports.memory;\n    const sumArray = instance.exports.sum_array;\n\n    // تخصيص ذاكرة للمصفوفة (4 بايت لكل int)\n    const bytesPerElement = 4;\n    const offset = instance.exports.malloc ? \n                   instance.exports.malloc(numbers.length * bytesPerElement) : \n                   0; // إذا لم يكن هناك malloc، استخدم عنواناً ثابتاً\n\n    // إنشاء Uint32Array لكتابة البيانات\n    const buffer = new Uint32Array(memory.buffer, offset, numbers.length);\n    buffer.set(numbers);\n\n    // استدعاء دالة Wasm\n    const result = sumArray(offset, numbers.length);\n\n    // تحرير الذاكرة (إذا كان هناك free)\n    if (instance.exports.free) {\n        instance.exports.free(offset);\n    }\n\n    return result;\n}\n\n(async () => {\n    const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\n    const sum = await sumArrayWithWasm(arr);\n    console.log('مجموع المصفوفة:', sum); // 55\                

المدونات

توصيات الكتب