Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
How to Boost Your Code with WebAssembly
Search
FUJI Goro
November 30, 2019
Technology
3.1k
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
How to Boost Your Code with WebAssembly
https://jsconf.jp/2019/talk/fuji-goro
FUJI Goro
November 30, 2019
More Decks by FUJI Goro
See All by FUJI Goro
ステートレスなLLMでステートフルなAI agentを作る - YAPC::Fukuoka 2025
gfx
7
2k
AssemblyScriptでライブラリコードの高速化をしてみる
gfx
5
3.3k
実践TypeScriptトークバトル
gfx
1
1.2k
歴史的経緯の説明 as code
gfx
7
2.9k
Elasticsearchによる 全文検索の実装 in Rails
gfx
6
9.8k
すばらしきGraphQLのSEKAIへようこそ
gfx
20
9.9k
マルチテナント・ウェブアプリケーションの実践
gfx
14
9.8k
How to choose the ORM on Android
gfx
1
4.4k
How Do We Get Along With Static Types
gfx
5
3.5k
Other Decks in Technology
See All in Technology
AWS Security Hub CSPMの成功・失敗体験
cmusudakeisuke
0
270
徹底討論!ECS vs EKS!
daitak
0
470
白金鉱業Meetup_Vol.24_「AIエージェントは分けるほど良い」は本当か? / Is it true that “the more you divide AI agents, the better”?
brainpadpr
1
420
データサイエンスを価値につなげるプロジェクト設計 〜 DS一年目が現場で得た気づき 〜
ysd113
1
280
IaC コードを資産へ:AWS CDK 社内ライブラリと横断展開 / aws-summit-japan-2026
gotok365
5
1.2k
AIネイティブな開発のサプライチェーンリスク対策 〜激動の開発現場でリスクに立ち向かう〜【ZennFes】
cscengineer
PRO
2
140
Agile and AI Redmine Japan 2026
hiranabe
3
310
Oracle AI Database@Azure:サービス概要のご紹介
oracle4engineer
PRO
6
2k
AIチャット検索改善の3週間
kworkdev
PRO
2
140
iOS アプリの「これって不具合ですか?」を AI に調べてもらう
miichan
0
100
[AWS Summit Japan 2026]迷っているあなたへ_小さな一歩が、やがて自分を助けてくれる
sh_fk2
1
170
人材育成分科会.pdf
_awache
4
300
Featured
See All Featured
Building Better People: How to give real-time feedback that sticks.
wjessup
370
20k
The Curious Case for Waylosing
cassininazir
1
390
Noah Learner - AI + Me: how we built a GSC Bulk Export data pipeline
techseoconnect
PRO
0
200
Organizational Design Perspectives: An Ontology of Organizational Design Elements
kimpetersen
PRO
1
750
The Organizational Zoo: Understanding Human Behavior Agility Through Metaphoric Constructive Conversations (based on the works of Arthur Shelley, Ph.D)
kimpetersen
PRO
0
360
Leo the Paperboy
mayatellez
7
1.8k
Done Done
chrislema
186
16k
Reality Check: Gamification 10 Years Later
codingconduct
0
2.2k
What's in a price? How to price your products and services
michaelherold
247
13k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
31
10k
Deep Space Network (abreviated)
tonyrice
0
210
Music & Morning Musume
bryan
47
7.2k
Transcript
How to Boost Your Code with WebAssembly JSConf JP, 2019/11/30
by FUJI Goro (@__gfx__)
ࣗݾհ • FUJI Goro / @__gfx__ • ͜͜ΣϒΤϯδχΞͱͯ͠ Rails/React/TypeScript/ GraphQL
͋ͨΓͷٕज़ΛͬͯαʔϏε։ൃΛͯ͠·ͨ͠ • ͜ͷτʔΫझຯͰ @msgpack/msgpack Λ։ൃͨ͠ͱ͖ ͷܦݧʹج͖ͮ·͢ • ࠷ۙCDN providerͷFastlyʹస৬͠·ͨ͠ • స৬͔ͯ͠ΒओʹCݴޠͰHTTP serverΛ։ൃͯ͠·͢
WebAssemblyͱԿ͔
WebAssembly • ϒϥβʹࡌ͢ΔόΠτίʔυͱͦͷॲཧ ܥͷ༷ • ΞηϯϒϦͱ͍͏͕ػցޠͱ1:1ରԠͰͳ ͍ʹ࣮ߦͷͨΊʹVM͕ඞཁɺ·ͨ͏Ұஈ ػցޠͷίϯύΠϧ͕ඞཁͰɺJSΑΓ ͍͕ػցޠΑΓ͍
WebAssemblyલ (2012~) • EmscriptenʹΑͬͯC/C++ΛJSʹίϯύΠϧ͢Δ ͜ͱͰ͖͍ͯͨ (2012~) • ྫ: https://github.com/gfx/perl.js (2014)
• ͨͩ͜͠Εඇৗʹಈ࡞͕͔ͬͨ • JSͰCPUΛΤϛϡϨʔτ͢ΔͷͰ͍ͷٕज़ తඞવͩͬͨ
WebAssembly (2015~) • 2015ʹWebAssemblyͷ༷ͷ͕ٞ։࢝ • 2017ʹChrome, Safari, Firefox, Edgeͱ͍ͬͨϞμ ϯϒϥβͰͷ
v1.0 (“MVP”) αϙʔτ͕ྃ • 2019WASIͷൃදBytecode Allienceͷൃͳ Ͳɺϒϥβ֎Ͱͷ༻్͕૿͑ͯ͘Δ • ݱࡏSIMDͳͲͷ͞ΒͳΔػೳ(“post MVP”)͕ਐߦத
WebAssemblyͷՁ
WebAssemblyͷຊ࣭తͳՁ • ϒϥβʹ͓͍ͯJSͱͰ͖Δ͜ͱಉ͡ • ͨͩʮ͍ʯ͜ͱ͕ѹతՁ • ͜ΕʹΑΓɺJSͩͱ͗ͯ͢ݱ࣮తͰͳ͍ྨ ͷλεΫ͕ՄೳʹͳΔ • কདྷతʹWasmʹSIMDαϙʔτͳͲ͕ೖΔͱ
Ұͦͷࠩ։͘
WebAssemblyͷͱ҆શੑ • ʮ͍ʯͱ͍͏ͷͷCΑΓഒ͍ • ͜Ε҆શੑͷͨΊະఆٛಈ࡞ (UB) Λͳ͍ͯ͘͠Δ͔Β • ͨͱ͑ɺCͰྻͷൣғ֎ͷΞΫηεUB •
WasmͰ֬อͨ͠ϝϞϦͷൣғ֎ͷΞΫηεఆٛࡁΈ • ͜ͷ҆શੑ͕ͦ͜େ͖ͳར • UB͕ͳ͍͔Βͦ͜όάͷ͋ΔWasmͰ҆શʹ࣮ߦͰ͖Δ
WebAssemblyαϙʔτঢ়گ
WebAssmblyݱ࣮ʹ͑Δ͔ • IE11Ҏ֎ͷϞμϯϒϥβ΄΅αϙʔτ • ιϑτΣΞ·ͨίϯϙʔωϯτશମΛ Wasmʹͯ͠͏ࣄྫ૿͖͍͑ͯͯΔ
γΣΞΛ άϥϑͰදࣔ
͑Δڥ͕ ΄΅ׂ γΣΞΛ άϥϑͰදࣔ
͑Δڥ͕ ΄΅ׂ γΣΞΛ άϥϑͰදࣔ *&Ͱ ະαϙʔτ
͑Δڥ͕ ΄΅ׂ γΣΞΛ άϥϑͰදࣔ *&Ͱ ະαϙʔτ 6$#SPXTFS Ұ෦ΞδΞͰҰఆͷγΣΞ
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ؚΉ͍͔ͭ͘ͷ࣮Λฒྻʹ࣮ߦͯ͠࠷ Ͱޭͨ݁͠ՌΛ࠾༻͢Δͱ͍͏ΞʔΩςΫνϟ
WebAssembly as a universal executable binary • ༨ஊ: WebAssemblyϢχόʔαϧͳόΠφϦͱͯ͠ ͭͭ͋͠Δ
• ͜ΕΛਐΊ͍ͯΔͷ͕ Bytecode Allience ͱ͍͏ஂମ • ྫ: Lucet (Fastly→Bytecode Allience): WasmΛωΠςΟ ϒίʔυʹίϯύΠϧ͢ΔπʔϧΩοτ • Compute@Edge (Fastly): LucetͰίϯύΠϧͨ͠ WasmΛCDNͷΤοδαʔόʔͰ࣮ߦ͢ΔΈ
WebAssemblyͷײ
ݴޠ͝ͱͷಈ࡞Πϝʔδ • Ϋϥε1 “θϩΦʔόʔϔου” • C, C++, Rust • Ϋϥε3
“ߴͳίϯύΠϥ” • Java, C#, Go, Swift • Ϋϥε5 “ߴͳΠϯλϓϦλ” • JavaScript (V8), Dart • Ϋϥε50 “ී௨ͷΠϯλϓϦλ” • Ruby, Python, Perl, PHP
WebAssemblyͷײ • WasmΛV8Ͱಈ͔͢߹ʮΫϥε5ͷதͰ ͍ํʯͱ͍͏ਫ४ • ͭ·ΓɺJavaScript (V8) ΑΓ΄΅࣮֬ʹ͍ ͕ɺJavaGoͱൺΔͱগ͠ྼΔਫ४ •
কདྷతʹΫϥε3ʹೖΔͱ༧ଌ͞ΕΔ͕ɺͦ ΕͰΫϥε1ʹٴͿ͜ͱͳ͍ͱࢥΘΕΔ
࣮ࡍʹͬͯΈͨ
WebAssemblyʹΑΔߴԽ • શମΛ C / C++ / Rust / Go
Ͱ࣮͠Wasmʹ ίϯύΠϧ͢Δ • ͦΕͳΓʹීٴ͠͡Ί͍ͯΔ • ͷҰ෦ΛWasmʹίϯύΠϧͯ͠ߴԽ͢Δ • ͜ͷτʔΫͰͷؔ৺ࣄͬͪ͜ • ͜ͷ༻్ͷՁະ
ݴޠ: AssemblyScript • બࢶ: C / C++ / Rust /
Go / AssemblyScript • ࠓճϥϯλΠϜ͕Ұ൪খͦ͞͏ͳ AssemblyScriptΛબ
AssemblyScript • TypeScriptͷαϒηοτΛߏจͱͯ͠ར༻ͨ͠શ͘ ৽͍͠ϓϩάϥϛϯάݴޠ • ॻ͖ຯTypeScriptΑΓCʹ͍ۙ • ͱ͍͏͔TypeScriptͷൽ͚ͩͯ͠தΛ͘Γൈ ͍ͯCΛ٧Ίͨ͠ݴޠͱ͍͏͖ •
࣮ࡍʹCΑΓߴػೳɺC++ΑΓශऑ
AssemblyScriptͷ࠷దԽث • AssemblyScriptͷόοΫΤϯυBynarien • Emscripten͕Β͖࣮ͬͯͨ͘ͷ͋Δόο ΫΤϯυ • ʢ͍·EmscriptenLLVM backendਪ͠ʣ •
খ͞ͳؔͷΠϯϥΠϯԽͳͲɺجຊతͳ࠷ద ԽBynarien͕ͬͯ͘ΕΔ
ςʔϚ: MessagePack࣮ • ͦͦWebAssemblyόΠτྻ (ArrayBuffer) ͔͠ѻ͑ͳ͍ͷͰɺಘҙ ݶΒΕͦ͏ • खݩͷίʔυͩͱMessagePackόΠφϦγ ϦΞϥΠβͳͷͰWasmͰߴԽͷ༨͋
Γͦ͏ͩͱ౿Μͩ
ϦϙδτϦ • https://github.com/msgpack/msgpack- javascript • v1.9.3 ݱࡏͷ • npm install
@msgpack/msgpack ͰΠϯείՄೳ • ͨͩ͠Wasm൛ݱࡏweb͚ͷϏϧυͰ ΘΕͳ͍Α͏ʹͳ͍ͬͯΔ
࣮ • MessagePack decoderͷҰ෦ɺจࣈྻͷσίʔυΛ JS / Wasm (AS) ͰͦΕͧΕͰ࣮ͨ͠ɻ·ͨV8Έ ࠐΈͷಉ͡ڍಈͷωΠςΟϒίʔυؔϕϯν
ϚʔΫରͱͨ͠ • ͍ͬͯΔ͜ͱUTF-8ͷྻΛUTF-16ͷྻʹม ͢Δ͜ͱ • AssemblyScriptʹҠ২ͯ͠100ߦఔͷίʔυ
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); }
ωΠςΟϒίʔυ൛ͷίʔυ 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); }
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; }
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); } }
Wasm functionͷೖྗ • WasmʹͤΔ2छྨ • (1) WASM functionͷݺͼग़͠ͷҾͱͯ͠ɺҙݸͷ·ͨුಈখ •
(2) WASM moduleͷbuffer: ArrayBufferʹΛॻ͖ࠐΉɻArrayBufferʹॻ ͖ࠐΊΔͳΒͳΜͰOK • Ͳ͜ʹॻ͖ࠐΜ͔ͩͷoffset () Λ(1)ͱͯ͠Ҿͱͯ͢͠ • Cݴޠతʹݴ͑͜Ε͕ϙΠϯλͱՁ • ASͰ load<T>(offset) ؔͰಡΈग़ͤΔ
Wasm function͔Βͷग़ྗ • ·ͨුಈখ1͚ͭͩ • ʢWasmతʹҙͷͷΛฦͤΔʣ • ࠓճೖྗͱͯ͠outputPtr (offset) Λ͠ɺ
Wasm functionग़ྗΛoutputPtrͷҐஔʹॻ ͖ࠐΈɺॻ͖ࠐΜͩαΠζΛWasm function͔ Βฦ͢ͱ͍͏͜ͱʹͨ͠
Wasm functionͷγάωνϟ • ίϝϯτ͖ͭͰ࠶ܝ͢Δͱɺ͜Μͳײ͡ export function utf8DecodeToUint16Array( outputPtr: usize, //
output offset inputPtr: usize, // input offset byteLength: usize, // input length ): usize; // output length
AssemblyScriptͷϋϚΓͲ͜Ζ • ϙΠϯλܕͳ͘ɺΦϑηοτͱͯ͠ usize (uint32_t) ܕ͕͋Δ͚ͩ • load<u16>() / store<u16>()
ͳͲϦτϧΤϯσΟΞϯͱنఆ͞Εͯ ͍Δ͕ɺJSଆͷtyped arrays (Uint16Array) ϗετͷΤϯσΟΞϯ ͳͷͰ༷తʹΤϯσΟΞϯޓੑ͕ͳ͍ • ͔͠͠ɺ͖ΐ͏ͼͷϚγϯ΄΅ͯ͢ϦτϧΤϯσΟΞϯͳͷ ͰɺԿߟ͑ͳͯ͘ಈ͍ͯ͠·͏ʢASͷͰͳ͍͕ʣ • ݟ͕ͨTypeScriptͳ͜ͱʹؾ͕࣋ͪҾ͖ͣΒΕͯຌϛε͕සൃ͢Δ
AS (v0.8 or later)ͷϋϚΓͲ͜Ζ • ͳ͓ݱߦόʔδϣϯ (v0.8) ϦϑΝϨϯεΧ ϯτϥϯλΠϜ͕Ճ͞ΕͨͷͰJSͱͷ૬ ޓӡ༻͕ΑΓ͘͠ͳͬͨ
• msgpack-javascriptAS 0.8ʹରԠͰ͖͍ͯ ͳ͍……
ϕϯνϚʔΫ
ڥ • macOS 10.14 • NodeJS 12.6.0 • v8 7.5
ʢChrome 75૬ʣ • ࠓճNodeJSͷΈͰϕϯνϚʔΫΛͨ͠
ϕϯνϚʔΫίʔυ • https://gist.github.com/gfx/ e3e33c80848f734a81dbd030fca16230 • “A”.repeat(N) ʢNσʔλαΠζʣͱ͍͏ σʔλΛUTF-8Τϯίʔυͨ͠όΠτྻΛɺ JS൛ /
Wasm൛ / ωΠςΟϒίʔυ൛ͷؔͰ จࣈྻʹσίʔυ͢Δ
νϟʔτͷݟํ • ॎ࣠ log10 (ops per sec) • ͦͷ··ͩͱݟͮΒ͍ͷͰରʹͯ͋͠Δ •
͕େ͖͍΄Ͳੑೳ͕Α͍ • ԣ࣠σʔλαΠζ • ಉ͡σʔλαΠζಉ࢜Ͱൺֱ͢Δ͜ͱ • σʔλαΠζ͕ҟͳΔσʔλͷൺֱແҙຯ
ϕϯνϚʔΫ݁Ռ 0 2 4 6 8 10 100 200 500
1000 10000 utf8DecodeJs utf8DecodeWasm TextDecoder default, NodeJS/v12.6.0, v8/7.5
νϟʔτ͔ΒಡΈऔΕΔ͜ͱ • σʔλαΠζ͕খ͍͞ͱ͖JS൛͕࠷ • Wasm൛ / ωΠςΟϒίʔυ൛ॲཧࣗମߴ ͕ͩݺͼग़͠ͷΦʔόʔϔου͕େ͖͍ͨΊ • σʔλαΠζ͕େ͖͘ͳΔͱ
ωΠςΟϒίʔυ൛ >> Wasm൛ > JS൛ • ͦͦJS൛ͱWasm൛Ͱ΄ͱΜͲࠩͳ͍
JS൛ͱWasm൛ͷ͕ࠩͳ͍ʁʁ • ͔֬ʹσʔλαΠζ͕େ͖͘ͳΔͱWasm൛ͷ΄͏ ͕গ͚ͩ͠ͱ͍ܾ͑ఆతʹ͍ • ͔ͦ͠͠ͷ͍͍ࠩͤͥ%͔Βे%Ͱɺ։ൃ Λߟ͑ΔͱWasm൛ͷίεύΑ͘ͳ͍ • AssemblyScript͕ͭΒ͍ͱ͍͏͜ͱ͋Δ͕ɺͦ ͦݴޠΛ·͙ͨάϧʔίʔυΛϝϯς͢Δͷ
ٕज़తͳқ͕ߴ͘ɺίεύѱ͍ײ͕ڧ͍
Wasm൛͋·Γҙຯ͕ͳ͍ʁ • ୯७ʹઌͷϕϯνϚʔΫ݁ՌΛղऍ͢Δͱɺ WasmͰίʔυͷҰ෦Λ࠷దԽ͢Δͷίε ύ͕ѱ͍Α͏ʹΈ͑Δ • ͔͠͠ɺઌͷϕϯνϚʔΫV8ͷ࠷దԽίϯ ύΠϥTurboFan͕ेʹ࠷దԽJITΛͨ݁͠Ռ
V8ͷΞʔΩςΫνϟ(2017)
࠷దԽίϯύΠϥ TurboFan • ϗοτεϙοτΛ࠷దԽ͢ΔJITίϯύΠϥ • ΣϒϖʔδͷॳճಡΈࠐΈ͔࣌Βૢ࡞Մೳʹ ͳΔ·ͰͷؒɺTurboFanʹΑΔ࠷దԽ͕·ͩ ޮ͍ͯͳ͍Մೳੑ͕͋Δ • ͭ·ΓTurboFanͷੑೳΛଌΔϕϯνϚʔΫ͕ຊ
൪ڥʹ͓͚ΔΛଌ͍ͬͯΔͱݶΒͳ͍
v8 —no-opt • v8ͷ࠷దԽΛແޮʹ࣮ͯ͠ߦ͢ΔΦϓγϣϯ • nodejsͰ͜ͷΦϓγϣϯ͕͑Δ • ͜ͷΦϓγϣϯʹΑΓɺͨͱ͑ҰॠͰ࣮ߦ Λऴ͑ΔίϚϯυϥΠϯπʔϧΣϒϖʔ δͷॳظԽίʔυͷ࣮ߦͳͲͷڥΛΤϛϡ
ϨʔτͰ͖Δ
nodejs —no-opt Ͱ࠶ܭଌ
ϕϯνϚʔΫ݁Ռ (—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
ϕϯνϚʔΫ݁Ռ (default) 0 2 4 6 8 10 100 200
500 1000 10000 utf8DecodeJs utf8DecodeWasm TextDecoder default, NodeJS/v12.6.0, v8/7.5
2ͭͷ݁Ռ͔ΒಡΈऔΕΔ͜ͱ • ࠷దԽ͕ޮ͔ͳ͍ͱ͖͔ͳΓখ͍͞σʔλα ΠζͰ Wasm൛ >> JS൛ • ࠷దԽ͕ޮ͍͍ͯͯɺσʔλ͕খ͍͞έʔε Λআ͖Wasm൛ͷ΄͏͕JS൛ΑΓগ͍͠
• ωΠςΟϒίʔυσʔλαΠζ͕େ͖͍΄Ͳ ଞͷ࣮ͱͷ͕ࠩେ͖͍͕ɺ௨ৗར༻ෆՄ
·ͱΊ
ঢ়گ࣍ୈͰݕ౼ͷՁ͋Γ • ʮWebAssemblyͰίʔυͷҰ෦ΛߴԽ͢ ΔʯՄೳ͕ͩɺίεύ͕ѱ͍ • WasmJSͷ࠷దԽίϯύΠϥʹґଘͤͣ҆ ఆͯ͠ߴͳͷͰɺঢ়گ࣍ୈͰޮՌత • ྫ͑ɺeBayࣄྫWasmΛߴԽͷͨΊʹ ར༻͢ΔͷʹཧతͳϢʔεέʔε
ݕ౼ͷՁͷ͋Δঢ়گ • ͻͱͭͷγϯϓϧͳλεΫʹ20msҎ্͔͔Δ͜ͱ • 1.5ഒʹߴԽ͢Δͱͯ͠ɺ1ϑϨʔϜ (1/60 sec) Ͱऩ·Βͳ͍λεΫ͕1ϑϨʔϜʹऩ·ΔΑ͏ʹ ͳΔ͘Β͍ͷظײ •
७ਮʹܭࢉόΠτྻͷॲཧͰ͋Δ͜ͱ • ΦϒδΣΫτDOMͷབྷΉॲཧ·ͩۤख
Any questions? • ࣭ twitter.com/__gfx__ ·Ͱʂ