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

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

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

เครื่องมือชุดค่าผสมของ Binaryen

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

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

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

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

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

ในระบบ Unix เครื่องมือที่ใช้บ่อยในการคอมไพล์ ได้แก่ lex และ yacc

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

ตัวอย่างที่ใช้งานได้

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

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

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

ถัดไปต้องมี parser จริงๆ แล้ว คุณสามารถสร้างต้นไม้ไวยากรณ์นามธรรมเวอร์ชันที่ง่ายมากได้โดยใช้นิพจน์ทั่วไปที่มีกลุ่มการจับคู่ที่มีชื่อ ดังนี้ /(?<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 สามารถแสดงเป็นต้นไม้ไวยากรณ์นามธรรม (แม้ว่าจะค่อนข้างง่าย) ขั้นตอนถัดไปคือการสร้างการแสดงข้อมูลกลางนามธรรม ขั้นตอนแรกคือการสร้างโมดูลใหม่ใน Binaryen โดยทำดังนี้

const module = new binaryen.Module();

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

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

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

หลังจากประมวลผลต้นไม้ไวยากรณ์นามธรรมแล้ว โมดูลจะมีเมธอด 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 ที่ได้ จะมี 2 วิธีใน Binaryen สำหรับการรับการนำเสนอแบบข้อความเป็นไฟล์ .wat ใน นิพจน์ S ในรูปแบบที่มนุษย์อ่านได้ และการนำเสนอแบบไบนารีเป็นไฟล์ .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)
  )
 )
)

ภาพหน้าจอคอนโซลของ DevTools ของการนําส่งโมดูล WebAssembly ที่แสดงฟังก์ชัน 4 รายการ ได้แก่ บวก หาร คูณ และลบ (แต่ไม่ใช่โค้ดที่ตายแล้วซึ่งไม่ได้แสดง)

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

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

การเพิ่มประสิทธิภาพด้วย 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();

ซึ่งจะนําโค้ดที่ตายแล้วซึ่งนําเข้าก่อนหน้านี้ออก ดังนั้นการนําเสนอแบบข้อความของตัวอย่างของ ExampleScript เวอร์ชัน Wasm จะไม่มีโค้ดดังกล่าวอีกต่อไป โปรดสังเกตด้วยว่าขั้นตอนการเพิ่มประสิทธิภาพ SimplifyLocals (การเพิ่มประสิทธิภาพที่เกี่ยวข้องกับตัวแปรย่อยอื่นๆ) และ Vacuum (นำโค้ดที่ไม่จำเป็นออกอย่างเห็นได้ชัด) นำคู่ local.set/get ออกได้อย่างไร และ RemoveUnusedBrs (นำการหยุดพักออกจากตำแหน่งที่ไม่จำเป็น) นำ 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

ต่อไปนี้เป็นตัวอย่างของบัตรบางส่วนที่เข้าใจได้โดยไม่ต้องมีความรู้เฉพาะทาง

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

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

สาธิต

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

สรุป

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

ขอขอบคุณ

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