টাইপস্ক্রিপ্টে ওয়েবঅ্যাসেম্বলি
এই আর্টিকেলে টাইপস্ক্রিপ্টে ওয়েবঅ্যাসেম্বলি ব্যাখ্যা করা হয়েছে।
আমরা টাইপস্ক্রিপ্ট ও ওয়েবঅ্যাসেম্বলি সংযুক্ত করার ব্যবহারিক ও সহজবোধ্য পদ্ধতি ব্যাখ্যা করব।
YouTube Video
টাইপস্ক্রিপ্টে ওয়েবঅ্যাসেম্বলি
ওয়েবঅ্যাসেম্বলি (Wasm) একটি বাইনারি ফরম্যাট রানটাইম, যা ব্রাউজারে প্রায়-নেটিভ গতিতে চলে। টাইপস্ক্রিপ্ট থেকে Wasm কল করে, আপনি দক্ষতার সঙ্গে কম্পিউট-ইনটেনসিভ প্রসেস ও C/C++ অথবা Rust-এ লেখা বিদ্যমান নেটিভ লাইব্রেরি ব্যবহার করতে পারবেন।
মৌলিক কার্যপ্রবাহ
এখানে, আমরা Wasm-এর মৌলিক কার্যপ্রবাহ ব্যাখ্যা করব। টাইপস্ক্রিপ্ট (অথবা ব্রাউজার) .wasm ফাইল আনে, ইনস্ট্যান্স তৈরি করে, এবং এক্সপোর্ট করা ফাংশনগুলো কল করে।
- ১. AssemblyScript, Rust, C++ দিয়ে .wasm বাইনারি তৈরি করুন, অথবা পূর্ব-বিদ্যমান একটি ব্যবহার করুন।
- ২. টাইপস্ক্রিপ্টে (অথবা ব্রাউজারে) .wasm ফাইল আনা এবং সেটি সিঙ্ক্রোনাস অথবা অ্যাসিঙ্ক্রোনাসভাবে ইনস্ট্যান্স করা।
- ৩. এক্সপোর্ট করা ফাংশনগুলো কল করুন এবং প্রয়োজনে
WebAssembly.Memoryব্যাবহার করে মেমোরি শেয়ার করুন।
WebAssembly.instantiateStreaming
পরবর্তীতে, আমরা একটি মৌলিক উদাহরণ দেখাব কীভাবে Wasm ফাইল লোড করা এবং এক্সপোর্ট করা ফাংশন কল করা যায়। ব্রাউজারের instantiateStreaming সমর্থন থাকা প্রয়োজন।
নিম্নলিখিত কোডটি সার্ভার থেকে simple.wasm এনে add ফাংশন কল করার একটি উদাহরণ।
1// TypeScript: load-and-call.ts
2// Fetch and instantiate a wasm module and call its exported `add` function.
3async function run() {
4 const response = await fetch('http://localhost:3000/simple.wasm');
5 // Use instantiateStreaming when available for efficiency.
6 const { instance } = await WebAssembly.instantiateStreaming(response, {});
7 // @ts-ignore
8 const result = instance.exports.add(2, 3);
9 console.log('2 + 3 =', result);
10}
11run().catch(console.error);- Wasm-এর মধ্যে থাকা ফাংশনগুলো
instance.exports-এ সংরক্ষিত থাকে। - টাইপস্ক্রিপ্ট টাইপ তথ্য পায় না বলে, আপনাকে
@ts-ignoreব্যবহার করতে হবে অথবা নিজের টাইপ ডেফিনিশন তৈরি করতে হবে।
AssemblyScript ব্যবহার করে ওয়ার্কফ্লো
AssemblyScript আপনাকে টাইপস্ক্রিপ্টের অনুরূপ সিনট্যাক্সে Wasm লেখার সুযোগ দেয়, ফলে এটি টাইপস্ক্রিপ্ট ডেভেলপারদের জন্য সহজ একটি অপশন। এখানে, আমরা একটি সহজ ফাংশন প্রস্তুত করি, সেটিকে .wasm এবং .d.ts এ কম্পাইল করি, এবং TypeScript থেকে কল করি।
1// assembly/index.ts (AssemblyScript)
2// npm install --save-dev assemblyscript
3
4// Export a simple function to add two integers.
5export function add(a: i32, b: i32): i32 {
6 return a + b;
7}asc(অর্থাৎAssemblyScriptকম্পাইলার) ব্যবহার করে আপনি একটি.wasmফাইল এবং ইচ্ছা করলে টাইপ সংজ্ঞা.d.tsফাইল তৈরি করতে পারেন। লোকালি চেষ্টা করতে,npmদিয়েassemblyscriptইন্সটল করুন এবং কম্পাইল করুন।
1# build commands
2# npm install --save-dev assemblyscript
3npx asc assembly/index.ts -o build/simple.wasm -t build/simple.wat --bindings esm --exportTable --sourceMap
4
5# optionally generate d.ts with --exportRuntime or use as-bind / loader toolsটাইপস্ক্রিপ্ট সাইড থেকে আনা ও কল করার একটি উদাহরণ নিচে দেখানো হলো।
1// ts client that loads AssemblyScript-generated wasm
2async function runAssemblyScript() {
3 const res = await fetch('http://localhost:3000/build/simple.wasm');
4 const { instance } = await WebAssembly.instantiateStreaming(res, {});
5 // @ts-ignore
6 console.log('AssemblyScript add:', instance.exports.add(10, 7));
7}
8runAssemblyScript().catch(console.error);- AssemblyScript-এ মেমোরি মডেল ও স্ট্রিংগুলোর বিশেষ যত্ন নিতে হয়, তবে সাধারণ সংখ্যাগত হিসাব-নিকাশের জন্য ব্যবহার করা সহজ।
Rust + wasm-bindgen (একটি শক্তিশালী ও বহুল ব্যবহৃত পদ্ধতি)
এই অংশে ব্যাখ্যা করা হয়েছে কীভাবে Rust-এ Wasm লিখে wasm-bindgen ব্যবহার করে JavaScript বা TypeScript-এর সঙ্গে সংযুক্ত করা যায়। এখানে, আমরা একটি সহজ Fibonacci ফাংশন উদাহরণ হিসেবে ব্যবহার করব, যাতে নিঃসৃত মডিউল ES মডিউল হিসেবে কিভাবে ইমপোর্ট করা হয় তা দেখানো যায়।
Rust সাইড থেকে wasm-bindgen ব্যবহার করে ফাংশন এক্সপোর্ট করুন।
1// src/lib.rs (Rust)
2// install wasm-pack from https://drager.github.io/wasm-pack/installer/
3use wasm_bindgen::prelude::*;
4
5// Export a function to JavaScript using wasm-bindgen.
6#[wasm_bindgen]
7pub fn fib(n: u32) -> u32 {
8 if n <= 1 { return n; }
9 let mut a = 0;
10 let mut b = 1;
11 for _ in 2..=n {
12 let tmp = a + b;
13 a = b;
14 b = tmp;
15 }
16 b
17}wasm-packঅথবাwasm-bindgenCLI দিয়ে বিল্ড করলে টাইপস্ক্রিপ্ট ও JS র্যাপারের জন্য টাইপ ডেফিনিশন স্বয়ংক্রিয়ভাবে তৈরি হয়, ফলে সরাসরি ESM হিসেবে ইমপোর্ট করা সম্ভব।
1# build with wasm-pack
2# install wasm-pack from https://drager.github.io/wasm-pack/installer/
3wasm-pack build --target nodejs --out-dir pkgটাইপস্ক্রিপ্ট সাইডে, pkg থেকে ES মডিউল ইমপোর্ট করুন এবং ব্যবহার করুন।
1// Node.js: import WASM module built with --target web
2// import init, { fib } from '../pkg/my_wasm_module.js';
3// Node.js: import WASM module built with --target nodejs
4import wasm from '../pkg/my_wasm_module.js';
5
6async function run() {
7 //await init(); // --target web
8 console.log('fib(10)=', wasm.fib(10));
9}
10
11run().catch(console.error);wasm-packজাভাস্ক্রিপ্ট র্যাপার ও.d.tsটাইপ ডেফিনিশন তৈরি করে, যা টাইপস্ক্রিপ্ট থেকে ব্যবহার সহজ করে তোলে। অনুগ্রহ করে মনে রাখবেন, যদি আপনিwasm-packকমান্ডের--targetঅপশনের জন্যwebনির্দিষ্ট করেন, তাহলে অ্যাসিনক্রোনাস ইনিশিয়ালাইজেশন প্রয়োজন।
বাস্তবিক মেমোরি শেয়ারিং উদাহরণ: অ্যারে পাঠানো ও প্রসেস করা (লো-লেভেল)
Wasm এর সঙ্গে বড় পরিমাণ ডেটা আদান-প্রদানে কার্যকর ডেটা এক্সচেঞ্জের জন্য ArrayBuffer শেয়ার করা গুরুত্বপূর্ণ। এখানে আমরা AssemblyScript দিয়ে উদাহরণ দেখাচ্ছি, তবে একই নিয়ম Rust এর wasm-bindgen-এর ক্ষেত্রেও প্রযোজ্য।
AssemblyScript সাইডে, মেমোরিতে লেখার জন্য একটি এক্সপোর্ট করা ফাংশন প্রস্তুত করুন। উদাহরণস্বরূপ, অ্যারের প্রতিটি উপাদানকে বর্গ করার জন্য ফাংশনটি এভাবে দেখতে হবে।
1// assembly/array_ops.ts (AssemblyScript)
2// Square values in place in the wasm linear memory starting at `ptr` for `len` elements.
3export function square_in_place(ptr: usize, len: i32): void {
4 // Treat memory as a pointer to 32-bit integers.
5 for (let i = 0; i < len; i++) {
6 let offset = ptr + (i << 2); // i * 4 bytes
7 let value = load<i32>(offset);
8 store<i32>(offset, value * value);
9 }
10}AssemblyScript দ্বারা ব্যবহৃত মেমরি সেটিংস নির্ধারণ করতে, নিম্নলিখিত asconfig.json প্রস্তুত করুন।
1{
2 "options": {
3 "memoryBase": 0,
4 "importMemory": false,
5 "initialMemory": 1,
6 "maximumMemory": 10
7 }
8}1 npx asc assembly/array_ops.ts -o build/array_ops.wasm -t build/array_ops.wat --bindings esm --exportTable --sourceMap- এই ফাংশন কল করতে, আপনাকে
ArrayBufferWasm মেমোরি স্পেসে কপি করতে হবে এবং পয়েন্টার পাঠাতে হবে।
নিচে টাইপস্ক্রিপ্টে WebAssembly.Memory ব্যবহার করে ডেটা কপি ও ফাংশন কল করার উদাহরণ দেয়া হলো।
1// TypeScript: use memory to pass array to wasm
2async function runArrayOps() {
3 const res = await fetch('http://localhost:3000/build/array_ops.wasm');
4 const { instance } = await WebAssembly.instantiateStreaming(res, {});
5 // @ts-ignore
6 const memory: WebAssembly.Memory = instance.exports.memory;
7 // Create a view into wasm memory.
8 const i32View = new Int32Array(memory.buffer);
9
10 // Example data
11 const input = new Int32Array([1, 2, 3, 4]);
12 // Choose an offset (in i32 elements) to copy data to (simple example: at index 0).
13 const offset = 0;
14 i32View.set(input, offset);
15
16 // Call wasm function: ptr in bytes, len in elements
17 // @ts-ignore
18 instance.exports.square_in_place(offset * 4, input.length);
19
20 // Read back result
21 const result = i32View.slice(offset, offset + input.length);
22 console.log('squared:', result);
23}
24runArrayOps().catch(console.error);memory.bufferহচ্ছে শেয়ারকৃত লিনিয়ার মেমোরি; যতটা সম্ভব কপি কমালে প্রসেসিং স্পিড বাড়ে। এছাড়াও মনে রাখুন, পয়েন্টারগুলি বাইট পজিশন নির্দেশ করে, অথচTypedArrayএলিমেন্ট গণনা অনুযায়ী ম্যানেজ হয়, তাই এই পার্থক্য না মেশানোর চেষ্টা করা উচিত।
টাইপ-নিরাপদ ব্যবস্থাপনা: টাইপস্ক্রিপ্ট টাইপ ডেফিনিশন প্রস্তুত করুন
Wasm এক্সপোর্ট হচ্ছে জাভাস্ক্রিপ্ট অবজেক্ট, তাই টাইপস্ক্রিপ্ট সাইডে টাইপ ডেফিনিশন দিলে ডেভেলপমেন্ট সহজ হয়। এখানে টাইপ ডেফিনিশন ফাইলের একটি সহজ উদাহরণ দেখানো হলো।
নিচে দেখানো হলো কিভাবে simple.d.ts নামে ম্যানুয়ালি একটি মৌলিক টাইপ ডেফিনিশন তৈরি করা যায়।
1// simple.d.ts
2export function add(a: number, b: number): number;
3export const memory: WebAssembly.Memory;- আপনার
tsconfig.json-এরtypeRoots-এ এটি রাখলে বাdeclare moduleব্যবহার করলে টাইপ চেকিং চালু হবে।wasm-packস্বয়ংক্রিয়ভাবে.d.tsফাইল তৈরি করে, তাই এসব ব্যবহার করা সুবিধাজনক।
রানটাইমে ইনিশিয়ালাইজেশনের ধরন: সিঙ্ক্রোনাস এবং অ্যাসিঙ্ক্রোনাস
Wasm মডিউলে I/O (ফেচিং) ও কম্পাইলেশন প্রয়োজন, তাই অ্যাসিঙ্ক্রোনাস ইনিশিয়ালাইজেশন সাধারণত বেশি ব্যবহৃত। তবে, এমন একটি প্যাটার্নও আছে যেখানে আপনি আগে WebAssembly.Module ক্যাশ করে পরে সিঙ্ক্রোনাসলি ইনস্ট্যান্স করতে পারেন।
নিচে অ্যাসিঙ্ক্রোনাসভাবে WebAssembly ইনিশিয়ালাইজ করার মৌলিক কোড স্ট্রাকচার দেয়া হলো। বাস্তব প্রজেক্টে, এই প্যাটার্নটি সুপারিশ করা হয়।
1// async init pattern
2async function initWasm(url: string) {
3 const res = await fetch(url);
4 const { instance, module } = await WebAssembly.instantiateStreaming(res, {});
5 return instance;
6}- অ্যাসিঙ্ক্রোনাস ইনিশিয়ালাইজেশন সহজভাবে ফ্লেক্সিবল এরর হ্যান্ডলিং ও লেইজি লোডিং অন্তর্ভুক্ত করতে দেয়, যা বাস্তব ডেভেলপমেন্টে সবচেয়ে সুবিধাজনক। এছাড়াও,
wasm-packদিয়ে তৈরি কোডে ইনিশিয়ালাইজেশনের জন্য একটিinit()API থাকে, তাই এই ফ্লোয়ে অভ্যস্ত হলে কাজ আরও সহজ হবে।
বাস্তবিক পারফরম্যান্স বিবেচনা
গুরুত্বপূর্ণ পারফরম্যান্স উন্নতির জন্য কিছু বিষয় এখানে উল্লেখ করা হলো। টাইপস্ক্রিপ্ট ও ওয়েবঅ্যাসেম্বলি একসাথে ব্যবহার করার সময় এই অপ্টিমাইজেশন টিপস অনুসরণ করুন।
- ফাংশন কল খুব ঘনঘন হলে, জাভাস্ক্রিপ্ট ও Wasm-এর মধ্যে কল করার ওভারহেড বেশ সমস্যার কারণ হতে পারে। আমরা পরামর্শ দিচ্ছি, যতটা সম্ভব ডেটা ব্যাচ করে একবারে প্রসেস করুন।
- মেমোরি বরাদ্দ ও কপি করলে প্রসেসিং লোড বাড়ে। এসব অপারেশন কমাতে শেয়ারকৃত বাফার ও পয়েন্টার ব্যবহার করুন।
- ফ্লোটিং পয়েন্ট নাম্বার ব্যবহারে সতর্ক থাকুন। টাইপস্ক্রিপ্ট-এ, এগুলো
numberটাইপে রূপান্তরিত হয়, কিন্তু Wasm সাইডের টাইপ ঠিক রেখে সঠিকভাবে হ্যান্ডল করতে পারবেন।
সারসংক্ষেপ
টাইপস্ক্রিপ্ট ও ওয়েবঅ্যাসেম্বলি একত্রে ব্যবহার করে, আপনি ব্রাউজারে প্রায়-নেটিভ পারফরম্যান্স পেতে পারেন। এটি বিশেষভাবে গণনাবহুল টাস্কের জন্য অথবা বিদ্যমান নেটিভ অ্যাসেট ব্যবহার করার ক্ষেত্রে কার্যকর। আপনার ওয়েব অ্যাপ্লিকেশনের পারফরম্যান্স বাড়াতে হলে এই সংমিশ্রণটি একটি অত্যন্ত শক্তিশালী অপশন।
আপনি আমাদের ইউটিউব চ্যানেলে ভিজ্যুয়াল স্টুডিও কোড ব্যবহার করে উপরের নিবন্ধটি অনুসরণ করতে পারেন। দয়া করে ইউটিউব চ্যানেলটিও দেখুন।