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

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

ความเป็นมาเกี่ยวกับไบนารี

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

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

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

AssemblyScript ในฐานะผู้ใช้ตัวอย่างของ Binaryen

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

อินพุต AssemblyScript:

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

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

  • binaryen.js: ไลบรารี JavaScript แบบสแตนด์อโลนที่แสดงเมธอดแบบไบนารีสำหรับการสร้างและเพิ่มประสิทธิภาพโมดูล 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 หลายไฟล์เข้าด้วยกันเป็นไฟล์เดียว เชื่อมต่อการนำเข้าที่เกี่ยวข้องกับการส่งออกเช่นเดียวกับที่ทำได้ เหมือนกับ Bundler สำหรับ JavaScript แต่สำหรับ Wasm

กำลังคอมไพล์ไปยัง WebAssembly

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

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

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

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

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

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

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

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

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

const module = new binaryen.Module();

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

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

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

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

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

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

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

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

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

wasm-opt --help

เพื่อให้คุณได้สัมผัสถึงบัตรผ่านต่างๆ ต่อไปนี้คือข้อความที่ตัดตอนมาบางส่วนซึ่งสามารถเข้าใจได้หากไม่มีความรู้จากผู้เชี่ยวชาญ

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

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

ข้อมูลประชากร

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

บทสรุป

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

ข้อความแสดงการยอมรับ

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