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

AssemblyScriptでライブラリコードの高速化をしてみる

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.

 AssemblyScriptでライブラリコードの高速化をしてみる

Avatar for FUJI Goro

FUJI Goro

July 24, 2019
Tweet

More Decks by FUJI Goro

Other Decks in Programming

Transcript

  1. ࣗݾ঺հ • FUJI Goro / @__gfx__ • ϑϩϯτΤϯυͱ͔Web APIͱ͔ •

    ࠷ۙ஫໨͍ͯ͠Δٕज़͸GraphQLͱ TypeScriptͱWebAssembly
  2. WebAssembly as a universal executable binary • Φϓτϐ: WebAssemblyʹ͸ϢχόʔαϧͳόΠφϦͱ ͯ͠ظ଴ͯ͠Δ

    • ͭ·ΓϓϥοτϑΥʔϜඇґଘͳόΠτίʔυ • ࢼ͠ʹzopfli (C library) ΛwasmʹϏϧυͯ͠ @gfx/ zopfli ͱͯ͠഑෍ͯ͠Έͨ • native binding൛ΑΓ஗͍͕Πϯείָ͕ͳͷͰͦ͜ ͦ͜࢖ΘΕΔΑ͏ʹͳͬͨ
  3. WASM in intereters • কདྷతʹ͸Ruby / Perl / Python ͱ͍ͬͨΠϯλϓϦλ͕

    WASM࣮ߦΤϯδϯΛ࣋ͭΑ͏ʹͳΔͷͰ͸ͳ͍͔ • ͜ͷํ޲ͰͷࢼΈ͕wasmerͰɺ͢Ͱʹϝδϟʔͳݴޠ༻ͷ binding͕͋Δ • WASM͕ϏϧυࡁΈόΠφϦͷϑΥʔϚοτͱͯ͠ϝ δϟʔʹͳΔͱ͍͍ͳ͋…ͱࢥ͍ͬͯΔ • ߴ଎ͳwasmॲཧܥ͸ඞཁʢwasmer͕Ͳ͏͔͸ະௐࠪʣ
  4. WebAssemblyͷ༻్ • طଘͷ C / C++ / Rust / Go

    ੡඼ΛJSʹίϯύΠϧ ͢Δ • ͦΕͳΓʹ҆ఆ͍ͯͯ͠ී௨ʹศར • ৽͍͠੡඼ͷҰ෦ΛWebAsemblyͰߴ଎Խ͢Δ • ࠓճ͸ͬͪ͜ • ͜ͷ༻్ͷύϑΥʔϚϯε͸ະ஌਺Ͱ͸ʁʁ
  5. ݴޠ: AssemblyScript • બ୒ࢶͱͯ͠͸ C / C++ / Rust /

    Go / AssemblyScript • ࠓճ͸ϥϯλΠϜ͕Ұ൪খͦ͞͏ͳ AssemblyScriptΛબ୒ • ʢଞͷݴޠ΋ࢼ͔͕ͨͬͨؒ͠ʹ߹Θͣʣ
  6. ϦϙδτϦ • https://github.com/msgpack/msgpack-javascript • v1.6.0 ݱࡏͷ࿩ • npm install @msgpack/msgpack

    ͰΠϯείՄೳ • ͨͩ͠WASM൛͸ݱࡏweb൛Ͱ͸࢖ΘΕͳ͍Α͏ʹͳͬ ͍ͯΔ • WASM·ΘΓͷίʔυ͸͍ͣΕফͨ͠ΓRustʹॻ͖׵͑ͨ Γ͢Δ͔΋
  7. ࣮૷ • MessagePack decoderͷҰ෦ɺจࣈྻͷσ ίʔυΛJS / WASM (AS) / ωΠςΟϒίʔυ

    ͦΕͧΕͰ࣮૷ͨ͠ • ΍͍ͬͯΔ͜ͱ͸UTF-8ͷ഑ྻΛUTF-16ͷ഑ ྻʹม׵͢Δ͜ͱ • AssemblyScriptʹҠ২ͯ͠100ߦఔ౓
  8. 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); }
  9. ωΠςΟϒίʔυ൛ͷίʔυ 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); }
  10. 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; }
  11. 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); } }
  12. WASM function΁ͷೖྗ • WASMʹ౉ͤΔ஋͸2छྨ • (1) WASM functionͷݺͼग़͠ͷҾ਺ͱͯ͠ɺ೚ҙݸͷ੔਺·ͨ͸ුಈখ ਺఺਺ •

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

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

    output offset inputPtr: usize, // input offset byteLength: usize, // input length ): usize; // output length
  15. AS (0.6) ͷϋϚΓͲ͜Ζ • ϙΠϯλܕ͕ͳ͘͢΂ͯ usize (uint32_t) ܕ • load<u16>()

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

    msgpack-javascript͸·ͩAS 0.7ʹରԠͰ͖ ͍ͯͳ͍ɺͱ͍͏͔͍ͬͦASΛࣺͯͯRust ʹ͠Α͏ͱࢥ͍ͬͯΔ
  17. ؀ڥ • macOS 10.14 • NodeJS 12.6.0 • v8 7.5

    ʢChrome 75૬౰ʣ • ࠓճ͸NodeJSͷΈͰϕϯνϚʔΫΛͨ͠
  18. νϟʔτͷݟํ • ॎ࣠͸ log10 (ops per sec) • ͦͷ··ͩͱݟͮΒ͍ͷͰର਺ʹͯ͋͠Δ •

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

    1000 10000 utf8DecodeJs utf8DecodeWasm TextDecoder default, NodeJS/v12.6.0, v8/7.5
  20. ϕϯνϚʔΫ݁Ռ (—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
  21. ϕϯνϚʔΫ݁Ռ (default) 0 2 4 6 8 10 100 200

    500 1000 10000 utf8DecodeJs utf8DecodeWasm TextDecoder default, NodeJS/v12.6.0, v8/7.5
  22. ݺͼग़͠ͷΦʔόʔϔου • WASMݺͼग़͠ͷΦʔόʔϔου͸ೖྗΛArrayBuffer΁ίϐʔ ͨ͠Γग़ྗΛArrayBuffer͔Βίϐʔͨ͠Γ͢Δͷ͕΄ͱΜͲ • ݱࡏWASMʹఏҊ͞Ε͍ͯΔ reference-types ͸ɺanyrefͱ͍͏ ܕͰJSͷΦϒδΣΫτΛ௚઀WASMʹ౉ͤΔΑ͏ʹͳΔ࢓༷ •

    anyrefࣗମ͸Կ΋ૢ࡞Ͱ͖ͳ͍஋͕ͩɺ ͨͱ͑͹ࠓճͷ৔߹͸ WASM moduleʹରͯ͠ readU8FromUint8Array(u8array: anyref, offset: usize): u8 Έ͍ͨͳؔ਺Λexport͢Δ͜ͱͰೖྗ ΛArrayBufferʹίϐʔͤͣʹࢀরͰ͖ΔΑ͏ʹͳΔʢ͸ͣʣ