Upgrade to Pro — share decks privately, control downloads, hide ads and more …

How to Boost Your Code with WebAssembly

FUJI Goro
November 30, 2019

How to Boost Your Code with WebAssembly

FUJI Goro

November 30, 2019
Tweet

More Decks by FUJI Goro

Other Decks in Technology

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ΛωΠςΟ ϒίʔυʹίϯύΠϧ͢ΔπʔϧΩοτ • [email protected] (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__ ·Ͱʂ