WebAssembly মডিউল দক্ষতার সাথে লোড হচ্ছে

WebAssembly এর সাথে কাজ করার সময়, আপনি প্রায়শই একটি মডিউল ডাউনলোড করতে চান, এটি কম্পাইল করতে চান, এটিকে ইনস্ট্যান্টিয়েট করতে চান এবং তারপরে এটি জাভাস্ক্রিপ্টে যা রপ্তানি করে তা ব্যবহার করতে চান। এই পোস্টটি সর্বোত্তম দক্ষতার জন্য আমাদের প্রস্তাবিত পদ্ধতির ব্যাখ্যা করে।

WebAssembly এর সাথে কাজ করার সময়, আপনি প্রায়শই একটি মডিউল ডাউনলোড করতে চান, এটি কম্পাইল করতে চান, এটিকে ইনস্ট্যান্টিয়েট করতে চান এবং তারপরে এটি জাভাস্ক্রিপ্টে যা রপ্তানি করে তা ব্যবহার করতে চান। এই পোস্টটি একটি সাধারণ কিন্তু সাবঅপ্টিমাল কোড স্নিপেট দিয়ে শুরু হয় যা ঠিক করে, বিভিন্ন সম্ভাব্য অপ্টিমাইজেশান নিয়ে আলোচনা করে এবং অবশেষে জাভাস্ক্রিপ্ট থেকে WebAssembly চালানোর সবচেয়ে সহজ, সবচেয়ে কার্যকর উপায় দেখায়।

এই কোড স্নিপেটটি সম্পূর্ণ ডাউনলোড-কম্পাইল-ইনস্ট্যান্টিয়েট নাচ করে, যদিও একটি সাবঅপ্টিমাল উপায়ে:

এই ব্যবহার করবেন না!

(async () => {
  const response = await fetch('fibonacci.wasm');
  const buffer = await response.arrayBuffer();
  const module = new WebAssembly.Module(buffer);
  const instance = new WebAssembly.Instance(module);
  const result = instance.exports.fibonacci(42);
  console.log(result);
})();

একটি প্রতিক্রিয়া বাফারকে মডিউলে পরিণত করতে আমরা কীভাবে new WebAssembly.Module(buffer) ব্যবহার করি তা নোট করুন। এটি একটি সিঙ্ক্রোনাস API, যার অর্থ এটি সম্পূর্ণ না হওয়া পর্যন্ত এটি মূল থ্রেডটিকে ব্লক করে। এর ব্যবহারকে নিরুৎসাহিত করতে, ক্রোম 4 KB-এর থেকে বড় বাফারগুলির জন্য WebAssembly.Module অক্ষম করে৷ আকারের সীমার কাছাকাছি কাজ করতে, আমরা পরিবর্তে await WebAssembly.compile(buffer) ব্যবহার করতে পারি:

(async () => {
  const response = await fetch('fibonacci.wasm');
  const buffer = await response.arrayBuffer();
  const module = await WebAssembly.compile(buffer);
  const instance = new WebAssembly.Instance(module);
  const result = instance.exports.fibonacci(42);
  console.log(result);
})();

await WebAssembly.compile(buffer) এখনও সর্বোত্তম পন্থা নয়, তবে আমরা এক সেকেন্ডের মধ্যে এটি পেতে পারব।

পরিবর্তিত স্নিপেটের প্রায় প্রতিটি অপারেশনই এখন অ্যাসিঙ্ক্রোনাস, কারণ await ব্যবহার স্পষ্ট করে। একমাত্র ব্যতিক্রম হল new WebAssembly.Instance(module) , যার ক্রোমে একই 4 KB বাফার আকারের সীমাবদ্ধতা রয়েছে৷ সামঞ্জস্যের জন্য এবং মূল থ্রেডটিকে মুক্ত রাখার স্বার্থে, আমরা অ্যাসিঙ্ক্রোনাস WebAssembly.instantiate(module) ব্যবহার করতে পারি।

(async () => {
  const response = await fetch('fibonacci.wasm');
  const buffer = await response.arrayBuffer();
  const module = await WebAssembly.compile(buffer);
  const instance = await WebAssembly.instantiate(module);
  const result = instance.exports.fibonacci(42);
  console.log(result);
})();

আসুন compile অপ্টিমাইজেশানে ফিরে যাই যা আমি আগে ইঙ্গিত দিয়েছিলাম। স্ট্রিমিং কম্পাইলেশনের সাথে, ব্রাউজার ইতিমধ্যেই WebAssembly মডিউল কম্পাইল করা শুরু করতে পারে যখন মডিউল বাইটগুলি এখনও ডাউনলোড হচ্ছে। যেহেতু ডাউনলোড এবং সংকলন সমান্তরালভাবে ঘটে, তাই এটি দ্রুততর — বিশেষ করে বড় পেলোডের জন্য।

যখন ডাউনলোডের সময় WebAssembly মডিউলের সংকলন সময়ের চেয়ে বেশি হয়, তখন WebAssembly.compileStreaming() শেষ বাইট ডাউনলোড হওয়ার পরপরই সংকলন শেষ করে।

এই অপ্টিমাইজেশন সক্ষম করতে, WebAssembly.compile এর পরিবর্তে WebAssembly.compileStreaming ব্যবহার করুন। এই পরিবর্তনটি আমাদের মধ্যবর্তী অ্যারে বাফার থেকে পরিত্রাণ পেতে দেয়, যেহেতু আমরা এখন সরাসরি await fetch(url) দ্বারা প্রত্যাবর্তিত Response উদাহরণটি পাস করতে পারি।

(async () => {
  const response = await fetch('fibonacci.wasm');
  const module = await WebAssembly.compileStreaming(response);
  const instance = await WebAssembly.instantiate(module);
  const result = instance.exports.fibonacci(42);
  console.log(result);
})();

WebAssembly.compileStreaming API একটি প্রতিশ্রুতি গ্রহণ করে যা একটি Response উদাহরণের সমাধান করে। যদি আপনার কোডের অন্য কোথাও response প্রয়োজন না থাকে, তাহলে আপনি ফলাফলের জন্য স্পষ্টভাবে await না করে সরাসরি ফিরিয়ে fetch প্রতিশ্রুতি পাস করতে পারেন:

(async () => {
  const fetchPromise = fetch('fibonacci.wasm');
  const module = await WebAssembly.compileStreaming(fetchPromise);
  const instance = await WebAssembly.instantiate(module);
  const result = instance.exports.fibonacci(42);
  console.log(result);
})();

আপনার যদি অন্য কোথাও ফলাফল fetch প্রয়োজন না হয়, আপনি এমনকি এটি সরাসরি পাস করতে পারেন:

(async () => {
  const module = await WebAssembly.compileStreaming(
    fetch('fibonacci.wasm'));
  const instance = await WebAssembly.instantiate(module);
  const result = instance.exports.fibonacci(42);
  console.log(result);
})();

যদিও আমি ব্যক্তিগতভাবে এটি একটি পৃথক লাইনে রাখা আরও পাঠযোগ্য বলে মনে করি।

দেখুন কিভাবে আমরা একটি মডিউলে প্রতিক্রিয়া কম্পাইল করি, এবং তারপর তা অবিলম্বে ইনস্ট্যান্টিয়েট করি? দেখা যাচ্ছে, WebAssembly.instantiate এক সাথে কম্পাইল এবং ইনস্ট্যান্টিয়েট করতে পারে। WebAssembly.instantiateStreaming API এটি একটি স্ট্রিমিং পদ্ধতিতে করে:

(async () => {
  const fetchPromise = fetch('fibonacci.wasm');
  const { module, instance } = await WebAssembly.instantiateStreaming(fetchPromise);
  // To create a new instance later:
  const otherInstance = await WebAssembly.instantiate(module);
  const result = instance.exports.fibonacci(42);
  console.log(result);
})();

আপনার যদি শুধুমাত্র একটি একক উদাহরণের প্রয়োজন হয়, তাহলে module অবজেক্টটিকে চারপাশে রাখার, কোডটিকে আরও সরলীকরণ করার কোন মানে নেই:

// This is our recommended way of loading WebAssembly.
(async () => {
  const fetchPromise = fetch('fibonacci.wasm');
  const { instance } = await WebAssembly.instantiateStreaming(fetchPromise);
  const result = instance.exports.fibonacci(42);
  console.log(result);
})();

আমরা যে অপ্টিমাইজেশানগুলি প্রয়োগ করেছি তা নিম্নরূপ সংক্ষিপ্ত করা যেতে পারে:

  • প্রধান থ্রেড ব্লক করা এড়াতে অ্যাসিঙ্ক্রোনাস API ব্যবহার করুন
  • WebAssembly মডিউলগুলিকে আরও দ্রুত কম্পাইল এবং ইনস্ট্যান্ট করতে স্ট্রিমিং API ব্যবহার করুন
  • আপনার প্রয়োজন নেই এমন কোড লিখবেন না

WebAssembly সঙ্গে মজা আছে!