बाइनरी के साथ Wasm को कंपाइल और ऑप्टिमाइज़ करना

Binaryen, C++ में लिखी गई WebAssembly के लिए एक कंपाइलर और टूलचैन इंफ़्रास्ट्रक्चर लाइब्रेरी है. इसका मकसद, WebAssembly में कोड को आसानी से, तेज़ी से, और असरदार तरीके से कंपाइल करना है. इस पोस्ट में, ExampleScript नाम की सिंथेटिक टॉय लैंग्वेज के उदाहरण का इस्तेमाल करके, Binaryen.js API का इस्तेमाल करके JavaScript में WebAssembly मॉड्यूल लिखने का तरीका जानें. आपको मॉड्यूल बनाने, मॉड्यूल के साथ जोड़ने, और मॉड्यूल से एक्सपोर्ट करने वाले फ़ंक्शन की बुनियादी बातों के बारे में बताना होगा. इससे आपको असली प्रोग्रामिंग लैंग्वेज को WebAssembly में कंपाइल करने के पूरे तरीके के बारे में जानकारी मिलेगी. इसके अलावा, आपको Binaryen.js और wasm-opt की मदद से, कमांड लाइन पर Wasm मॉड्यूल को ऑप्टिमाइज़ करने का तरीका भी पता चलेगा.

Binaryen के बारे में जानकारी

Binaryen में एक ही हेडर में, आसानी से इस्तेमाल किया जा सकने वाला C API होता है. साथ ही, इसका इस्तेमाल JavaScript से भी किया जा सकता है. यह WebAssembly फ़ॉर्म में इनपुट स्वीकार करता है. हालांकि, यह ऐसे कंपाइलर के लिए एक सामान्य कंट्रोल फ़्लो ग्राफ़ भी स्वीकार करता है जो इसे पसंद करते हैं.

इंटरमीडिएट रिप्रज़ेंटेशन (आईआर), डेटा स्ट्रक्चर या कोड होता है. इसका इस्तेमाल, सोर्स कोड को दिखाने के लिए, कंपाइलर या वर्चुअल मशीन के अंदर किया जाता है. बाइनरी का इंटरनल आईआर, कॉम्पैक्ट डेटा स्ट्रक्चर का इस्तेमाल करता है. इसे सभी उपलब्ध सीपीयू कोर का इस्तेमाल करके, पूरी तरह से पैरलल कोड जनरेशन और ऑप्टिमाइज़ेशन के लिए डिज़ाइन किया गया है. WebAssembly का एक सबसेट होने की वजह से, बाइनरी का IR WebAssembly तक इकट्ठा होता है.

बाइनरीयन के ऑप्टिमाइज़र में ऐसे कई पास हैं जो कोड का साइज़ और स्पीड बढ़ा सकते हैं. इन ऑप्टिमाइज़ेशन का मकसद, Binaryen को इतना बेहतर बनाना है कि इसे अपने-आप कंपाइलर बैकएंड के तौर पर इस्तेमाल किया जा सके. इसमें WebAssembly के हिसाब से ऑप्टिमाइज़ेशन शामिल होते हैं. ये ऐसे ऑप्टिमाइज़ेशन होते हैं जिन्हें सामान्य मकसद के लिए बने कंपाइलर शायद न कर पाएं. इन्हें Wasm को छोटा करने के तौर पर देखा जा सकता है.

Binaryen के उदाहरण के तौर पर AssemblyScript

Binaryen का इस्तेमाल कई प्रोजेक्ट करते हैं. उदाहरण के लिए, AssemblyScript, जो TypeScript जैसी भाषा से सीधे WebAssembly में कंपाइल करने के लिए, Binaryen का इस्तेमाल करता है. AssemblyScript के प्लेग्राउंड में, उदाहरण आज़माएं.

AssemblyScript इनपुट:

export function add(a: i32, b: i32): i32 {
  return a + b;
}

Binaryen से जनरेट किया गया, टेक्स्ट फ़ॉर्मैट में मौजूद मिलता-जुलता WebAssembly कोड:

(module
 (type $0 (func (param i32 i32) (result i32)))
 (memory $0 0)
 (export "add" (func $module/add))
 (export "memory" (memory $0))
 (func $module/add (param $0 i32) (param $1 i32) (result i32)
  local.get $0
  local.get $1
  i32.add
 )
)

AssemblyScript का प्लेग्राउंड, जिसमें पिछले उदाहरण के आधार पर जनरेट किया गया WebAssembly कोड दिखाया गया है.

Binaryen टूलचेन

Binaryen टूलचेन, JavaScript डेवलपर और कमांड लाइन के उपयोगकर्ताओं, दोनों के लिए कई काम के टूल उपलब्ध कराता है. इन टूल का एक सबसेट नीचे दिया गया है; प्रोजेक्ट की README फ़ाइल पर शामिल टूल की पूरी सूची उपलब्ध है.

  • binaryen.js: यह एक स्टैंडअलोन JavaScript लाइब्रेरी है, जो Wasm मॉड्यूल बनाने और उन्हें ऑप्टिमाइज़ करने के लिए, Binaryen के तरीकों को दिखाती है. बिल्ड के लिए, npm पर binaryen.js देखें (या इसे सीधे GitHub या unpkg से डाउनलोड करें).
  • wasm-opt: यह कमांड लाइन टूल, WebAssembly को लोड करता है और इस पर बाइनरी आईआर पास चलाता है.
  • wasm-as और wasm-dis: ये कमांड-लाइन टूल, WebAssembly को असेंबल और डिसअसेम्बल करते हैं.
  • wasm-ctor-eval: कमांड-लाइन टूल, जो कंपाइल करने के समय फ़ंक्शन (या फ़ंक्शन के कुछ हिस्सों) को चला सकता है.
  • wasm-metadce: कमांड-लाइन टूल, जिसका इस्तेमाल करके Wasm फ़ाइलों के कुछ हिस्सों को आसानी से हटाया जा सकता है. यह इस बात पर निर्भर करता है कि मॉड्यूल का इस्तेमाल कैसे किया जा रहा है.
  • wasm-merge: कमांड-लाइन टूल, जो कई Wasm फ़ाइलों को एक फ़ाइल में मर्ज करता है. साथ ही, ऐसा करते समय, उनसे जुड़े इंपोर्ट को एक्सपोर्ट से जोड़ता है. यह JavaScript के लिए बंडलर की तरह ही काम करता है, लेकिन Wasm के लिए.

WebAssembly में कंपाइल करना

एक भाषा को दूसरी भाषा से कंपाइल करने में आम तौर पर कई चरण शामिल होते हैं. इनमें से सबसे ज़्यादा ज़रूरी निर्देश इस सूची में दिए गए हैं:

  • लेक्सिकल विश्लेषण: सोर्स कोड को टोकन में बांटना.
  • सिंटैक्स का विश्लेषण: एब्स्ट्रैक्ट सिंटैक्स ट्री बनाएं.
  • सेमांटिक विश्लेषण: गड़बड़ियों की जांच करना और भाषा के नियमों को लागू करना.
  • इंटरमीडिएट कोड जनरेशन: ज़्यादा बेहतर तरीके से दिखाएं.
  • कोड जनरेट करना: टारगेट की भाषा में अनुवाद करें.
  • टारगेट के हिसाब से कोड ऑप्टिमाइज़ेशन: टारगेट के लिए ऑप्टिमाइज़ करें.

यूनिक्स वर्ल्ड में, अक्सर कंपाइल करने के लिए इस्तेमाल किए जाने वाले टूल ये हैं: lex और yacc:

  • lex (लेक्सिकल ऐनलिज़र जनरेटर): lex एक ऐसा टूल है जो लेक्सिकल ऐनलिज़र जनरेट करता है. इन्हें लेक्सर या स्कैनर भी कहा जाता है. यह इनपुट के तौर पर रेगुलर एक्सप्रेशन और उनसे जुड़ी कार्रवाइयों का एक सेट लेता है. साथ ही, लेक्सिकल ऐनालाइज़र के लिए कोड जनरेट करता है, जो इनपुट सोर्स कोड में पैटर्न की पहचान करता है.
  • yacc (Yet Another Compiler Compiler): yacc एक ऐसा टूल है जो सिंटैक्स विश्लेषण के लिए पार्सर जनरेट करता है. यह प्रोग्रामिंग भाषा के व्याकरण के बारे में जानकारी को इनपुट के तौर पर लेता है और पार्स करने वाले टूल के लिए कोड जनरेट करता है. आम तौर पर, पार्स करने वाले टूल, अब्स्ट्रैक्ट सिंटैक्स ट्री (एएसटी) जनरेट करते हैं. ये एएसटी, सोर्स कोड के हैरारकी वाले स्ट्रक्चर को दिखाते हैं.

काम करने का उदाहरण

इस पोस्ट के दायरे को देखते हुए, पूरी प्रोग्रामिंग भाषा को कवर करना नामुमकिन है. इसलिए, अपनी समझ को आसान बनाने के लिए, ExampleScript नाम की एक बहुत सीमित और काम न आने वाली प्रोग्रामिंग भाषा का इस्तेमाल करें. यह प्रोग्रामिंग भाषा के लिए, सटीक उदाहरणों की मदद से सामान्य ऑपरेशन के बारे में बताती है.

  • add() फ़ंक्शन लिखने के लिए, किसी भी जोड़ के उदाहरण को कोड में लिखें, जैसे कि 2 + 3.
  • उदाहरण के लिए, multiply() फ़ंक्शन लिखने के लिए, 6 * 12 लिखें.

पहले से दी गई चेतावनी के मुताबिक, यह पूरी तरह से बेकार है. हालांकि, लेक्सिकल ऐनालाइज़र के लिए यह एक रेगुलर एक्सप्रेशन के तौर पर काफ़ी आसान है: /\d+\s*[\+\-\*\/]\s*\d+\s*/.

इसके बाद, पार्सर होना चाहिए. असल में, कैप्चरिंग ग्रुप के नाम वाले रेगुलर एक्सप्रेशन का इस्तेमाल करके, किसी ऐब्स्ट्रैक्ट सिंटैक्स ट्री का बहुत आसान वर्शन बनाया जा सकता है: /(?<first_operand>\d+)\s*(?<operator>[\+\-\*\/])\s*(?<second_operand>\d+)/.

ExampleScript के निर्देशों को एक लाइन में एक ही निर्देश लिखा जाता है. इसलिए, पार्स करने वाला टूल, कोड को लाइन के हिसाब से प्रोसेस कर सकता है. इसके लिए, वह नई लाइन के वर्णों के हिसाब से कोड को बांटता है. इतना ही, बुलेट सूची में दिए गए पहले तीन चरणों की जांच करने के लिए काफ़ी है. जैसे, लेक्सिकल विश्लेषण, सिंटैक्स विश्लेषण, और सेमैंटिक विश्लेषण. इन चरणों का कोड, यहां दी गई लिस्टिंग में मौजूद है.

export default class Parser {
  parse(input) {
    input = input.split(/\n/);
    if (!input.every((line) => /\d+\s*[\+\-\*\/]\s*\d+\s*/gm.test(line))) {
      throw new Error('Parse error');
    }

    return input.map((line) => {
      const { groups } =
        /(?<first_operand>\d+)\s*(?<operator>[\+\-\*\/])\s*(?<second_operand>\d+)/gm.exec(
          line,
        );
      return {
        firstOperand: Number(groups.first_operand),
        operator: groups.operator,
        secondOperand: Number(groups.second_operand),
      };
    });
  }
}

इंटरमीडिएट कोड जनरेशन

अब ExampleScript प्रोग्राम को ऐब्स्ट्रैक्ट सिंटैक्स ट्री के तौर पर दिखाया जा सकता है. भले ही, यह काफ़ी आसान है, इसलिए अगला चरण है, ऐब्स्ट्रैक्ट इंटरमीडिएट का प्रतिनिधित्व करना. सबसे पहले बाइनरीन में एक नया मॉड्यूल बनाएं:

const module = new binaryen.Module();

अब्सट्रैक्ट सिंटैक्स ट्री की हर लाइन में एक ट्रिपल होता है, जिसमें firstOperand, operator, और secondOperand शामिल होते हैं. ExampleScript में मौजूद चार ऑपरेटर, यानी +, -, *, / के लिए, Binaryen के Module#addFunction() तरीके का इस्तेमाल करके, मॉड्यूल में एक नया फ़ंक्शन जोड़ना ज़रूरी है. Module#addFunction() तरीकों के पैरामीटर इस तरह के होते हैं:

  • name: string, फ़ंक्शन का नाम दिखाता है.
  • functionType: Signature, फ़ंक्शन के हस्ताक्षर को दिखाता है.
  • varTypes: Type[], दिए गए क्रम में अन्य लोकल कोड दिखाता है.
  • body: Expression, फ़ंक्शन का कॉन्टेंट.

इस बारे में ज़्यादा जानकारी पाने के लिए, Binaryen दस्तावेज़ पढ़ें. इससे आपको इस बारे में ज़्यादा जानकारी मिल सकती है. हालांकि, आखिर में, ExampleScript के + ऑपरेटर के लिए, आपको Module#i32.add() का तरीका दिखेगा. यह कई उपलब्ध इंटिजर ऑपरेशन में से एक है. जोड़ने के लिए दो ऑपरेंड की ज़रूरत होती है, पहला और दूसरा जोड़. फ़ंक्शन को असल में कॉल करने के लिए, उसे Module#addFunctionExport() के साथ एक्सपोर्ट करना होगा.

module.addFunction(
  'add', // name: string
  binaryen.createType([binaryen.i32, binaryen.i32]), // params: Type
  binaryen.i32, // results: Type
  [binaryen.i32], // vars: Type[]
  //  body: ExpressionRef
  module.block(null, [
    module.local.set(
      2,
      module.i32.add(
        module.local.get(0, binaryen.i32),
        module.local.get(1, binaryen.i32),
      ),
    ),
    module.return(module.local.get(2, binaryen.i32)),
  ]),
);
module.addFunctionExport('add', 'add');

एब्स्ट्रैक्ट सिंटैक्स ट्री को प्रोसेस करने के बाद, मॉड्यूल में चार तरीके होते हैं. इनमें से तीन तरीके पूर्णांक संख्याओं के साथ काम करते हैं. जैसे, Module#i32.add() पर आधारित add(), Module#i32.sub() पर आधारित subtract(), Module#i32.mul() पर आधारित multiply(), और Module#f64.div() पर आधारित आउटलायर divide(). ऐसा इसलिए है, क्योंकि ExampleScript फ़्लोटिंग पॉइंट के नतीजों के साथ भी काम करता है.

for (const line of parsed) {
      const { firstOperand, operator, secondOperand } = line;

      if (operator === '+') {
        module.addFunction(
          'add', // name: string
          binaryen.createType([binaryen.i32, binaryen.i32]), // params: Type
          binaryen.i32, // results: Type
          [binaryen.i32], // vars: Type[]
          //  body: ExpressionRef
          module.block(null, [
            module.local.set(
              2,
              module.i32.add(
                module.local.get(0, binaryen.i32),
                module.local.get(1, binaryen.i32)
              )
            ),
            module.return(module.local.get(2, binaryen.i32)),
          ])
        );
        module.addFunctionExport('add', 'add');
      } else if (operator === '-') {
        module.subtractFunction(
          // Skipped for brevity.
        )
      } else if (operator === '*') {
          // Skipped for brevity.
      }
      // And so on for all other operators, namely `-`, `*`, and `/`.

अगर आपको असल कोड बेस से काम करना है, तो कभी-कभी ऐसा कोड मिल सकता है जिसे कभी इस्तेमाल नहीं किया जाता. Wasm के लिए ExampleScript के कंपाइलेशन के मौजूदा उदाहरण में, डेड कोड (जिसे बाद के चरण में ऑप्टिमाइज़ किया जाएगा और हटाया जाएगा) को आर्टिफ़िशियल तरीके से पेश करने के लिए, बिना एक्सपोर्ट किए गए फ़ंक्शन को जोड़ने से यह काम हो जाता है.

// This function is added, but not exported,
// so it's effectively dead code.
module.addFunction(
  'deadcode', // name: string
  binaryen.createType([binaryen.i32, binaryen.i32]), // params: Type
  binaryen.i32, // results: Type
  [binaryen.i32], // vars: Type[]
  //  body: ExpressionRef
  module.block(null, [
    module.local.set(
      2,
      module.i32.div_u(
        module.local.get(0, binaryen.i32),
        module.local.get(1, binaryen.i32),
      ),
    ),
    module.return(module.local.get(2, binaryen.i32)),
  ]),
);

कंपाइलर अब करीब-करीब तैयार है. यह ज़रूरी नहीं है, लेकिन Module#validate() तरीके का इस्तेमाल करके मॉड्यूल की पुष्टि करना एक अच्छा तरीका है.

if (!module.validate()) {
  throw new Error('Validation error');
}

नतीजे के तौर पर मिला Wasm कोड पाना

नतीजे के तौर पर मिलने वाला Wasm कोड पाने के लिए, Binaryen में दो तरीके मौजूद हैं. पहला, S-एक्सप्रेशन में .wat फ़ाइल के तौर पर टेक्स्ट के तौर पर दिखाया गया, जो कि इंसान के पढ़ने लायक फ़ॉर्मैट में होता है. दूसरा, .wasm फ़ाइल के तौर पर बाइनरी के तौर पर दिखाया गया, जो सीधे ब्राउज़र में चल सकता है. बाइनरी कोड को सीधे ब्राउज़र में चलाया जा सकता है. यह देखने के लिए कि एक्सपोर्ट काम कर रहे हैं या नहीं, एक्सपोर्ट को लॉग करना मददगार हो सकता है.

const textData = module.emitText();
console.log(textData);

const wasmData = module.emitBinary();
const compiled = new WebAssembly.Module(wasmData);
const instance = new WebAssembly.Instance(compiled, {});
console.log('Wasm exports:\n', instance.exports);

सभी चार कार्रवाइयों के साथ किसी ExampleScript प्रोग्राम के लिए, टेक्स्ट के तौर पर पूरी जानकारी नीचे दी गई है. ध्यान दें कि खराब कोड अब भी वहां मौजूद है, लेकिन वह WebAssembly.Module.exports() के स्क्रीनशॉट के मुताबिक नहीं दिखता.

(module
 (type $0 (func (param i32 i32) (result i32)))
 (type $1 (func (param f64 f64) (result f64)))
 (export "add" (func $add))
 (export "subtract" (func $subtract))
 (export "multiply" (func $multiply))
 (export "divide" (func $divide))
 (func $add (param $0 i32) (param $1 i32) (result i32)
  (local $2 i32)
  (local.set $2
   (i32.add
    (local.get $0)
    (local.get $1)
   )
  )
  (return
   (local.get $2)
  )
 )
 (func $subtract (param $0 i32) (param $1 i32) (result i32)
  (local $2 i32)
  (local.set $2
   (i32.sub
    (local.get $0)
    (local.get $1)
   )
  )
  (return
   (local.get $2)
  )
 )
 (func $multiply (param $0 i32) (param $1 i32) (result i32)
  (local $2 i32)
  (local.set $2
   (i32.mul
    (local.get $0)
    (local.get $1)
   )
  )
  (return
   (local.get $2)
  )
 )
 (func $divide (param $0 f64) (param $1 f64) (result f64)
  (local $2 f64)
  (local.set $2
   (f64.div
    (local.get $0)
    (local.get $1)
   )
  )
  (return
   (local.get $2)
  )
 )
 (func $deadcode (param $0 i32) (param $1 i32) (result i32)
  (local $2 i32)
  (local.set $2
   (i32.div_u
    (local.get $0)
    (local.get $1)
   )
  )
  (return
   (local.get $2)
  )
 )
)

WebAssembly मॉड्यूल एक्सपोर्ट के DevTools कंसोल का स्क्रीनशॉट, जिसमें चार फ़ंक्शन दिख रहे हैं: जोड़ना, भाग देना, गुणा करना, और घटाना. हालांकि, इसमें एक्सपोज़ नहीं किया गया डेड कोड नहीं दिख रहा है.

WebAssembly को ऑप्टिमाइज़ करना

Wasm कोड को ऑप्टिमाइज़ करने के लिए,binaryen दो तरीके देता है. Binaryen.js में एक और कमांड लाइन के लिए एक. पहला विकल्प, डिफ़ॉल्ट रूप से ऑप्टिमाइज़ेशन के नियमों का स्टैंडर्ड सेट लागू करता है. साथ ही, आपको ऑप्टिमाइज़ और छोटा करने का लेवल सेट करने की सुविधा देता है. वहीं, दूसरा विकल्प डिफ़ॉल्ट रूप से किसी भी नियम का इस्तेमाल नहीं करता. हालांकि, इसमें सेटिंग को पूरी तरह से पसंद के मुताबिक बनाया जा सकता है. इसका मतलब है कि ज़रूरत के मुताबिक प्रयोग करके, अपने कोड के आधार पर ऑप्टिमाइज़ेशन के लिए सेटिंग को बेहतर बनाया जा सकता है.

Binaryen.js का इस्तेमाल करके ऑप्टिमाइज़ करना

Binaryen की मदद से Wasm मॉड्यूल को ऑप्टिमाइज़ करने का सबसे आसान तरीका यह है कि आप सीधे तौर पर Binaryen.js के Module#optimize() तरीके को कॉल करें. इसके अलावा, ऑप्टिमाइज़ और छोटा करने के लेवल को सेट करना भी ज़रूरी नहीं है.

// Assume the `wast` variable contains a Wasm program.
const module = binaryen.parseText(wast);
binaryen.setOptimizeLevel(2);
binaryen.setShrinkLevel(1);
// This corresponds to the `-Os` setting.
module.optimize();

ऐसा करने से, पहले से आर्टिफ़िशियल तरीके से पेश किए गए डेड कोड को हटा दिया जाता है. ऐसा करने से, ExampleScript खिलौने के उदाहरण के Wasm वर्शन को टेक्स्ट के हिसाब से अब दिखाया नहीं जाता. यह भी ध्यान दें कि ऑप्टिमाइज़ेशन के चरणों के ज़रिए local.set/get पेयर कैसे हटाए जाते हैं. जैसे, SimplifyLocals (स्थानीय वैरिएबल से जुड़े अलग-अलग ऑप्टिमाइज़ेशन) और Vacuum (बेवजह इस्तेमाल होने वाले कोड को हटाता है). साथ ही, return को RemoveUnusedBrs (जिन जगहों पर ब्रेक की ज़रूरत नहीं है वहां से ब्रेक हटाता है) के ज़रिए हटाया जाता है.

 (module
 (type $0 (func (param i32 i32) (result i32)))
 (type $1 (func (param f64 f64) (result f64)))
 (export "add" (func $add))
 (export "subtract" (func $subtract))
 (export "multiply" (func $multiply))
 (export "divide" (func $divide))
 (func $add (; has Stack IR ;) (param $0 i32) (param $1 i32) (result i32)
  (i32.add
   (local.get $0)
   (local.get $1)
  )
 )
 (func $subtract (; has Stack IR ;) (param $0 i32) (param $1 i32) (result i32)
  (i32.sub
   (local.get $0)
   (local.get $1)
  )
 )
 (func $multiply (; has Stack IR ;) (param $0 i32) (param $1 i32) (result i32)
  (i32.mul
   (local.get $0)
   (local.get $1)
  )
 )
 (func $divide (; has Stack IR ;) (param $0 f64) (param $1 f64) (result f64)
  (f64.div
   (local.get $0)
   (local.get $1)
  )
 )
)

ऑप्टिमाइज़ेशन पास कई तरह के होते हैं. Module#optimize() इनमें, ऑप्टिमाइज़ और कम करने वाले लेवल के डिफ़ॉल्ट सेट का इस्तेमाल किया जाता है. पूरी तरह से कस्टमाइज़ करने के लिए, आपको कमांड लाइन टूल wasm-opt का इस्तेमाल करना होगा.

Wasm-ऑप्ट कमांड लाइन टूल की मदद से ऑप्टिमाइज़ किया जा रहा है

इस्तेमाल किए जाने वाले पास को पूरी तरह से पसंद के मुताबिक बनाने के लिए, Binaryen में wasm-opt कमांड लाइन टूल शामिल होता है. ऑप्टिमाइज़ेशन के संभावित विकल्पों की पूरी सूची पाने के लिए, टूल का सहायता मैसेज देखें. wasm-opt टूल शायद टूल में सबसे लोकप्रिय है. इसका इस्तेमाल, कई कंपाइलर टूलचेन ने Wasm कोड को ऑप्टिमाइज़ करने के लिए किया है. इनमें Emscripten, J2CL, Kotlin/Wasm, dart2wasm, wasm-pack वगैरह शामिल हैं.

wasm-opt --help

पास के बारे में आपको जानकारी देने के लिए, यहां कुछ पास के बारे में बताया गया है. इनमें से कुछ ऐसे पास हैं जिन्हें विशेषज्ञों की जानकारी के बिना भी समझा जा सकता है:

  • CodeFolding: डुप्लीकेट कोड को मर्ज करके उसे हटाता है. उदाहरण के लिए, अगर दो if ग्रुप के आखिर में कुछ शेयर किए गए निर्देश हैं.
  • DeadArgumentElimination: लिंक टाइम ऑप्टिमाइज़ेशन पास, किसी फ़ंक्शन के लिए आर्ग्युमेंट हटाने के लिए है. ऐसा तब किया जाता है, जब फ़ंक्शन को हमेशा एक ही कॉन्स्टेंट के साथ कॉल किया जाता है.
  • MinifyImportsAndExports: इन फ़ंक्शन को "a", "b" तक छोटा कर देता है.
  • DeadCodeElimination: डैड कोड हटाएं.

यहां ऑप्टिमाइज़ेशन कुकबुक उपलब्ध है. इसमें कई सलाह दी गई हैं. इनकी मदद से, यह पता लगाया जा सकता है कि किस तरह के फ़्लैग ज़्यादा ज़रूरी हैं और उन्हें सबसे पहले आज़माया जा सकता है. उदाहरण के लिए, कभी-कभी wasm-opt को बार-बार इस्तेमाल करने पर, इनपुट का साइज़ और भी छोटा हो जाता है. ऐसे मामलों में, --converge फ़्लैग के साथ चलाना तब तक बार-बार चलता रहता है, जब तक कोई और ऑप्टिमाइज़ेशन नहीं होता और एक तय पॉइंट तक नहीं पहुंच जाता.

डेमो

इस पोस्ट में बताए गए कॉन्सेप्ट को काम करते हुए देखने के लिए, एम्बेड किए गए डिमो को चलाएं. इसके लिए, अपनी पसंद के मुताबिक ExampleScript का कोई भी इनपुट दें. साथ ही, डेमो का सोर्स कोड देखना न भूलें.

मीटिंग में सामने आए नतीजे

Binaryen, भाषाओं को WebAssembly में कंपाइल करने और उससे मिले कोड को ऑप्टिमाइज़ करने के लिए, एक बेहतरीन टूलकिट उपलब्ध कराता है. इसकी JavaScript लाइब्रेरी और कमांड-लाइन टूल, इस्तेमाल में आसान और सुविधाजनक हैं. इस पोस्ट में, Wasm को कंपाइल करने के मुख्य सिद्धांतों के बारे में बताया गया है. साथ ही, यह भी बताया गया है कि बाइनरीयन कितना असरदार है और इसे ज़्यादा से ज़्यादा ऑप्टिमाइज़ कैसे किया जा सकता है. Binaryen के ऑप्टिमाइज़ेशन को पसंद के मुताबिक बनाने के कई विकल्पों के लिए, Wasm के इंटरनल के बारे में अच्छी जानकारी ज़रूरी होती है. हालांकि, आम तौर पर डिफ़ॉल्ट सेटिंग पहले से ही बेहतर तरीके से काम करती हैं. इसके बाद, Binaryen की मदद से कोड को कॉम्पाइल और ऑप्टिमाइज़ करने का आनंद लें!

धन्यवाद

इस पोस्ट की समीक्षा अलोन ज़काई, थॉमस लाइवली, और रेचल एंड्रयू ने की है.