Share
Embed
アルゴリズムの専門家と挑むフロントエンド実装 − 複雑なロジックを支える設計とパフォーマンス最適化
Akiyoshi Keisuke
November 26, 2025
Transcript
39 slides
-
アルゴリズムの専⾨家と挑む フロントエンド実装 − 複雑なロジックを⽀える設計とパフォーマンス最適化 2025.11.23 TSKaigi Hokuriku 2025 澤浦 司
フロントエンドエンジニア 秋吉 圭輔 アルゴリズムエンジニア -
⾃⼰紹介 3 澤浦 司 @plcherrim • フロントエンドエンジニア (React) • 推し
TypeScript テクは Branded Types • 放置ゲームに⼈⽣を溶かされている 秋吉 圭輔 @udon1206 • アルゴリズムエンジニア • 最近は Rust にお熱 • ゲームは Splatoon が好き -
突然ですが問題です これを解けますか? には解けません 6 ある化学⼯場では、N (≈500,000) ⼯程からなる⽣産計画を⽴てている。 各⼯程はちょうど 1 ⽇かかり、複数の⼯程を同⽇に実⾏してもよい。 これらの⼯程には、次の
2 種類の制約が合計 M (≈500,000) 個 課されている。 • 順序制約:⼯程 A は⼯程 B よりも前の⽇に実⾏する必要がある • 同時制約:⼯程 C と⼯程 D は同じ⽇に実⾏する必要がある すべての制約を満たしながら、全⼯程を完了できるか判定せよ。 可能な場合は、必要となる最⼩の⽇数を求めよ。 可能でない場合は、原因となる制約をすべて列挙せよ。 D A C B … … -
こたえ アルゴリズムの専⾨家 に お願いします 7 1. 制約から有向グラフを構成 2. 強連結成分分解して閉路を検出。あれば完了不可なので対象の制約を列挙 3.
トポロジカルソート + 最⻑路 DP で最⼩⽇数を求める 計算量は O(N+M) で、⼗分⾼速 🤔 いかがでしたか? -
計画最適化サービスの構成 計算‧最適化ロジックを内包したアルゴリズムを VertexAI で動作 9 データ永続化 Frontend Cloud Run Backend
Cloud Run Algorithm Vertex AI Cloud Pub/Sub Database Cloud Firestore Cloud Storage フロントが要求すると バックエンドから起動し 結果を書き込みながら 終了を通知 最適化本体にはそれなりに時間がかかるため、ジョブシステムに分離 この際、インスタンスの起動などにも数⼗秒ほど必要になる -
問題の整理 同じ計算ロジックが 2 箇所に分散してしまった! 12 フロントエンド アルゴリズム 計算ロジック 最適化 計算ロジック
UI フロントエンドの専⾨家なので、正直難しいロジック書きたくない‧‧‧ アルゴリズムの専⾨家なので、正直フロントエンド覚えたくない‧‧‧ -
誰がどこで実装するのか 真⾯⽬に向き合うと、多くのつらみと戦うことになる 13 13 TypeScript を覚えてもらう? ⼆重実装の保守は⾮現実的 • フロントエンドで動かすもので はあるが、内容は複雑
• 内部の挙動や実装⽅針などは理 解が難しい部分も...... • インタフェースだけ決めて、中 ⾝の実装をお願いできるといい 実装責任を分離したい • アルゴリズムエンジニアが使う ⾔語はRust / C# • JS / TS の “お作法” 的な部分に は慣れも必要。習熟を必須とは したくない • パフォーマンスも⼤きな問題。 数⼗万要素のデータを⾼速に扱 えるか? ⾔語/環境差 を抑えたい ⼆重実装は避けたい • そもそもフロントで動かしたい ロジックは、最適化アルゴリズ ムの⼀部分 • これって完全に⼆重実装じゃな いですか?? • ⾔うまでもない話だが、保守コ ストが2倍になるので避けたい アルゴリズムエンジニアに任せたい Where Who How -
解決策は複数ありそう‧‧‧ではあるが アルゴリズムエンジニアが、フロントの罠を⾃然に回避し実装可能な構造が⼤事 14 • テスト駆動にした上で、重い部分を React でメモ化する → コードが純粋関数に閉じづらく、React の気持ちを理解する必要アリ
• フロントエンド実装を避け、バックエンドに API を作成し処理を委譲 → 通信のオーバーヘッドが不可避。楽観的な UI 更新をもっと頑張る必要アリ • アルゴリズムの実装を、WebAssembly として呼び出す → 再実装も不要で、速度もそこそこ出るかも‧‧‧なんだか良さそう! うぃっす ‧‧‧というわけで WASM、お願いします!! -
WebAssembly で⽬指す姿 WebAssembly で、⼆重実装とパフォーマンスの 2 つの課題を解決したい 16 • フロントエンド⽤ロジックと最適化ロジックの⼆重実装をなくしたい •
ユーザー操作に即座に応答できるだけのパフォーマンスがほしい -
WebAssembly とは WebAssembly (WASM) とは、プログラムを記述するためのポータブルな バイナリフォーマット 17 • C/C++ や
Rust などで書いたコードをコンパイルして、 JavaScript/TypeScript など他⾔語と⼀緒に動かせる仕組み → もちろんブラウザ上でも JS/TS から呼び出せる • サンドボックス内で動作し、 OS に直接⼲渉しないので安全 → ブラウザのサンドボックス内でも動作する -
Rust × WASM × TS wasm-bindgen などのライブラリを⽤いると Rust のコードから TS
の型 が ⾃動で⽣成される → WASM のバイナリを使って、TS から型安全に呼び出せる 18 Rust: lib.rs TS: wasm_crate.d.ts -
WASM と TypeScript のパフォーマンス ユーザー操作に即座に応答する「インタラクティブさ」を保つには、 WASM を⾼パフォーマンスで動かす必要がある 19 • ネイティブな
Rust と⽐べると WASM ランタイム上で動作する Rust は遅い • JS/TS だとそのまま扱えるデータも WASM で扱うにはデータコピーが発⽣す ることがある • JS/TS は V8 をはじめとする JS エンジン上で、スクリプト⾔語とは思えない ほど⾼速に動作するがそれを超えていく必要がある -
WASM と TypeScript のパフォーマンス 単純な数値計算では、WASMよりも TypeScript の⽅が速くなることがある 20 配列の sum
を求める関数を 50 回実⾏した時の実⾏時間 L\N 10,000 100,000 1,000,000 TS 1.9 ms 6.8 ms 45.5 ms WASM 3.0 ms 6.7 ms 51.5 ms -
WASM と SIMD(Single Instruction Multiple Data) SIMD は CPU が
1 つの命令で複数のデータを同時に処理する 22 主要なブラウザは WASM の SIMD 対応 している 明⽰的に書かなくても コンパイラが対応して くれることもあるが... -
WASM と TypeScript のパフォーマンス SIMD 対応の演算は WASM が⾼速。ただし可読性とのトレードオフあり 配列の sum
を求める関数を 50 回実⾏した時の実⾏時間 23 L\N 10,000 100,000 1,000,000 TS 1.9 ms 6.8 ms 45.5 ms WASM 3.0 ms 6.7 ms 51.5 ms WASM (SIMD) 1.1 ms 3.4 ms 28.8 ms 配列の sum を求める関数を 50 回実⾏した時の実⾏時間 -
WASM と TypeScript のパフォーマンス 複雑な処理は特に⼯夫もなく WASM の⽅が⾼速になりがち 24 強連結成分分解のアルゴリズム (頂点数
500,000 辺数 500,000) TS 84.7 ms WASM 41.8 ms 再帰処理や 入力に対して計算量が多い もの はWASMが高速な傾向がある -
JS/TS と WASM はメモリの考え⽅がまるで違う JS / TS 26 Object がヒープに分散
WASM 連続した 1 次元のバイト配列 -
TS と WASM のデータのやりとり データのやりとり⾃体はライブラリがやってくれる 27 serde-wasm-bindgen で Object を直接渡す
WASM の⽅で型をつければ、 そのまま渡せる ただデータを渡すコストが 無視できない... -
TS と WASM のデータのやりとり 28 化学品⼯場のデータ例 80 施設 × 20
品⽬ × 300⽇ = 480,000 のネストされた Object 構造のデータ serde-wasm-bindgen で やりとりすると 50 ms ぐらい かかる... -
TS と WASM のデータのやりとり WASM 側は線型メモリなので、それにあったデータ構造は渡しやすい 29 build した際に出てくる js
のコードをみると malloc でメモリを確保し、 set で⼀括でコピーする 挙動は JS エンジンに依存する が、多くの場合⾮常に⾼速 -
TS と WASM のデータのやりとり Object の構造を配列にしてから WASM とやりとりする 30 TS
側で配列にして WASM と データをやりとりする 今回のケースだと変換込みで 7 ms 程度でやりとりできる -
TS と WASM のデータのやりとり 他に紹介できていない⽅法として 31 • WASM 側でメモリを確保して、フロントエンド側でそれを参照して扱う •
境界を跨ぐような回数を減らすために、まとめて WASM にデータを送る • 最適化処理で⽂字列として扱う必要のないデータは数値として扱う などなど... -
WebAssembly のまとめ 32 • WebAssembly を使うと⼆重実装を避けることができる • 速度⾯では WASM が⾼速になる傾向があるが、可読性を失うこともある
• データ転送で何がいいかは状況次第。可読性や速度との塩梅を取るのが⼤事 -
WASM によって得られたもの 適切に責務を分割、各⾃が専⾨家として開発に集中可能に 34 フロントエンド アルゴリズム 計算ロジック (WASM) 最適化 計算ロジック
UI 神!! でも、まだ考えなきゃいけないことが‧‧‧ 実⽤可能な速度を維持しつつ、⼆重実装も回避! -
フロントエンドでしか利⽤しないなら、フロントエンドエンジニアが実装する 35 最適化時には考慮せず、UI 上の表⽰にのみ利⽤するロジックも複数存在 → それはフロントエンドエンジニアが実装する、という整理に • アルゴリズムに無いなら、そもそも⼆重実装ではない → WASM
側で実装する利点は速度くらい • そのようなロジックは、多くの場合で UI と密結合なビジネスロジック → どうしても煩雑な実装になり、React のメカニズムと絡めた⽅が⾃然 というわけで、気合いでフロント側に実装を寄せている箇所もある 最適化で利⽤しないロジックの存在 重実装で得た、当たり前の知⾒を共有します -
36 難しいロジックを書くための、あたりまえの3箇条 1 単体テストをちゃんと書く 3 計算量やメモ化戦略など、実⾏速度を注視するプロンプトを⽤意する 2 引数を減らした、副作⽤を持たない純粋関数に、意思を持って分割する ロジックの複雑さのせいで、⽬視ではミスに気付けないことが特に多いです ⾔うまでもないことではありますが書きましょう
今の AI Agent は、最悪計算量の⾒積もりは割かし正確です。頼りましょう 特に、想定されるデータ量の情報を与えておくのは⼤事です 思いもよらないパーツを使い回すことがあります ソフトウェア開発の基本ですが、ちゃんと分割しておくと後々メモ化が楽です