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

(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 का इस्तेमाल करें!