Pro Yearly is on sale from $80 to $50! »

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. How to Boost Your Code with WebAssembly JSConf JP, 2019/11/30

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

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

  4. WebAssembly • ϒϥ΢βʹ౥ࡌ͢ΔόΠτίʔυͱͦͷॲཧ ܥͷ࢓༷ • ΞηϯϒϦͱ͸͍͏͕ػցޠͱ1:1ରԠͰ͸ͳ ͍ʹ࣮ߦͷͨΊʹVM͕ඞཁɺ·ͨ͸΋͏Ұஈ ػցޠ΁ͷίϯύΠϧ͕ඞཁͰɺJSΑΓ͸଎ ͍͕ػցޠΑΓ஗͍

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

    • ͨͩ͜͠Ε͸ඇৗʹಈ࡞଎౓͕஗͔ͬͨ • JSͰCPUΛΤϛϡϨʔτ͢ΔͷͰ஗͍ͷ͸ٕज़ తඞવͩͬͨ
  6. WebAssembly (2015~) • 2015೥ʹWebAssemblyͷ࢓༷ͷٞ࿦͕։࢝ • 2017೥ʹChrome, Safari, Firefox, Edgeͱ͍ͬͨϞμ ϯϒϥ΢βͰͷ

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

  8. WebAssemblyͷຊ࣭తͳՁ஋ • ϒϥ΢βʹ͓͍ͯ͸JSͱͰ͖Δ͜ͱ͸ಉ͡ • ͨͩʮ଎͍ʯ͜ͱ͕ѹ౗తՁ஋ • ͜ΕʹΑΓɺJSͩͱ஗͗ͯ͢ݱ࣮తͰͳ͍ྨ ͷλεΫ͕ՄೳʹͳΔ • কདྷతʹWasmʹSIMDαϙʔτͳͲ͕ೖΔͱ

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

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

  11. WebAssmbly͸ݱ࣮ʹ࢖͑Δ͔ • IE11Ҏ֎ͷϞμϯϒϥ΢β͸΄΅αϙʔτ • ιϑτ΢ΣΞ·ͨ͸ίϯϙʔωϯτશମΛ Wasmʹͯ͠࢖͏ࣄྫ͸૿͖͍͑ͯͯΔ

  12. γΣΞΛ๮ άϥϑͰදࣔ

  13. ࢖͑Δ؀ڥ͕ ΄΅ׂ γΣΞΛ๮ άϥϑͰදࣔ

  14. ࢖͑Δ؀ڥ͕ ΄΅ׂ γΣΞΛ๮ άϥϑͰදࣔ *&Ͱ͸ ະαϙʔτ

  15. ࢖͑Δ؀ڥ͕ ΄΅ׂ γΣΞΛ๮ άϥϑͰදࣔ *&Ͱ͸ ະαϙʔτ 6$#SPXTFS͸ Ұ෦ΞδΞͰҰఆͷγΣΞ

  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ؚΉ͍͔ͭ͘ͷ࣮૷Λฒྻʹ࣮ߦͯ͠࠷଎ Ͱ੒ޭͨ݁͠ՌΛ࠾༻͢Δͱ͍͏ΞʔΩςΫνϟ
  17. WebAssembly as a universal executable binary • ༨ஊ: WebAssembly͸͸ϢχόʔαϧͳόΠφϦͱͯ͠ ੒௕ͭͭ͋͠Δ

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

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

    “ߴ଎ͳίϯύΠϥ” • Java, C#, Go, Swift • Ϋϥε5 “ߴ଎ͳΠϯλϓϦλ” • JavaScript (V8), Dart • Ϋϥε50 “ී௨ͷΠϯλϓϦλ” • Ruby, Python, Perl, PHP
  20. WebAssemblyͷ଎౓ײ • WasmΛV8Ͱಈ͔͢৔߹ʮΫϥε5ͷதͰ͸଎ ͍ํʯͱ͍͏ਫ४ • ͭ·ΓɺJavaScript (V8) ΑΓ͸΄΅࣮֬ʹ଎͍ ͕ɺJava΍Goͱൺ΂Δͱগ͠ྼΔਫ४ •

    কདྷతʹ͸Ϋϥε3ʹೖΔͱ༧ଌ͞ΕΔ͕ɺͦ ΕͰ΋Ϋϥε1ʹ͸ٴͿ͜ͱ͸ͳ͍ͱࢥΘΕΔ
  21. ࣮ࡍʹ΍ͬͯΈͨ

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

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

    Go / AssemblyScript • ࠓճ͸ϥϯλΠϜ͕Ұ൪খͦ͞͏ͳ AssemblyScriptΛબ୒
  24. AssemblyScript • TypeScriptͷαϒηοτΛߏจͱͯ͠ར༻ͨ͠શ͘ ৽͍͠ϓϩάϥϛϯάݴޠ • ॻ͖ຯ͸TypeScriptΑΓ͸Cʹ͍ۙ • ͱ͍͏͔TypeScriptͷൽ͚ͩ࢒ͯ͠த਎Λ͘Γൈ ͍ͯCΛ٧Ί௚ͨ͠ݴޠͱ͍͏΂͖ •

    ࣮ࡍʹ͸CΑΓ͸ߴػೳɺC++ΑΓ͸ශऑ
  25. AssemblyScriptͷ࠷దԽث • AssemblyScriptͷόοΫΤϯυ͸Bynarien • Emscripten͕௕Β͘࢖͖࣮ͬͯͨ੷ͷ͋Δόο ΫΤϯυ • ʢ͍·͸Emscripten͸LLVM backendਪ͠ʣ •

    খ͞ͳؔ਺ͷΠϯϥΠϯԽͳͲɺجຊతͳ࠷ద Խ͸Bynarien͕΍ͬͯ͘ΕΔ
  26. ςʔϚ: MessagePack࣮૷ • ͦ΋ͦ΋WebAssembly͸όΠτྻ (ArrayBuffer) ͔͠ѻ͑ͳ͍ͷͰɺಘҙ෼໺͸ ݶΒΕͦ͏ • खݩͷίʔυͩͱMessagePack͸όΠφϦγ ϦΞϥΠβͳͷͰWasmͰߴ଎Խͷ༨஍͸͋

    Γͦ͏ͩͱ౿Μͩ
  27. ϦϙδτϦ • https://github.com/msgpack/msgpack- javascript • v1.9.3 ݱࡏͷ࿩ • npm install

    @msgpack/msgpack ͰΠϯείՄೳ • ͨͩ͠Wasm൛͸ݱࡏweb޲͚ͷϏϧυͰ͸࢖ ΘΕͳ͍Α͏ʹͳ͍ͬͯΔ
  28. ࣮૷ • MessagePack decoderͷҰ෦ɺจࣈྻͷσίʔυΛ JS / Wasm (AS) ͰͦΕͧΕͰ࣮૷ͨ͠ɻ·ͨV8૊Έ ࠐΈͷಉ͡ڍಈͷωΠςΟϒίʔυؔ਺΋ϕϯν

    ϚʔΫର৅ͱͨ͠ • ΍͍ͬͯΔ͜ͱ͸UTF-8ͷ഑ྻΛUTF-16ͷ഑ྻʹม ׵͢Δ͜ͱ • AssemblyScriptʹҠ২ͯ͠100ߦఔ౓ͷίʔυ
  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); }
  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); }
  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; }
  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); } }
  33. Wasm function΁ͷೖྗ • Wasmʹ౉ͤΔ஋͸2छྨ • (1) WASM functionͷݺͼग़͠ͷҾ਺ͱͯ͠ɺ೚ҙݸͷ੔਺·ͨ͸ුಈখ ਺఺਺ •

    (2) WASM moduleͷbuffer: ArrayBufferʹ஋Λॻ͖ࠐΉɻArrayBufferʹॻ ͖ࠐΊΔ஋ͳΒͳΜͰ΋OK • Ͳ͜ʹॻ͖ࠐΜ͔ͩͷoffset (੔਺) Λ(1)ͱͯ͠Ҿ਺ͱͯ͠౉͢ • Cݴޠతʹݴ͑͹͜Ε͕ϙΠϯλͱ౳Ձ • ASͰ͸ load<T>(offset) ؔ਺ͰಡΈग़ͤΔ
  34. Wasm function͔Βͷग़ྗ • ੔਺·ͨ͸ුಈখ਺఺਺1͚ͭͩ • ʢWasmతʹ͸೚ҙͷ਺ͷ஋ΛฦͤΔʣ • ࠓճ͸ೖྗ஋ͱͯ͠outputPtr (offset) Λ౉͠ɺ

    Wasm function͸ग़ྗ஋ΛoutputPtrͷҐஔʹॻ ͖ࠐΈɺॻ͖ࠐΜͩαΠζΛWasm function͔ Βฦ͢ͱ͍͏͜ͱʹͨ͠
  35. Wasm functionͷγάωνϟ • ίϝϯτ͖ͭͰ࠶ܝ͢Δͱɺ͜Μͳײ͡ export function utf8DecodeToUint16Array( outputPtr: usize, //

    output offset inputPtr: usize, // input offset byteLength: usize, // input length ): usize; // output length
  36. AssemblyScriptͷϋϚΓͲ͜Ζ • ϙΠϯλܕ͸ͳ͘ɺΦϑηοτͱͯ͠ usize (uint32_t) ܕ͕͋Δ͚ͩ • load<u16>() / store<u16>()

    ͳͲ͸ϦτϧΤϯσΟΞϯͱنఆ͞Εͯ ͍Δ͕ɺJSଆͷtyped arrays (Uint16Array) ͸ϗετͷΤϯσΟΞϯ ͳͷͰ࢓༷తʹ͸ΤϯσΟΞϯޓ׵ੑ͕ͳ͍ • ͔͠͠ɺ͖ΐ͏ͼͷϚγϯ͸΄΅͢΂ͯϦτϧΤϯσΟΞϯͳͷ ͰɺԿ΋ߟ͑ͳͯ͘΋ಈ͍ͯ͠·͏ʢASͷ໰୊Ͱ͸ͳ͍͕ʣ • ݟͨ໨͕TypeScriptͳ͜ͱʹؾ͕࣋ͪҾ͖ͣΒΕͯຌϛε͕සൃ͢Δ
  37. AS (v0.8 or later)ͷϋϚΓͲ͜Ζ • ͳ͓ݱߦόʔδϣϯ (v0.8) ͸ϦϑΝϨϯεΧ ΢ϯτϥϯλΠϜ͕௥Ճ͞ΕͨͷͰJSͱͷ૬ ޓӡ༻͕ΑΓ೉͘͠ͳͬͨ

    • msgpack-javascript͸AS 0.8ʹରԠͰ͖͍ͯ ͳ͍……
  38. ϕϯνϚʔΫ

  39. ؀ڥ • macOS 10.14 • NodeJS 12.6.0 • v8 7.5

    ʢChrome 75૬౰ʣ • ࠓճ͸NodeJSͷΈͰϕϯνϚʔΫΛͨ͠
  40. ϕϯνϚʔΫίʔυ • https://gist.github.com/gfx/ e3e33c80848f734a81dbd030fca16230 • “A”.repeat(N) ʢN͸σʔλαΠζʣͱ͍͏ σʔλΛUTF-8Τϯίʔυͨ͠όΠτྻΛɺ JS൛ /

    Wasm൛ / ωΠςΟϒίʔυ൛ͷؔ਺Ͱ จࣈྻʹσίʔυ͢Δ
  41. νϟʔτͷݟํ • ॎ࣠͸ log10 (ops per sec) • ͦͷ··ͩͱݟͮΒ͍ͷͰର਺ʹͯ͋͠Δ •

    ஋͕େ͖͍΄Ͳੑೳ͕Α͍ • ԣ࣠͸σʔλαΠζ • ಉ͡σʔλαΠζಉ࢜Ͱൺֱ͢Δ͜ͱ • σʔλαΠζ͕ҟͳΔσʔλͷൺֱ͸ແҙຯ
  42. ϕϯνϚʔΫ݁Ռ 0 2 4 6 8 10 100 200 500

    1000 10000 utf8DecodeJs utf8DecodeWasm TextDecoder default, NodeJS/v12.6.0, v8/7.5
  43. νϟʔτ͔ΒಡΈऔΕΔ͜ͱ • σʔλαΠζ͕খ͍͞ͱ͖͸JS൛͕࠷଎ • Wasm൛ / ωΠςΟϒίʔυ൛͸ॲཧࣗମ͸ߴ଎ ͕ͩݺͼग़͠ͷΦʔόʔϔου͕େ͖͍ͨΊ • σʔλαΠζ͕େ͖͘ͳΔͱ

    ωΠςΟϒίʔυ൛ >> Wasm൛ > JS൛ • ͦ΋ͦ΋JS൛ͱWasm൛Ͱ΄ͱΜͲࠩ͸ͳ͍
  44. JS൛ͱWasm൛ͷ͕ࠩͳ͍ʁʁ • ͔֬ʹσʔλαΠζ͕େ͖͘ͳΔͱWasm൛ͷ΄͏ ͕গ͚ͩ͠ͱ͸͍ܾ͑ఆతʹ଎͍ • ͔ͦ͠͠ͷࠩ͸͍͍ͤͥ਺%͔Β਺े%Ͱɺ։ൃ޻ ਺Λߟ͑ΔͱWasm൛ͷίεύ͸Α͘ͳ͍ • AssemblyScript͕ͭΒ͍ͱ͍͏͜ͱ΋͋Δ͕ɺͦ ΋ͦ΋ݴޠΛ·͙ͨάϧʔίʔυΛϝϯς͢Δͷ

    ͸ٕज़తͳ೉қ౓͕ߴ͘ɺίεύѱ͍ײ͕ڧ͍
  45. Wasm൛͸͋·Γҙຯ͕ͳ͍ʁ • ୯७ʹઌͷϕϯνϚʔΫ݁ՌΛղऍ͢Δͱɺ WasmͰίʔυͷҰ෦Λ࠷దԽ͢Δͷ͸ίε ύ͕ѱ͍Α͏ʹΈ͑Δ • ͔͠͠ɺઌͷϕϯνϚʔΫ͸V8ͷ࠷దԽίϯ ύΠϥTurboFan͕े෼ʹ࠷దԽJITΛͨ݁͠Ռ

  46. V8ͷΞʔΩςΫνϟ(2017)

  47. ࠷దԽίϯύΠϥ TurboFan • ϗοτεϙοτΛ࠷దԽ͢ΔJITίϯύΠϥ • ΢ΣϒϖʔδͷॳճಡΈࠐΈ͔࣌Βૢ࡞Մೳʹ ͳΔ·Ͱͷؒ͸ɺTurboFanʹΑΔ࠷దԽ͕·ͩ ޮ͍ͯͳ͍Մೳੑ͕͋Δ • ͭ·ΓTurboFanͷੑೳΛଌΔϕϯνϚʔΫ͕ຊ

    ൪؀ڥʹ͓͚Δ଎౓Λଌ͍ͬͯΔͱ͸ݶΒͳ͍
  48. v8 —no-opt • v8ͷ࠷దԽΛແޮʹ࣮ͯ͠ߦ͢ΔΦϓγϣϯ • nodejsͰ΋͜ͷΦϓγϣϯ͕࢖͑Δ • ͜ͷΦϓγϣϯʹΑΓɺͨͱ͑͹ҰॠͰ࣮ߦ Λऴ͑ΔίϚϯυϥΠϯπʔϧ΍΢Σϒϖʔ δͷॳظԽίʔυͷ࣮ߦͳͲͷ؀ڥΛΤϛϡ

    ϨʔτͰ͖Δ
  49. nodejs —no-opt Ͱ࠶ܭଌ

  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
  51. ϕϯνϚʔΫ݁Ռ (default) 0 2 4 6 8 10 100 200

    500 1000 10000 utf8DecodeJs utf8DecodeWasm TextDecoder default, NodeJS/v12.6.0, v8/7.5
  52. 2ͭͷ݁Ռ͔ΒಡΈऔΕΔ͜ͱ • ࠷దԽ͕ޮ͔ͳ͍ͱ͖͸͔ͳΓখ͍͞σʔλα ΠζͰ΋ Wasm൛ >> JS൛ • ࠷దԽ͕ޮ͍͍ͯͯ΋ɺσʔλ͕খ͍͞έʔε Λআ͖Wasm൛ͷ΄͏͕JS൛ΑΓগ͠଎͍

    • ωΠςΟϒίʔυ͸σʔλαΠζ͕େ͖͍΄Ͳ ଞͷ࣮૷ͱͷ͕ࠩେ͖͍͕ɺ௨ৗ͸ར༻ෆՄ
  53. ·ͱΊ

  54. ঢ়گ࣍ୈͰ͸ݕ౼ͷՁ஋͋Γ • ʮWebAssemblyͰίʔυͷҰ෦Λߴ଎Խ͢ Δʯ͸Մೳ͕ͩɺίεύ͕ѱ͍ • Wasm͸JSͷ࠷దԽίϯύΠϥʹґଘͤͣ҆ ఆͯ͠ߴ଎ͳͷͰɺঢ়گ࣍ୈͰ͸ޮՌత • ྫ͑͹ɺeBayࣄྫ͸WasmΛߴ଎ԽͷͨΊʹ ར༻͢Δͷʹཧ૝తͳϢʔεέʔε

  55. ݕ౼ͷՁ஋ͷ͋Δঢ়گ • ͻͱͭͷγϯϓϧͳλεΫʹ20msҎ্͔͔Δ͜ͱ • 1.5ഒʹߴ଎Խ͢Δͱͯ͠ɺ1ϑϨʔϜ (1/60 sec) Ͱऩ·Βͳ͍λεΫ͕1ϑϨʔϜʹऩ·ΔΑ͏ʹ ͳΔ͘Β͍ͷظ଴ײ •

    ७ਮʹ਺஋ܭࢉ΍όΠτྻͷॲཧͰ͋Δ͜ͱ • ΦϒδΣΫτ΍DOMͷབྷΉॲཧ͸·ͩۤख
  56. Any questions? • ࣭໰͸ twitter.com/__gfx__ ·Ͱʂ