Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up for free
How to Boost Your Code with WebAssembly
FUJI Goro
November 30, 2019
Technology
2
2.1k
How to Boost Your Code with WebAssembly
https://jsconf.jp/2019/talk/fuji-goro
FUJI Goro
November 30, 2019
Tweet
Share
More Decks by FUJI Goro
See All by FUJI Goro
AssemblyScriptでライブラリコードの高速化をしてみる
gfx
5
2.2k
実践TypeScriptトークバトル
gfx
1
730
歴史的経緯の説明 as code
gfx
7
2.2k
Elasticsearchによる 全文検索の実装 in Rails
gfx
5
8.2k
すばらしきGraphQLのSEKAIへようこそ
gfx
20
8.2k
マルチテナント・ウェブアプリケーションの実践
gfx
14
8.8k
How to choose the ORM on Android
gfx
1
3.7k
How Do We Get Along With Static Types
gfx
5
3k
"OSSにコントリビュート" なんてしてる場合じゃない!
gfx
21
13k
Other Decks in Technology
See All in Technology
Reactivity Transform
kazupon
1
430
やってみたLT会 Fleet Managerのススメ
yukiiiiikuma
PRO
0
420
Microsoft Data Analytics trends : ”Lakehouse” , ”Data Mesh"
ryomaru0825
2
170
金融領域のマルチプロダクトを効率よく開発・運用するためのシステム基盤と組織設計について / 2022-07-28-multi-product-platform
stajima
0
150
DevRel組織についての考察
taijihagino
PRO
0
160
セキュアなTerraformの使い方 ~ 機密情報をコードに含めず環境構築するにはどうしたらいいの?
harukasakihara
9
1.6k
Goで実装するブランドネットワークとの接続ポイント
pongzu
2
300
ECS Exec を使った ECS の トラブルシューティング
dohara
0
170
サイバー攻撃を想定したクラウドネイティブセキュリティガイドラインとCNAPP及びSecurity Observabilityの未来
syoshie
2
1.5k
20220731 如何跟隨開源技術保持你的職涯發展
pichuang
0
120
Autonomous Database Cloud 技術詳細 / adb-s_technical_detail_jp
oracle4engineer
PRO
10
19k
プロダクトマネージャーの役割と育成、評価
middleokada
18
12k
Featured
See All Featured
JazzCon 2018 Closing Keynote - Leadership for the Reluctant Leader
reverentgeek
173
8.6k
Art, The Web, and Tiny UX
lynnandtonic
280
18k
GraphQLとの向き合い方2022年版
quramy
16
8.5k
Designing Dashboards & Data Visualisations in Web Apps
destraynor
223
49k
Designing with Data
zakiwarfel
91
4k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
119
28k
GraphQLの誤解/rethinking-graphql
sonatard
31
6.8k
Music & Morning Musume
bryan
35
4.3k
Stop Working from a Prison Cell
hatefulcrawdad
262
17k
Three Pipe Problems
jasonvnalue
89
8.7k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
39
13k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
349
27k
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__ ·Ͱʂ