การคอมไพล์และเพิ่มประสิทธิภาพ Wasm ด้วยไบนารี

Binaryen เป็นไลบรารีโครงสร้างพื้นฐานของคอมไพเลอร์และเครื่องมือ สำหรับ WebAssembly ซึ่งเขียนด้วย C++ โดยมีเป้าหมายเพื่อทำให้การ คอมไพล์เป็น WebAssembly เป็นไปอย่างรวดเร็ว มีประสิทธิภาพ และใช้งานง่าย ในโพสต์นี้ คุณจะได้เรียนรู้วิธีเขียนโมดูล WebAssembly ใน JavaScript โดยใช้ Binaryen.js API โดยใช้ ตัวอย่างภาษาของเล่นสังเคราะห์ที่ชื่อ ExampleScript คุณจะได้เรียนรู้ พื้นฐานของการสร้างโมดูล การเพิ่มฟังก์ชันลงในโมดูล และการส่งออก ฟังก์ชันจากโมดูล ซึ่งจะช่วยให้คุณมีความรู้เกี่ยวกับกลไกโดยรวม ของการคอมไพล์ภาษาโปรแกรมจริงเป็น WebAssembly นอกจากนี้ คุณจะได้เรียนรู้วิธีเพิ่มประสิทธิภาพโมดูล Wasm ทั้งด้วย Binaryen.js และใน บรรทัดคำสั่งด้วย wasm-opt

ข้อมูลเบื้องต้นเกี่ยวกับ Binaryen

Binaryen มี C API ที่ใช้งานง่ายในส่วนหัวเดียว และยังใช้จาก JavaScript ได้ด้วย โดยรับอินพุตในรูปแบบ WebAssembly แต่ก็รับกราฟโฟลว์การควบคุมทั่วไป สำหรับคอมไพเลอร์ที่ต้องการเช่นกัน

การแสดงผลระดับกลาง (IR) คือโครงสร้างข้อมูลหรือโค้ดที่คอมไพเลอร์หรือเครื่องเสมือนใช้ภายในเพื่อแสดงซอร์สโค้ด IR ภายในของ Binaryen ใช้โครงสร้างข้อมูลที่กะทัดรัดและออกแบบมาเพื่อการสร้างโค้ดและการเพิ่มประสิทธิภาพแบบขนานอย่างสมบูรณ์ โดยใช้แกน CPU ที่มีอยู่ทั้งหมด IR ของ Binaryen คอมไพล์ลงใน WebAssembly เนื่องจากเป็นชุดย่อยของ WebAssembly

เครื่องมือเพิ่มประสิทธิภาพของ Binaryen มีหลายการส่งผ่านที่ช่วยปรับปรุงขนาดและความเร็วของโค้ดได้ การเพิ่มประสิทธิภาพเหล่านี้มีจุดมุ่งหมายเพื่อให้ Binaryen มีประสิทธิภาพเพียงพอที่จะใช้เป็นคอมไพเลอร์ แบ็กเอนด์ได้ด้วยตัวเอง ซึ่งรวมถึงการเพิ่มประสิทธิภาพเฉพาะ WebAssembly (ที่คอมไพเลอร์อเนกประสงค์อาจไม่ทำ) ซึ่งคุณอาจคิดว่าเป็นการลดขนาด Wasm

AssemblyScript เป็นตัวอย่างผู้ใช้ Binaryen

โปรเจ็กต์จำนวนมากใช้ Binaryen เช่น AssemblyScript ซึ่งใช้ Binaryen เพื่อ คอมไพล์จากภาษาที่คล้ายกับ TypeScript ไปยัง WebAssembly โดยตรง ลองใช้ตัวอย่าง ใน AssemblyScript Playground

อินพุต AssemblyScript

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

โค้ด WebAssembly ที่เกี่ยวข้องในรูปแบบข้อความซึ่งสร้างโดย Binaryen

(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 ที่สร้างขึ้นตามตัวอย่างก่อนหน้า

Toolchain ของ Binaryen

ชุดเครื่องมือ Binaryen มีเครื่องมือที่มีประโยชน์มากมายสำหรับทั้งนักพัฒนา JavaScript และผู้ใช้บรรทัดคำสั่ง เครื่องมือบางส่วนเหล่านี้แสดงอยู่ใน รายการต่อไปนี้ ส่วนรายการเครื่องมือทั้งหมดที่รวมอยู่ จะอยู่ในREADMEไฟล์ของโปรเจ็กต์

  • binaryen.js: ไลบรารี JavaScript แบบสแตนด์อโลนที่แสดงเมธอด Binaryen สำหรับ การสร้างและเพิ่มประสิทธิภาพโมดูล Wasm สำหรับบิลด์ โปรดดู binaryen.js ใน npm (หรือดาวน์โหลดโดยตรงจาก GitHub หรือ unpkg)
  • wasm-opt: เครื่องมือบรรทัดคำสั่งที่โหลด WebAssembly และเรียกใช้การส่งผ่าน Binaryen IR
  • wasm-as และ wasm-dis: เครื่องมือบรรทัดคำสั่งที่ประกอบและแยกชิ้นส่วน WebAssembly
  • wasm-ctor-eval: เครื่องมือบรรทัดคำสั่งที่สามารถเรียกใช้ฟังก์ชัน (หรือบางส่วนของฟังก์ชัน) ในเวลาคอมไพล์
  • wasm-metadce: เครื่องมือบรรทัดคำสั่งสำหรับนำส่วนต่างๆ ของไฟล์ Wasm ออกได้อย่างยืดหยุ่น ซึ่งขึ้นอยู่กับวิธีใช้โมดูล
  • wasm-merge: เครื่องมือบรรทัดคำสั่งที่ผสานไฟล์ Wasm หลายไฟล์เป็นไฟล์เดียว โดยเชื่อมต่อการนำเข้าที่เกี่ยวข้องกับการส่งออกขณะดำเนินการ เหมือนกับ เครื่องมือจัดกลุ่มสำหรับ JavaScript แต่เป็นของ Wasm

การคอมไพล์เป็น WebAssembly

การคอมไพล์ภาษาหนึ่งเป็นอีกภาษาหนึ่งมักเกี่ยวข้องกับหลายขั้นตอน โดยขั้นตอนที่สำคัญที่สุดจะแสดงอยู่ในรายการต่อไปนี้

  • การวิเคราะห์คำ: แบ่งซอร์สโค้ดออกเป็นโทเค็น
  • การวิเคราะห์ไวยากรณ์: สร้าง Abstract Syntax Tree
  • การวิเคราะห์เชิงความหมาย: ตรวจหาข้อผิดพลาดและบังคับใช้กฎภาษา
  • การสร้างโค้ดระดับกลาง: สร้างการแสดงผลที่เป็นนามธรรมมากขึ้น
  • การสร้างโค้ด: แปลเป็นภาษาเป้าหมาย
  • การเพิ่มประสิทธิภาพโค้ดเฉพาะเป้าหมาย: เพิ่มประสิทธิภาพสำหรับเป้าหมาย

ในโลกของ Unix เครื่องมือที่ใช้บ่อยสำหรับการคอมไพล์คือ lex และ yacc

  • lex (Lexical Analyzer Generator): lex เป็นเครื่องมือที่สร้างโปรแกรมวิเคราะห์คำศัพท์ หรือที่เรียกว่า Lexer หรือ Scanner โดยจะรับชุดนิพจน์ทั่วไป และการดำเนินการที่เกี่ยวข้องเป็นอินพุต และสร้างโค้ดสำหรับ โปรแกรมวิเคราะห์คำที่จดจำรูปแบบในซอร์สโค้ดอินพุต
  • yacc (Yet Another Compiler Compiler): yacc เป็นเครื่องมือที่สร้าง ตัวแยกวิเคราะห์สำหรับการวิเคราะห์ไวยากรณ์ โดยจะรับคำอธิบายไวยากรณ์อย่างเป็นทางการของภาษาโปรแกรมเป็นอินพุตและสร้างโค้ดสำหรับตัวแยกวิเคราะห์ โดยทั่วไป ตัวแยกวิเคราะห์จะสร้าง Abstract Syntax Tree (AST) ที่แสดงโครงสร้างลำดับชั้นของซอร์สโค้ด

ตัวอย่างที่ทำเสร็จแล้ว

เนื่องจากขอบเขตของโพสต์นี้ เราจึงไม่สามารถครอบคลุมภาษาโปรแกรมทั้งหมดได้ ดังนั้นเพื่อความเรียบง่าย ให้พิจารณาภาษาโปรแกรมสังเคราะห์ที่จำกัดและไม่มีประโยชน์อย่างยิ่งที่ชื่อว่า ExampleScript ซึ่งทำงานโดยการแสดงการดำเนินการทั่วไปผ่านตัวอย่างที่เป็นรูปธรรม

  • หากต้องการเขียนadd() ฟังก์ชัน ให้เขียนโค้ดตัวอย่างการบวกใดๆ เช่น 2 + 3
  • หากต้องการเขียนฟังก์ชัน multiply() ให้เขียน เช่น 6 * 12

ตามคำเตือนล่วงหน้า ซึ่งไม่มีประโยชน์เลย แต่ก็ง่ายพอที่โปรแกรมวิเคราะห์ คำจะใช้เป็นนิพจน์ทั่วไปเดียวได้: /\d+\s*[\+\-\*\/]\s*\d+\s*/

จากนั้นต้องมีตัวแยกวิเคราะห์ จริงๆ แล้ว คุณสามารถสร้างเวอร์ชันที่ง่ายมากของ Abstract Syntax Tree ได้โดยใช้นิพจน์ทั่วไปกับ กลุ่มการจับภาพที่มีชื่อ /(?<first_operand>\d+)\s*(?<operator>[\+\-\*\/])\s*(?<second_operand>\d+)/

คำสั่ง ExampleScript จะมีคำสั่งละ 1 บรรทัด เพื่อให้ตัวแยกวิเคราะห์ประมวลผลโค้ด ทีละบรรทัดได้โดยการแยกตามอักขระขึ้นบรรทัดใหม่ ซึ่งเพียงพอที่จะตรวจสอบ 3 ขั้นตอนแรกจากรายการหัวข้อย่อยก่อนหน้านี้ ได้แก่ การวิเคราะห์คำ การวิเคราะห์ไวยากรณ์ และการวิเคราะห์ความหมาย โค้ดสำหรับขั้นตอนเหล่านี้อยู่ในข้อมูลต่อไปนี้

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 สามารถแสดงเป็น Abstract Syntax Tree (แม้ว่าจะเป็นแบบที่ค่อนข้างเรียบง่าย) ขั้นตอนถัดไปคือการสร้างการแสดงระดับกลางแบบนามธรรม ขั้นตอนแรกคือการสร้างโมดูลใหม่ใน Binaryen โดยทำดังนี้

const module = new binaryen.Module();

แต่ละบรรทัดของ Abstract Syntax Tree จะมี 3 สิ่งต่อไปนี้ firstOperand, operator และ secondOperand สำหรับตัวดำเนินการที่เป็นไปได้ทั้ง 4 รายการ ใน ExampleScript ได้แก่ +, -, *, / จะต้องเพิ่มฟังก์ชันใหม่ลงในโมดูล ด้วยเมธอด Module#addFunction() ของ Binaryen พารามิเตอร์ของเมธอด Module#addFunction() มีดังนี้

  • name: string แสดงชื่อฟังก์ชัน
  • functionType: Signature แสดงถึงลายเซ็นของฟังก์ชัน
  • varTypes: Type[] แสดงถึงผู้โทรในพื้นที่เพิ่มเติมตามลำดับที่ระบุ
  • body: Expression เนื้อหาของฟังก์ชัน

ยังมีรายละเอียดเพิ่มเติมที่ต้องทำความเข้าใจและแยกย่อย และเอกสารประกอบ Binaryen จะช่วยให้คุณเข้าใจพื้นที่นี้ได้ แต่ในที่สุดสำหรับตัวดำเนินการ + ของ ExampleScript คุณจะไปที่เมธอด Module#i32.add() ซึ่งเป็นหนึ่งในการดำเนินการกับจำนวนเต็มที่มีอยู่หลายรายการ การบวกต้องมีตัวถูกดำเนินการ 2 ตัว ได้แก่ ตัวตั้งและตัวบวก หากต้องการเรียกใช้ฟังก์ชันจริง คุณต้องส่งออกด้วย 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');

หลังจากประมวลผล Abstract Syntax Tree แล้ว โมดูลจะมี 4 วิธี 3 วิธีที่ใช้กับจำนวนเต็ม ได้แก่ add() ตาม Module#i32.add() subtract() ตาม Module#i32.sub() multiply() ตาม Module#i32.mul() และค่าผิดปกติ 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 `/`.

หากคุณจัดการกับโค้ดเบสจริง บางครั้งอาจมีโค้ดที่ไม่ได้ใช้ซึ่งไม่เคย ถูกเรียกใช้ หากต้องการสร้างโค้ดที่ไม่ได้ใช้ (ซึ่งจะได้รับการเพิ่มประสิทธิภาพและ กำจัดในขั้นตอนต่อๆ ไป) ในตัวอย่างที่กำลังทำงานของการคอมไพล์ ExampleScript เป็น Wasm การเพิ่มฟังก์ชันที่ไม่ได้ส่งออกจะช่วยให้ทำได้

// 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 มี 2 วิธีในการรับการแสดงข้อความ เป็นไฟล์ .wat ใน S-expression ในรูปแบบที่มนุษย์อ่านได้ และการแสดงไบนารี เป็นไฟล์ .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 ที่มีการดำเนินการทั้ง 4 รายการแสดงอยู่ในส่วนต่อไปนี้ โปรดสังเกตว่าโค้ดที่ไม่ได้ใช้ยังคงอยู่ แต่ไม่ได้แสดงตามภาพหน้าจอของ 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 ที่แสดงการส่งออก 4 ฟังก์ชัน ได้แก่ add, divide, multiply และ subtract (แต่ไม่ใช่โค้ดที่ไม่ได้ใช้ซึ่งไม่ได้เปิดเผย)

การเพิ่มประสิทธิภาพ WebAssembly

Binaryen มี 2 วิธีในการเพิ่มประสิทธิภาพโค้ด Wasm หนึ่งใน Binaryen.js เอง และ อีกหนึ่งสำหรับบรรทัดคำสั่ง ตัวเลือกแรกจะใช้ชุดกฎการเพิ่มประสิทธิภาพมาตรฐานโดยค่าเริ่มต้น และให้คุณตั้งค่าระดับการเพิ่มประสิทธิภาพและการลดขนาด ส่วนตัวเลือกที่สองจะไม่มีการใช้กฎใดๆ โดยค่าเริ่มต้น แต่จะอนุญาตให้ปรับแต่งได้อย่างเต็มที่ ซึ่งหมายความว่าหากทำการทดสอบมากพอ คุณจะปรับแต่งการตั้งค่าเพื่อให้ได้ผลลัพธ์ที่ดีที่สุดตามโค้ดของคุณได้

การเพิ่มประสิทธิภาพด้วย Binaryen.js

วิธีที่ตรงไปตรงมาที่สุดในการเพิ่มประสิทธิภาพโมดูล Wasm ด้วย Binaryen คือการเรียกใช้เมธอด Module#optimize() ของ Binaryen.js โดยตรง และอาจตั้งค่าระดับการเพิ่มประสิทธิภาพและการลดขนาด

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

การทำเช่นนี้จะนำโค้ดที่ไม่ได้ใช้ซึ่งเพิ่มเข้ามาโดยไม่จำเป็นออก ดังนั้น การแสดงข้อความของ Wasm เวอร์ชันของตัวอย่างของเล่น ExampleScript จะไม่มีโค้ดดังกล่าวอีกต่อไป โปรดสังเกตด้วยว่า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-opt

Binaryen มีwasm-optเครื่องมือบรรทัดคำสั่งสำหรับการปรับแต่งพาสที่จะใช้ทั้งหมด หากต้องการดูรายการตัวเลือกการเพิ่มประสิทธิภาพทั้งหมดที่เป็นไปได้ โปรดดูข้อความช่วยเหลือของเครื่องมือ wasm-opt น่าจะเป็นเครื่องมือที่ได้รับความนิยมมากที่สุด ในบรรดาเครื่องมือต่างๆ และใช้โดยเครื่องมือคอมไพเลอร์หลายตัวเพื่อเพิ่มประสิทธิภาพโค้ด Wasm รวมถึง Emscripten J2CL Kotlin/Wasm dart2wasm wasm-pack และอื่นๆ

wasm-opt --help

ตัวอย่างบัตรที่เข้าใจได้โดยไม่ต้องมีความรู้จากผู้เชี่ยวชาญมีดังนี้

  • การพับรหัส: หลีกเลี่ยงรหัสที่ซ้ำกันโดยการผสานรหัส (เช่น หาก if อาร์ม 2 ตัวมีคำสั่งที่แชร์กันที่ส่วนท้าย)
  • DeadArgumentElimination: การเพิ่มประสิทธิภาพเวลาลิงก์เพื่อนำอาร์กิวเมนต์ ไปยังฟังก์ชันออกหากมีการเรียกใช้ด้วยค่าคงที่เดียวกันเสมอ
  • MinifyImportsAndExports: ย่อขนาดเป็น "a", "b"
  • DeadCodeElimination: นำโค้ดที่ไม่ได้ใช้แล้วออก

เรามี สูตรการเพิ่มประสิทธิภาพ พร้อมเคล็ดลับมากมายในการระบุว่าฟีเจอร์ต่างๆ ใดมีความสำคัญมากกว่า และควรลองใช้ก่อน เช่น บางครั้งการเรียกใช้ wasm-opt ซ้ำๆ จะทำให้อินพุตเล็กลงไปอีก ในกรณีดังกล่าว การเรียกใช้ ด้วย--convergeแฟล็ก จะทําซ้ำไปเรื่อยๆ จนกว่าจะไม่มีการเพิ่มประสิทธิภาพอีกและถึงจุดคงที่

สาธิต

หากต้องการดูแนวคิดที่แนะนำในโพสต์นี้ในทางปฏิบัติ ให้ลองเล่นเดโมที่ฝังไว้ โดยป้อน ExampleScript ที่คุณคิดออก นอกจากนี้ อย่าลืมดูซอร์สโค้ดของเดโมด้วย

บทสรุป

Binaryen มีชุดเครื่องมือที่มีประสิทธิภาพสำหรับการคอมไพล์ภาษาต่างๆ เป็น WebAssembly และ การเพิ่มประสิทธิภาพโค้ดที่ได้ ไลบรารี JavaScript และเครื่องมือบรรทัดคำสั่ง ช่วยให้มีความยืดหยุ่นและใช้งานง่าย โพสต์นี้แสดงให้เห็นหลักการสำคัญของการ คอมไพล์ Wasm โดยเน้นประสิทธิภาพและศักยภาพของ Binaryen ในการ เพิ่มประสิทธิภาพสูงสุด แม้ว่าตัวเลือกหลายอย่างสำหรับการปรับแต่งการเพิ่มประสิทธิภาพของ Binaryen จะต้องมีความรู้เชิงลึกเกี่ยวกับส่วนประกอบภายในของ Wasm แต่โดยปกติแล้ว การตั้งค่าเริ่มต้นก็ทำงานได้ดีอยู่แล้ว ขอให้สนุกกับการคอมไพล์และเพิ่มประสิทธิภาพ ด้วย Binaryen!

คำขอบคุณ

โพสต์นี้ได้รับการตรวจสอบโดย Alon Zakai, Thomas Lively และ Rachel Andrew