How to Boost Your Code with WebAssembly

9278c3a06b8d8752fb913dea93f959c1?s=47 FUJI Goro
November 30, 2019

How to Boost Your Code with WebAssembly

9278c3a06b8d8752fb913dea93f959c1?s=128

FUJI Goro

November 30, 2019
Tweet

Transcript

  1. 2.

    ࣗݾ঺հ • FUJI Goro / @__gfx__ • ͜͜਺೥͸΢ΣϒΤϯδχΞͱͯ͠ Rails/React/TypeScript/ GraphQL

    ͋ͨΓͷٕज़Λ࢖ͬͯαʔϏε։ൃΛͯ͠·ͨ͠ • ͜ͷτʔΫ͸झຯͰ @msgpack/msgpack Λ։ൃͨ͠ͱ͖ ͷܦݧʹج͖ͮ·͢ • ࠷ۙCDN providerͷFastlyʹస৬͠·ͨ͠ • స৬͔ͯ͠Β͸ओʹCݴޠͰHTTP serverΛ։ൃͯ͠·͢
  2. 5.

    WebAssemblyલ໷ (2012~) • EmscriptenʹΑͬͯC/C++ΛJSʹίϯύΠϧ͢Δ ͜ͱ͸Ͱ͖͍ͯͨ (2012~) • ྫ: https://github.com/gfx/perl.js (2014)

    • ͨͩ͜͠Ε͸ඇৗʹಈ࡞଎౓͕஗͔ͬͨ • JSͰCPUΛΤϛϡϨʔτ͢ΔͷͰ஗͍ͷ͸ٕज़ తඞવͩͬͨ
  3. 6.

    WebAssembly (2015~) • 2015೥ʹWebAssemblyͷ࢓༷ͷٞ࿦͕։࢝ • 2017೥ʹChrome, Safari, Firefox, Edgeͱ͍ͬͨϞμ ϯϒϥ΢βͰͷ

    v1.0 (“MVP”) αϙʔτ͕׬ྃ • 2019೥͸WASIͷൃද΍Bytecode Allienceͷൃ଍ͳ Ͳɺϒϥ΢β֎Ͱͷ༻్͕૿͑ͯ͘Δ • ݱࡏ͸SIMDͳͲͷ͞ΒͳΔػೳ(“post MVP”)͕ਐߦத
  4. 9.

    WebAssemblyͷ଎౓ͱ҆શੑ • ʮ଎͍ʯͱ͸͍͏΋ͷͷCΑΓ਺ഒ஗͍ • ͜Ε͸҆શੑͷͨΊະఆٛಈ࡞ (UB) Λͳ͍ͯ͘͠Δ͔Β • ͨͱ͑͹ɺCͰ͸഑ྻͷൣғ֎΁ͷΞΫηε͸UB •

    WasmͰ͸֬อͨ͠ϝϞϦͷൣғ֎΁ͷΞΫηε΋ఆٛࡁΈ • ͜ͷ҆શੑ͕ͦ͜େ͖ͳར఺ • UB͕ͳ͍͔Βͦ͜όάͷ͋ΔWasmͰ΋҆શʹ࣮ߦͰ͖Δ
  5. 16.

    WebAssembly࠾༻ࣄྫ • eBayͷࣄྫ - όʔίʔυεΩϟφ for Web • “WebAssembly at

    eBay - a Real-World Use Case” • https://tech.ebayinc.com/engineering/ webassembly-at-ebay-a-real-world-use-case/ • WasmؚΉ͍͔ͭ͘ͷ࣮૷Λฒྻʹ࣮ߦͯ͠࠷଎ Ͱ੒ޭͨ݁͠ՌΛ࠾༻͢Δͱ͍͏ΞʔΩςΫνϟ
  6. 17.

    WebAssembly as a universal executable binary • ༨ஊ: WebAssembly͸͸ϢχόʔαϧͳόΠφϦͱͯ͠ ੒௕ͭͭ͋͠Δ

    • ͜ΕΛਐΊ͍ͯΔͷ͕ Bytecode Allience ͱ͍͏ஂମ • ྫ: Lucet (Fastly→Bytecode Allience): WasmΛωΠςΟ ϒίʔυʹίϯύΠϧ͢ΔπʔϧΩοτ • Compute@Edge (Fastly): ͸LucetͰίϯύΠϧͨ͠ WasmΛCDNͷΤοδαʔόʔͰ࣮ߦ͢Δ࢓૊Έ
  7. 19.

    ݴޠ͝ͱͷಈ࡞଎౓Πϝʔδ • Ϋϥε1 “θϩΦʔόʔϔου” • C, C++, Rust • Ϋϥε3

    “ߴ଎ͳίϯύΠϥ” • Java, C#, Go, Swift • Ϋϥε5 “ߴ଎ͳΠϯλϓϦλ” • JavaScript (V8), Dart • Ϋϥε50 “ී௨ͷΠϯλϓϦλ” • Ruby, Python, Perl, PHP
  8. 22.

    WebAssemblyʹΑΔߴ଎Խ • ੡඼શମΛ C / C++ / Rust / Go

    Ͱ࣮૷͠Wasmʹ ίϯύΠϧ͢Δ • ͦΕͳΓʹීٴ͠͸͡Ί͍ͯΔ • ੡඼ͷҰ෦ΛWasmʹίϯύΠϧͯ͠ߴ଎Խ͢Δ • ͜ͷτʔΫͰͷؔ৺ࣄ͸ͬͪ͜ • ͜ͷ༻్ͷՁ஋͸ະ஌਺
  9. 23.

    ݴޠ: AssemblyScript • બ୒ࢶ: C / C++ / Rust /

    Go / AssemblyScript • ࠓճ͸ϥϯλΠϜ͕Ұ൪খͦ͞͏ͳ AssemblyScriptΛબ୒
  10. 27.

    ϦϙδτϦ • https://github.com/msgpack/msgpack- javascript • v1.9.3 ݱࡏͷ࿩ • npm install

    @msgpack/msgpack ͰΠϯείՄೳ • ͨͩ͠Wasm൛͸ݱࡏweb޲͚ͷϏϧυͰ͸࢖ ΘΕͳ͍Α͏ʹͳ͍ͬͯΔ
  11. 28.

    ࣮૷ • MessagePack decoderͷҰ෦ɺจࣈྻͷσίʔυΛ JS / Wasm (AS) ͰͦΕͧΕͰ࣮૷ͨ͠ɻ·ͨV8૊Έ ࠐΈͷಉ͡ڍಈͷωΠςΟϒίʔυؔ਺΋ϕϯν

    ϚʔΫର৅ͱͨ͠ • ΍͍ͬͯΔ͜ͱ͸UTF-8ͷ഑ྻΛUTF-16ͷ഑ྻʹม ׵͢Δ͜ͱ • AssemblyScriptʹҠ২ͯ͠100ߦఔ౓ͷίʔυ
  12. 29.

    JS൛ͷίʔυʢൈਮʣ export function utf8DecodeJs(bytes: Uint8Array, inputOffset: numbe byteLength: number): string

    { let offset = inputOffset; const end = offset + byteLength; const units: Array<number> = []; while (offset < end) { const byte1 = bytes[offset++]; if ((byte1 & 0x80) === 0) { // 1 byte units.push(byte1); } // ... } return String.fromCharCode(...units); }
  13. 30.

    ωΠςΟϒίʔυ൛ͷίʔυ const sharedTextDecoder = new TextDecoder(); export function utf8DecodeTD(bytes: Uint8Array,

    inputOffset: numbe byteLength: number): string { const stringBytes = bytes.subarray(inputOffset, inputOffset + byteLength); return sharedTextDecoder!.decode(stringBytes); }
  14. 31.

    AS൛ͷίʔυʢൈਮ, AS 0.6ʣ export function utf8DecodeToUint16Array(outputPtr: usize, inputPtr: usize, byteLength:

    usize): usize { let inputOffset = inputPtr; let outputOffset = outputPtr; let inputOffsetEnd = inputOffset + byteLength; const u16s = sizeof<u16>(); while (inputOffset < inputOffsetEnd) { let byte1: u16 = load<u8>(inputOffset++); if ((byte1 & 0x80) === 0) { // 1 byte store<u16>(outputOffset, byte1); outputOffset += u16s; } } return (outputOffset - outputPtr) / u16s; }
  15. 32.

    AS൛ͷίʔυʢJSଆʣ // wm = InstantiatedWasmModule.exports type pointer = number; //

    32-bit integer export function utf8DecodeWasm(bytes: Uint8Array, inputOffset: numbe byteLength: number): string { const inputPtr: pointer = wm.malloc(byteLength); const outputPtr: pointer = wm.malloc(byteLength * 2); try { setMemoryU8(inputPtr, bytes.subarray(inputOffset, inputOffset + byteLength), byteLength); const outputArraySize = wm.utf8DecodeToUint16Array(outputPtr, inputPtr, byteLength); const units = new Uint16Array(wm.memory.buffer, outputPtr, outputArraySize); return String.fromCharCode(...units); } finally { wm.free(inputPtr); wm.free(outputPtr); } }
  16. 33.

    Wasm function΁ͷೖྗ • Wasmʹ౉ͤΔ஋͸2छྨ • (1) WASM functionͷݺͼग़͠ͷҾ਺ͱͯ͠ɺ೚ҙݸͷ੔਺·ͨ͸ුಈখ ਺఺਺ •

    (2) WASM moduleͷbuffer: ArrayBufferʹ஋Λॻ͖ࠐΉɻArrayBufferʹॻ ͖ࠐΊΔ஋ͳΒͳΜͰ΋OK • Ͳ͜ʹॻ͖ࠐΜ͔ͩͷoffset (੔਺) Λ(1)ͱͯ͠Ҿ਺ͱͯ͠౉͢ • Cݴޠతʹݴ͑͹͜Ε͕ϙΠϯλͱ౳Ձ • ASͰ͸ load<T>(offset) ؔ਺ͰಡΈग़ͤΔ
  17. 34.

    Wasm function͔Βͷग़ྗ • ੔਺·ͨ͸ුಈখ਺఺਺1͚ͭͩ • ʢWasmతʹ͸೚ҙͷ਺ͷ஋ΛฦͤΔʣ • ࠓճ͸ೖྗ஋ͱͯ͠outputPtr (offset) Λ౉͠ɺ

    Wasm function͸ग़ྗ஋ΛoutputPtrͷҐஔʹॻ ͖ࠐΈɺॻ͖ࠐΜͩαΠζΛWasm function͔ Βฦ͢ͱ͍͏͜ͱʹͨ͠
  18. 35.

    Wasm functionͷγάωνϟ • ίϝϯτ͖ͭͰ࠶ܝ͢Δͱɺ͜Μͳײ͡ export function utf8DecodeToUint16Array( outputPtr: usize, //

    output offset inputPtr: usize, // input offset byteLength: usize, // input length ): usize; // output length
  19. 36.

    AssemblyScriptͷϋϚΓͲ͜Ζ • ϙΠϯλܕ͸ͳ͘ɺΦϑηοτͱͯ͠ usize (uint32_t) ܕ͕͋Δ͚ͩ • load<u16>() / store<u16>()

    ͳͲ͸ϦτϧΤϯσΟΞϯͱنఆ͞Εͯ ͍Δ͕ɺJSଆͷtyped arrays (Uint16Array) ͸ϗετͷΤϯσΟΞϯ ͳͷͰ࢓༷తʹ͸ΤϯσΟΞϯޓ׵ੑ͕ͳ͍ • ͔͠͠ɺ͖ΐ͏ͼͷϚγϯ͸΄΅͢΂ͯϦτϧΤϯσΟΞϯͳͷ ͰɺԿ΋ߟ͑ͳͯ͘΋ಈ͍ͯ͠·͏ʢASͷ໰୊Ͱ͸ͳ͍͕ʣ • ݟͨ໨͕TypeScriptͳ͜ͱʹؾ͕࣋ͪҾ͖ͣΒΕͯຌϛε͕සൃ͢Δ
  20. 39.

    ؀ڥ • macOS 10.14 • NodeJS 12.6.0 • v8 7.5

    ʢChrome 75૬౰ʣ • ࠓճ͸NodeJSͷΈͰϕϯνϚʔΫΛͨ͠
  21. 41.

    νϟʔτͷݟํ • ॎ࣠͸ log10 (ops per sec) • ͦͷ··ͩͱݟͮΒ͍ͷͰର਺ʹͯ͋͠Δ •

    ஋͕େ͖͍΄Ͳੑೳ͕Α͍ • ԣ࣠͸σʔλαΠζ • ಉ͡σʔλαΠζಉ࢜Ͱൺֱ͢Δ͜ͱ • σʔλαΠζ͕ҟͳΔσʔλͷൺֱ͸ແҙຯ
  22. 42.

    ϕϯνϚʔΫ݁Ռ 0 2 4 6 8 10 100 200 500

    1000 10000 utf8DecodeJs utf8DecodeWasm TextDecoder default, NodeJS/v12.6.0, v8/7.5
  23. 50.

    ϕϯνϚʔΫ݁Ռ (—no-opt) 0 2 4 6 8 10 100 200

    500 1000 10000 utf8DecodeJs utf8DecodeWasm TextDecoder --no-opt NodeJS v12.6.0, v8 7.5 on macOS
  24. 51.

    ϕϯνϚʔΫ݁Ռ (default) 0 2 4 6 8 10 100 200

    500 1000 10000 utf8DecodeJs utf8DecodeWasm TextDecoder default, NodeJS/v12.6.0, v8/7.5
  25. 53.