WebAssembly मॉड्यूल बेहतर तरीके से लोड किए जा रहे हैं

WebAssembly के साथ काम करते समय, आप अक्सर एक मॉड्यूल डाउनलोड करना चाहते हैं, उसे कंपाइल करना चाहते हैं, उसे इंस्टैंशिएट करना चाहते हैं, और उसके बाद JavaScript में एक्सपोर्ट होने वाली किसी भी चीज़ का इस्तेमाल करना चाहते हैं. इस पोस्ट में, बेहतर तरीके से काम करने के लिए हमारे सुझाए गए तरीकों के बारे में बताया गया है.

माथियास बायनस
माथियास बायनस

WebAssembly के साथ काम करते समय, आप अक्सर एक मॉड्यूल डाउनलोड करना चाहते हैं, उसे कंपाइल करना चाहते हैं, उसे इंस्टैंशिएट करना चाहते हैं, और उसके बाद JavaScript में एक्सपोर्ट होने वाली सभी चीज़ों का इस्तेमाल करना चाहते हैं. इस पोस्ट की शुरुआत एक सामान्य, लेकिन सबसे बेहतर कोड स्निपेट से होती है जो ठीक उसी तरह काम करता है, जैसे कई संभावित ऑप्टिमाइज़ेशन के बारे में बताता है और आखिर में JavaScript से 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) का इस्तेमाल कैसे करते हैं. यह एक सिंक्रोनस एपीआई है. इसका मतलब है कि यह मुख्य थ्रेड को तब तक ब्लॉक करता है, जब तक वह पूरा नहीं हो जाता. इसके इस्तेमाल को रोकने के लिए, Chrome 4 केबी से बड़े बफ़र के लिए 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) को अपवाद माना जा सकता है. हालांकि, Chrome में इसका बफ़र साइज़ 4 केबी जैसा ही है. एक जैसा अनुभव देने और मुख्य थ्रेड को मुफ़्त में रखने के लिए, हम एसिंक्रोनस 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 की ज़रूरत नहीं है, तो fetch के ज़रिए दिखाया गया प्रॉमिस सीधे तौर पर awaitनतीजे के बिना पास किया जा सकता है:

(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 एपीआई यह काम स्ट्रीमिंग के तरीके से करता है:

(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);
})();

लागू किए गए ऑप्टिमाइज़ेशन की खास जानकारी इस तरह देखी जा सकती है:

  • मुख्य थ्रेड को ब्लॉक करने से रोकने के लिए, एसिंक्रोनस एपीआई का इस्तेमाल करें
  • WebAssembly मॉड्यूल को ज़्यादा तेज़ी से कंपाइल और इंस्टैंशिएट करने के लिए, स्ट्रीमिंग एपीआई का इस्तेमाल करें
  • ऐसा कोड न लिखें जिसकी आपको ज़रूरत नहीं है

WebAssembly के साथ आनंद लें!