Slide 1

Slide 1 text

How to Boost Your Code with WebAssembly JSConf JP, 2019/11/30 by FUJI Goro (@__gfx__)

Slide 2

Slide 2 text

ࣗݾ঺հ • FUJI Goro / @__gfx__ • ͜͜਺೥͸΢ΣϒΤϯδχΞͱͯ͠ Rails/React/TypeScript/ GraphQL ͋ͨΓͷٕज़Λ࢖ͬͯαʔϏε։ൃΛͯ͠·ͨ͠ • ͜ͷτʔΫ͸झຯͰ @msgpack/msgpack Λ։ൃͨ͠ͱ͖ ͷܦݧʹج͖ͮ·͢ • ࠷ۙCDN providerͷFastlyʹస৬͠·ͨ͠ • స৬͔ͯ͠Β͸ओʹCݴޠͰHTTP serverΛ։ൃͯ͠·͢

Slide 3

Slide 3 text

WebAssemblyͱ͸Կ͔

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

WebAssemblyલ໷ (2012~) • EmscriptenʹΑͬͯC/C++ΛJSʹίϯύΠϧ͢Δ ͜ͱ͸Ͱ͖͍ͯͨ (2012~) • ྫ: https://github.com/gfx/perl.js (2014) • ͨͩ͜͠Ε͸ඇৗʹಈ࡞଎౓͕஗͔ͬͨ • JSͰCPUΛΤϛϡϨʔτ͢ΔͷͰ஗͍ͷ͸ٕज़ తඞવͩͬͨ

Slide 6

Slide 6 text

WebAssembly (2015~) • 2015೥ʹWebAssemblyͷ࢓༷ͷٞ࿦͕։࢝ • 2017೥ʹChrome, Safari, Firefox, Edgeͱ͍ͬͨϞμ ϯϒϥ΢βͰͷ v1.0 (“MVP”) αϙʔτ͕׬ྃ • 2019೥͸WASIͷൃද΍Bytecode Allienceͷൃ଍ͳ Ͳɺϒϥ΢β֎Ͱͷ༻్͕૿͑ͯ͘Δ • ݱࡏ͸SIMDͳͲͷ͞ΒͳΔػೳ(“post MVP”)͕ਐߦத

Slide 7

Slide 7 text

WebAssemblyͷՁ஋

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

WebAssemblyͷ଎౓ͱ҆શੑ • ʮ଎͍ʯͱ͸͍͏΋ͷͷCΑΓ਺ഒ஗͍ • ͜Ε͸҆શੑͷͨΊະఆٛಈ࡞ (UB) Λͳ͍ͯ͘͠Δ͔Β • ͨͱ͑͹ɺCͰ͸഑ྻͷൣғ֎΁ͷΞΫηε͸UB • WasmͰ͸֬อͨ͠ϝϞϦͷൣғ֎΁ͷΞΫηε΋ఆٛࡁΈ • ͜ͷ҆શੑ͕ͦ͜େ͖ͳར఺ • UB͕ͳ͍͔Βͦ͜όάͷ͋ΔWasmͰ΋҆શʹ࣮ߦͰ͖Δ

Slide 10

Slide 10 text

WebAssemblyαϙʔτঢ়گ

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

γΣΞΛ๮ άϥϑͰදࣔ

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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ؚΉ͍͔ͭ͘ͷ࣮૷Λฒྻʹ࣮ߦͯ͠࠷଎ Ͱ੒ޭͨ݁͠ՌΛ࠾༻͢Δͱ͍͏ΞʔΩςΫνϟ

Slide 17

Slide 17 text

WebAssembly as a universal executable binary • ༨ஊ: WebAssembly͸͸ϢχόʔαϧͳόΠφϦͱͯ͠ ੒௕ͭͭ͋͠Δ • ͜ΕΛਐΊ͍ͯΔͷ͕ Bytecode Allience ͱ͍͏ஂମ • ྫ: Lucet (Fastly→Bytecode Allience): WasmΛωΠςΟ ϒίʔυʹίϯύΠϧ͢ΔπʔϧΩοτ • Compute@Edge (Fastly): ͸LucetͰίϯύΠϧͨ͠ WasmΛCDNͷΤοδαʔόʔͰ࣮ߦ͢Δ࢓૊Έ

Slide 18

Slide 18 text

WebAssemblyͷ଎౓ײ

Slide 19

Slide 19 text

ݴޠ͝ͱͷಈ࡞଎౓Πϝʔδ • Ϋϥε1 “θϩΦʔόʔϔου” • C, C++, Rust • Ϋϥε3 “ߴ଎ͳίϯύΠϥ” • Java, C#, Go, Swift • Ϋϥε5 “ߴ଎ͳΠϯλϓϦλ” • JavaScript (V8), Dart • Ϋϥε50 “ී௨ͷΠϯλϓϦλ” • Ruby, Python, Perl, PHP

Slide 20

Slide 20 text

WebAssemblyͷ଎౓ײ • WasmΛV8Ͱಈ͔͢৔߹ʮΫϥε5ͷதͰ͸଎ ͍ํʯͱ͍͏ਫ४ • ͭ·ΓɺJavaScript (V8) ΑΓ͸΄΅࣮֬ʹ଎͍ ͕ɺJava΍Goͱൺ΂Δͱগ͠ྼΔਫ४ • কདྷతʹ͸Ϋϥε3ʹೖΔͱ༧ଌ͞ΕΔ͕ɺͦ ΕͰ΋Ϋϥε1ʹ͸ٴͿ͜ͱ͸ͳ͍ͱࢥΘΕΔ

Slide 21

Slide 21 text

࣮ࡍʹ΍ͬͯΈͨ

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

ݴޠ: AssemblyScript • બ୒ࢶ: C / C++ / Rust / Go / AssemblyScript • ࠓճ͸ϥϯλΠϜ͕Ұ൪খͦ͞͏ͳ AssemblyScriptΛબ୒

Slide 24

Slide 24 text

AssemblyScript • TypeScriptͷαϒηοτΛߏจͱͯ͠ར༻ͨ͠શ͘ ৽͍͠ϓϩάϥϛϯάݴޠ • ॻ͖ຯ͸TypeScriptΑΓ͸Cʹ͍ۙ • ͱ͍͏͔TypeScriptͷൽ͚ͩ࢒ͯ͠த਎Λ͘Γൈ ͍ͯCΛ٧Ί௚ͨ͠ݴޠͱ͍͏΂͖ • ࣮ࡍʹ͸CΑΓ͸ߴػೳɺC++ΑΓ͸ශऑ

Slide 25

Slide 25 text

AssemblyScriptͷ࠷దԽث • AssemblyScriptͷόοΫΤϯυ͸Bynarien • Emscripten͕௕Β͘࢖͖࣮ͬͯͨ੷ͷ͋Δόο ΫΤϯυ • ʢ͍·͸Emscripten͸LLVM backendਪ͠ʣ • খ͞ͳؔ਺ͷΠϯϥΠϯԽͳͲɺجຊతͳ࠷ద Խ͸Bynarien͕΍ͬͯ͘ΕΔ

Slide 26

Slide 26 text

ςʔϚ: MessagePack࣮૷ • ͦ΋ͦ΋WebAssembly͸όΠτྻ (ArrayBuffer) ͔͠ѻ͑ͳ͍ͷͰɺಘҙ෼໺͸ ݶΒΕͦ͏ • खݩͷίʔυͩͱMessagePack͸όΠφϦγ ϦΞϥΠβͳͷͰWasmͰߴ଎Խͷ༨஍͸͋ Γͦ͏ͩͱ౿Μͩ

Slide 27

Slide 27 text

ϦϙδτϦ • https://github.com/msgpack/msgpack- javascript • v1.9.3 ݱࡏͷ࿩ • npm install @msgpack/msgpack ͰΠϯείՄೳ • ͨͩ͠Wasm൛͸ݱࡏweb޲͚ͷϏϧυͰ͸࢖ ΘΕͳ͍Α͏ʹͳ͍ͬͯΔ

Slide 28

Slide 28 text

࣮૷ • MessagePack decoderͷҰ෦ɺจࣈྻͷσίʔυΛ JS / Wasm (AS) ͰͦΕͧΕͰ࣮૷ͨ͠ɻ·ͨV8૊Έ ࠐΈͷಉ͡ڍಈͷωΠςΟϒίʔυؔ਺΋ϕϯν ϚʔΫର৅ͱͨ͠ • ΍͍ͬͯΔ͜ͱ͸UTF-8ͷ഑ྻΛUTF-16ͷ഑ྻʹม ׵͢Δ͜ͱ • AssemblyScriptʹҠ২ͯ͠100ߦఔ౓ͷίʔυ

Slide 29

Slide 29 text

JS൛ͷίʔυʢൈਮʣ export function utf8DecodeJs(bytes: Uint8Array, inputOffset: numbe byteLength: number): string { let offset = inputOffset; const end = offset + byteLength; const units: Array = []; while (offset < end) { const byte1 = bytes[offset++]; if ((byte1 & 0x80) === 0) { // 1 byte units.push(byte1); } // ... } return String.fromCharCode(...units); }

Slide 30

Slide 30 text

ωΠςΟϒίʔυ൛ͷίʔυ 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); }

Slide 31

Slide 31 text

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(); while (inputOffset < inputOffsetEnd) { let byte1: u16 = load(inputOffset++); if ((byte1 & 0x80) === 0) { // 1 byte store(outputOffset, byte1); outputOffset += u16s; } } return (outputOffset - outputPtr) / u16s; }

Slide 32

Slide 32 text

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); } }

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

Wasm function͔Βͷग़ྗ • ੔਺·ͨ͸ුಈখ਺఺਺1͚ͭͩ • ʢWasmతʹ͸೚ҙͷ਺ͷ஋ΛฦͤΔʣ • ࠓճ͸ೖྗ஋ͱͯ͠outputPtr (offset) Λ౉͠ɺ Wasm function͸ग़ྗ஋ΛoutputPtrͷҐஔʹॻ ͖ࠐΈɺॻ͖ࠐΜͩαΠζΛWasm function͔ Βฦ͢ͱ͍͏͜ͱʹͨ͠

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

AssemblyScriptͷϋϚΓͲ͜Ζ • ϙΠϯλܕ͸ͳ͘ɺΦϑηοτͱͯ͠ usize (uint32_t) ܕ͕͋Δ͚ͩ • load() / store() ͳͲ͸ϦτϧΤϯσΟΞϯͱنఆ͞Εͯ ͍Δ͕ɺJSଆͷtyped arrays (Uint16Array) ͸ϗετͷΤϯσΟΞϯ ͳͷͰ࢓༷తʹ͸ΤϯσΟΞϯޓ׵ੑ͕ͳ͍ • ͔͠͠ɺ͖ΐ͏ͼͷϚγϯ͸΄΅͢΂ͯϦτϧΤϯσΟΞϯͳͷ ͰɺԿ΋ߟ͑ͳͯ͘΋ಈ͍ͯ͠·͏ʢASͷ໰୊Ͱ͸ͳ͍͕ʣ • ݟͨ໨͕TypeScriptͳ͜ͱʹؾ͕࣋ͪҾ͖ͣΒΕͯຌϛε͕සൃ͢Δ

Slide 37

Slide 37 text

AS (v0.8 or later)ͷϋϚΓͲ͜Ζ • ͳ͓ݱߦόʔδϣϯ (v0.8) ͸ϦϑΝϨϯεΧ ΢ϯτϥϯλΠϜ͕௥Ճ͞ΕͨͷͰJSͱͷ૬ ޓӡ༻͕ΑΓ೉͘͠ͳͬͨ • msgpack-javascript͸AS 0.8ʹରԠͰ͖͍ͯ ͳ͍……

Slide 38

Slide 38 text

ϕϯνϚʔΫ

Slide 39

Slide 39 text

؀ڥ • macOS 10.14 • NodeJS 12.6.0 • v8 7.5 ʢChrome 75૬౰ʣ • ࠓճ͸NodeJSͷΈͰϕϯνϚʔΫΛͨ͠

Slide 40

Slide 40 text

ϕϯνϚʔΫίʔυ • https://gist.github.com/gfx/ e3e33c80848f734a81dbd030fca16230 • “A”.repeat(N) ʢN͸σʔλαΠζʣͱ͍͏ σʔλΛUTF-8Τϯίʔυͨ͠όΠτྻΛɺ JS൛ / Wasm൛ / ωΠςΟϒίʔυ൛ͷؔ਺Ͱ จࣈྻʹσίʔυ͢Δ

Slide 41

Slide 41 text

νϟʔτͷݟํ • ॎ࣠͸ log10 (ops per sec) • ͦͷ··ͩͱݟͮΒ͍ͷͰର਺ʹͯ͋͠Δ • ஋͕େ͖͍΄Ͳੑೳ͕Α͍ • ԣ࣠͸σʔλαΠζ • ಉ͡σʔλαΠζಉ࢜Ͱൺֱ͢Δ͜ͱ • σʔλαΠζ͕ҟͳΔσʔλͷൺֱ͸ແҙຯ

Slide 42

Slide 42 text

ϕϯνϚʔΫ݁Ռ 0 2 4 6 8 10 100 200 500 1000 10000 utf8DecodeJs utf8DecodeWasm TextDecoder default, NodeJS/v12.6.0, v8/7.5

Slide 43

Slide 43 text

νϟʔτ͔ΒಡΈऔΕΔ͜ͱ • σʔλαΠζ͕খ͍͞ͱ͖͸JS൛͕࠷଎ • Wasm൛ / ωΠςΟϒίʔυ൛͸ॲཧࣗମ͸ߴ଎ ͕ͩݺͼग़͠ͷΦʔόʔϔου͕େ͖͍ͨΊ • σʔλαΠζ͕େ͖͘ͳΔͱ ωΠςΟϒίʔυ൛ >> Wasm൛ > JS൛ • ͦ΋ͦ΋JS൛ͱWasm൛Ͱ΄ͱΜͲࠩ͸ͳ͍

Slide 44

Slide 44 text

JS൛ͱWasm൛ͷ͕ࠩͳ͍ʁʁ • ͔֬ʹσʔλαΠζ͕େ͖͘ͳΔͱWasm൛ͷ΄͏ ͕গ͚ͩ͠ͱ͸͍ܾ͑ఆతʹ଎͍ • ͔ͦ͠͠ͷࠩ͸͍͍ͤͥ਺%͔Β਺े%Ͱɺ։ൃ޻ ਺Λߟ͑ΔͱWasm൛ͷίεύ͸Α͘ͳ͍ • AssemblyScript͕ͭΒ͍ͱ͍͏͜ͱ΋͋Δ͕ɺͦ ΋ͦ΋ݴޠΛ·͙ͨάϧʔίʔυΛϝϯς͢Δͷ ͸ٕज़తͳ೉қ౓͕ߴ͘ɺίεύѱ͍ײ͕ڧ͍

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

nodejs —no-opt Ͱ࠶ܭଌ

Slide 50

Slide 50 text

ϕϯνϚʔΫ݁Ռ (—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

Slide 51

Slide 51 text

ϕϯνϚʔΫ݁Ռ (default) 0 2 4 6 8 10 100 200 500 1000 10000 utf8DecodeJs utf8DecodeWasm TextDecoder default, NodeJS/v12.6.0, v8/7.5

Slide 52

Slide 52 text

2ͭͷ݁Ռ͔ΒಡΈऔΕΔ͜ͱ • ࠷దԽ͕ޮ͔ͳ͍ͱ͖͸͔ͳΓখ͍͞σʔλα ΠζͰ΋ Wasm൛ >> JS൛ • ࠷దԽ͕ޮ͍͍ͯͯ΋ɺσʔλ͕খ͍͞έʔε Λআ͖Wasm൛ͷ΄͏͕JS൛ΑΓগ͠଎͍ • ωΠςΟϒίʔυ͸σʔλαΠζ͕େ͖͍΄Ͳ ଞͷ࣮૷ͱͷ͕ࠩେ͖͍͕ɺ௨ৗ͸ར༻ෆՄ

Slide 53

Slide 53 text

·ͱΊ

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

ݕ౼ͷՁ஋ͷ͋Δঢ়گ • ͻͱͭͷγϯϓϧͳλεΫʹ20msҎ্͔͔Δ͜ͱ • 1.5ഒʹߴ଎Խ͢Δͱͯ͠ɺ1ϑϨʔϜ (1/60 sec) Ͱऩ·Βͳ͍λεΫ͕1ϑϨʔϜʹऩ·ΔΑ͏ʹ ͳΔ͘Β͍ͷظ଴ײ • ७ਮʹ਺஋ܭࢉ΍όΠτྻͷॲཧͰ͋Δ͜ͱ • ΦϒδΣΫτ΍DOMͷབྷΉॲཧ͸·ͩۤख

Slide 56

Slide 56 text

Any questions? • ࣭໰͸ twitter.com/__gfx__ ·Ͱʂ