$30 off During Our Annual Pro Sale. View Details »

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

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

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ʹίϐʔͤͣʹࢀরͰ͖ΔΑ͏ʹͳΔʢ͸ͣʣ