Binaryen-এর সাথে Wasm-এ কম্পাইল করা এবং অপ্টিমাইজ করা

Binaryen হল WebAssembly-এর জন্য একটি কম্পাইলার এবং টুলচেইন অবকাঠামো লাইব্রেরি, C++ এ লেখা। এটির লক্ষ্য হল WebAssembly-এ কম্পাইলিংকে স্বজ্ঞাত, দ্রুত এবং কার্যকর করা। এই পোস্টে, ExampleScript নামক একটি সিন্থেটিক খেলনা ভাষার উদাহরণ ব্যবহার করে, Binaryen.js API ব্যবহার করে জাভাস্ক্রিপ্টে WebAssembly মডিউল কীভাবে লিখতে হয় তা শিখুন। আপনি মডিউল তৈরির মৌলিক বিষয়গুলি কভার করবেন, মডিউলে ফাংশন যোগ করবেন এবং মডিউল থেকে ফাংশন রপ্তানি করবেন। এটি আপনাকে WebAssembly-এ প্রকৃত প্রোগ্রামিং ভাষা কম্পাইল করার সামগ্রিক মেকানিক্স সম্পর্কে জ্ঞান দেবে। আরও, আপনি শিখবেন কিভাবে Binaryen.js এর সাথে এবং wasm-opt এর সাথে কমান্ড লাইনে Wasm মডিউল অপ্টিমাইজ করতে হয়।

Binaryen উপর পটভূমি

বাইনারিনের একটি একক শিরোনামে একটি স্বজ্ঞাত C API রয়েছে এবং এটি JavaScript থেকেও ব্যবহার করা যেতে পারে। এটি WebAssembly আকারে ইনপুট গ্রহণ করে, তবে এটি পছন্দ করে এমন কম্পাইলারদের জন্য একটি সাধারণ নিয়ন্ত্রণ প্রবাহ গ্রাফও গ্রহণ করে।

একটি মধ্যবর্তী প্রতিনিধিত্ব (IR) হল ডেটা স্ট্রাকচার বা কোড যা অভ্যন্তরীণভাবে একটি কম্পাইলার বা ভার্চুয়াল মেশিন দ্বারা সোর্স কোডের প্রতিনিধিত্ব করার জন্য ব্যবহৃত হয়। Binaryen এর অভ্যন্তরীণ IR কমপ্যাক্ট ডেটা স্ট্রাকচার ব্যবহার করে এবং সমস্ত উপলব্ধ CPU কোর ব্যবহার করে সম্পূর্ণ সমান্তরাল কোড জেনারেশন এবং অপ্টিমাইজেশনের জন্য ডিজাইন করা হয়েছে। WebAssembly-এর একটি উপসেট হওয়ার কারণে Binaryen-এর IR WebAssembly-এ কম্পাইল করা হয়।

বাইনারিনের অপ্টিমাইজারের অনেকগুলি পাস রয়েছে যা কোডের আকার এবং গতি উন্নত করতে পারে। এই অপ্টিমাইজেশানগুলি বাইনারিনকে যথেষ্ট শক্তিশালী করে তোলার লক্ষ্যে নিজেই একটি কম্পাইলার ব্যাকএন্ড হিসাবে ব্যবহার করা যায়। এতে WebAssembly-নির্দিষ্ট অপ্টিমাইজেশান অন্তর্ভুক্ত রয়েছে (যেটি সাধারণ-উদ্দেশ্য কম্পাইলাররা নাও করতে পারে), যা আপনি Wasm minification হিসাবে ভাবতে পারেন।

Binaryen এর একটি উদাহরণ ব্যবহারকারী হিসাবে অ্যাসেম্বলিস্ক্রিপ্ট

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

সমাবেশস্ক্রিপ্ট ইনপুট:

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
 )
)

অ্যাসেম্বলিস্ক্রিপ্ট খেলার মাঠ পূর্ববর্তী উদাহরণের উপর ভিত্তি করে তৈরি করা WebAssembly কোড দেখাচ্ছে।

বাইনারিন টুলচেইন

Binaryen টুলচেইন জাভাস্ক্রিপ্ট ডেভেলপার এবং কমান্ড লাইন ব্যবহারকারী উভয়ের জন্যই বেশ কিছু দরকারী টুল অফার করে। এই টুলগুলির একটি উপসেট নীচে তালিকাভুক্ত করা হয়েছে; অন্তর্ভুক্ত সরঞ্জামগুলির সম্পূর্ণ তালিকা প্রকল্পের README ফাইলে উপলব্ধ।

  • binaryen.js : একটি স্বতন্ত্র জাভাস্ক্রিপ্ট লাইব্রেরি যা Wasm মডিউল তৈরি এবং অপ্টিমাইজ করার জন্য বাইনারিন পদ্ধতিগুলিকে প্রকাশ করে৷ বিল্ডগুলির জন্য, npm-এ binaryen.js দেখুন (বা এটি সরাসরি GitHub বা unpkg থেকে ডাউনলোড করুন)।
  • wasm-opt : কমান্ড লাইন টুল যা WebAssembly লোড করে এবং Binaryen IR পাস চালায়।
  • wasm-as এবং wasm-dis : কমান্ড লাইন টুল যা WebAssembly একত্রিত এবং বিচ্ছিন্ন করে।
  • wasm-ctor-eval : কমান্ড লাইন টুল যা কম্পাইলের সময় ফাংশন (বা ফাংশনের অংশ) চালাতে পারে।
  • wasm-metadce : একটি নমনীয় উপায়ে Wasm ফাইলের অংশগুলি সরানোর জন্য কমান্ড লাইন টুল যা মডিউলটি কীভাবে ব্যবহার করা হয় তার উপর নির্ভর করে।
  • wasm-merge : কমান্ড লাইন টুল যা একাধিক Wasm ফাইলকে একক ফাইলে একত্রিত করে, সংশ্লিষ্ট আমদানিকে রপ্তানির সাথে সংযুক্ত করে যেমন এটি করে। জাভাস্ক্রিপ্টের জন্য একটি বান্ডলারের মতো, তবে ওয়াসমের জন্য।

WebAssembly এ কম্পাইল করা হচ্ছে

একটি ভাষাকে অন্য ভাষায় কম্পাইল করার জন্য সাধারণত কয়েকটি ধাপ জড়িত থাকে, সবচেয়ে গুরুত্বপূর্ণগুলি নিম্নলিখিত তালিকায় তালিকাভুক্ত করা হয়েছে:

  • আভিধানিক বিশ্লেষণ: সোর্স কোডকে টোকেনে বিভক্ত করুন।
  • সিনট্যাক্স বিশ্লেষণ: একটি বিমূর্ত সিনট্যাক্স ট্রি তৈরি করুন।
  • শব্দার্থগত বিশ্লেষণ: ত্রুটিগুলি পরীক্ষা করুন এবং ভাষার নিয়মগুলি প্রয়োগ করুন৷
  • মধ্যবর্তী কোড জেনারেশন: আরও বিমূর্ত উপস্থাপনা তৈরি করুন।
  • কোড জেনারেশন: টার্গেট ভাষায় অনুবাদ করুন।
  • লক্ষ্য-নির্দিষ্ট কোড অপ্টিমাইজেশান: লক্ষ্যের জন্য অপ্টিমাইজ করুন।

ইউনিক্স বিশ্বে, কম্পাইল করার জন্য প্রায়শই ব্যবহৃত সরঞ্জামগুলি হল lex এবং yacc :

  • lex (লেক্সিক্যাল অ্যানালাইজার জেনারেটর): lex হল একটি টুল যা লেক্সিক্যাল অ্যানালাইজার তৈরি করে, যা লেক্সার বা স্ক্যানার নামেও পরিচিত। এটি ইনপুট হিসাবে নিয়মিত অভিব্যক্তি এবং সংশ্লিষ্ট ক্রিয়াগুলির একটি সেট নেয় এবং একটি আভিধানিক বিশ্লেষকের জন্য কোড তৈরি করে যা ইনপুট উত্স কোডের নিদর্শনগুলিকে স্বীকৃতি দেয়।
  • yacc (এখনও আরেকটি কম্পাইলার কম্পাইলার): yacc একটি টুল যা সিনট্যাক্স বিশ্লেষণের জন্য পার্সার তৈরি করে। এটি ইনপুট হিসাবে একটি প্রোগ্রামিং ভাষার একটি আনুষ্ঠানিক ব্যাকরণ বর্ণনা নেয় এবং একটি পার্সারের জন্য কোড তৈরি করে। পার্সাররা সাধারণত বিমূর্ত সিনট্যাক্স ট্রি (ASTs) তৈরি করে যা সোর্স কোডের শ্রেণীবদ্ধ কাঠামোকে প্রতিনিধিত্ব করে।

একটি কাজের উদাহরণ

এই পোস্টের সুযোগের পরিপ্রেক্ষিতে, একটি সম্পূর্ণ প্রোগ্রামিং ভাষা কভার করা অসম্ভব, তাই সরলতার জন্য, 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 , ফাংশনের বিষয়বস্তু।

বিচ্ছিন্ন করার জন্য আরও কিছু বিবরণ রয়েছে এবং বাইনারিন ডকুমেন্টেশন আপনাকে স্থানটি নেভিগেট করতে সাহায্য করতে পারে, কিন্তু শেষ পর্যন্ত, 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');

বিমূর্ত সিনট্যাক্স ট্রি প্রক্রিয়া করার পরে, মডিউলটিতে চারটি পদ্ধতি রয়েছে, তিনটি পূর্ণসংখ্যার সাথে কাজ করে, যথা add() Module#i32.add() এর উপর ভিত্তি করে, subtract() Module#i32.sub() এর উপর ভিত্তি করে, multiply() ভিত্তিক। Module#i32.mul() , এবং Outlier divide() Module#f64.div() এর উপর ভিত্তি করে কারণ 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)
  )
 )
)

DevTools Console WebAssembly মডিউল রপ্তানির স্ক্রিনশট চারটি ফাংশন দেখাচ্ছে: যোগ, ভাগ, গুণ এবং বিয়োগ (কিন্তু প্রকাশ না করা ডেড কোড নয়)।

WebAssembly অপ্টিমাইজ করা

Binaryen Wasm কোড অপ্টিমাইজ করার দুটি উপায় অফার করে। একটি 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 সংস্করণের পাঠ্য উপস্থাপনা আর এটি ধারণ করে না। অপ্টিমাইজেশান ধাপ SimplifyLocals (বিবিধ স্থানীয়-সম্পর্কিত অপ্টিমাইজেশন) এবং ভ্যাকুয়াম (স্পষ্টতই অপ্রয়োজনীয় কোড সরিয়ে দেয়) দ্বারা local.set/get জোড়াগুলি কীভাবে সরানো হয় তাও নোট করুন এবং RemoveUusedBrs দ্বারা return সরানো হয় (অপ্রয়োজনীয় অবস্থানগুলি থেকে বিরতিগুলি সরিয়ে দেয়) )

 (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-opt কমান্ড লাইন টুল দিয়ে অপ্টিমাইজ করা হচ্ছে

ব্যবহারযোগ্য পাসের সম্পূর্ণ কাস্টমাইজেশনের জন্য, Binaryen wasm-opt কমান্ড লাইন টুল অন্তর্ভুক্ত করে। সম্ভাব্য অপ্টিমাইজেশান বিকল্পগুলির একটি সম্পূর্ণ তালিকা পেতে, টুলটির সহায়তা বার্তাটি পরীক্ষা করুন৷ wasm-opt টুলটি সম্ভবত টুলগুলির মধ্যে সবচেয়ে জনপ্রিয়, এবং অনেকগুলি কম্পাইলার টুলচেইন দ্বারা Wasm কোড অপ্টিমাইজ করার জন্য ব্যবহার করা হয়, যার মধ্যে Emscripten , J2CL , Kotlin/Wasm , dart2wasm , wasm-pack এবং অন্যান্য রয়েছে।

wasm-opt --help

পাসগুলি সম্পর্কে আপনাকে একটি অনুভূতি দেওয়ার জন্য, বিশেষজ্ঞের জ্ঞান ছাড়াই বোধগম্য কিছুগুলির একটি অংশ এখানে দেওয়া হল:

  • কোডফোল্ডিং: এটিকে মার্জ করে ডুপ্লিকেট কোড এড়িয়ে যায় (উদাহরণস্বরূপ, if দুটি অস্ত্রের শেষে কিছু শেয়ার করা নির্দেশ থাকে)।
  • DeadArgumentElimination: একটি ফাংশন থেকে আর্গুমেন্ট অপসারণের জন্য লিংক টাইম অপ্টিমাইজেশান পাস যদি সবসময় একই ধ্রুবক দিয়ে কল করা হয়।
  • MinifyImportsAndExports: এগুলিকে "a" , "b" এ ছোট করে।
  • DeadCodeElimination: মৃত কোড সরান।

বিভিন্ন পতাকাগুলির মধ্যে কোনটি বেশি গুরুত্বপূর্ণ এবং প্রথমে চেষ্টা করার মতো তা সনাক্ত করার জন্য বিভিন্ন টিপস সহ একটি অপ্টিমাইজেশন কুকবুক উপলব্ধ রয়েছে৷ উদাহরণস্বরূপ, কখনও কখনও wasm-opt বারবার বারবার চালানো ইনপুটকে আরও সঙ্কুচিত করে। এই ধরনের ক্ষেত্রে, --converge ফ্ল্যাগ দিয়ে চালানো পুনরাবৃত্তি করতে থাকে যতক্ষণ না আর কোনো অপ্টিমাইজেশান ঘটছে এবং একটি নির্দিষ্ট পয়েন্টে পৌঁছানো যাচ্ছে।

ডেমো

এই পোস্টে প্রবর্তিত ধারণাগুলিকে কার্যকরভাবে দেখতে, এমবেডেড ডেমো দিয়ে খেলুন যা আপনি ভাবতে পারেন এমন যেকোনো ExampleScript ইনপুট প্রদান করে। এছাড়াও ডেমোর সোর্স কোড দেখতে ভুলবেন না।

উপসংহার

Binaryen WebAssembly-এ ভাষা কম্পাইল করার জন্য এবং ফলাফল কোড অপ্টিমাইজ করার জন্য একটি শক্তিশালী টুলকিট প্রদান করে। এর জাভাস্ক্রিপ্ট লাইব্রেরি এবং কমান্ড-লাইন টুল নমনীয়তা এবং ব্যবহারের সহজতা প্রদান করে। এই পোস্টটি ওয়াসম সংকলনের মূল নীতিগুলি প্রদর্শন করেছে, বাইনারিনের কার্যকারিতা এবং সর্বাধিক অপ্টিমাইজেশনের সম্ভাব্যতা তুলে ধরেছে। যদিও বাইনারিনের অপ্টিমাইজেশন কাস্টমাইজ করার জন্য অনেকগুলি বিকল্পের জন্য Wasm-এর অভ্যন্তরীণ সম্পর্কে গভীর জ্ঞানের প্রয়োজন, সাধারণত ডিফল্ট সেটিংস ইতিমধ্যেই দুর্দান্ত কাজ করে। যে সঙ্গে, খুশি কম্পাইলিং এবং Binaryen সঙ্গে অপ্টিমাইজ করা!

স্বীকৃতি

এই পোস্টটি অ্যালোন জাকাই , থমাস লাইভলি এবং রাচেল অ্যান্ড্রু দ্বারা পর্যালোচনা করা হয়েছে।