皆様はじめまして!普段はマネジメントや別事業をしつつ、インフラからアプリまで何でもこなす現場のIT何でも屋です。
ある日、Qiitaのトレンドに流れてきた kanryu さんの うっかり世界最強のWasmコンパイラを作ってしまった件 などの記事を拝見し、「自社のファイル暗号化・大量送信システムに利用できるのでは?」と考えたのがすべての始まりでした。
実際に試してみたところ、予期せぬ凄まじい軽量化(99.8%削減)と圧倒的な高速化に感動し、実務への採用を即決。さらにその過程で、初めてGitHubでバグ修正のパッチ(Issue)を送るという貴重な体験もしました。
実はQiitaへの投稿自体も今回が初めてとなるのですが、この技術選定からコントリビューションに至る一部始終を備忘録がてらシェアします!
背景:既存技術の悩み(JSの限界 vs Goの重さ、そしてTinyGoの検討)
社内のファイル共有や暗号化・データ送信システムでは、アップロードやダウンロードの過程で「暗号化・ハッシュ」「ファイルアーカイブ生成」「QRコード※処理」「画像メタデータ除去」をクライアントサイド(ブラウザ)で行っています。
1. もともとのJS実装の限界
-
大容量でメモリパンク(OOM):
数GB〜100GB規模のデータをJSヒープ上で扱おうとすると、ブラウザのメモリ制限(1〜2GB)を超過し、タブが即座にクラッシュします。 -
処理落ち・フリーズ:
JSでバイナリ・ストリーム処理を自前で行うと、GCプレッシャーやJITの型変換オーバーヘッドでCPUが100%になり、UIが凍結します。
2. 標準Go言語によるWASMの壁
-
バイナリが約9MBと重すぎる:
Goの標準ライブラリを使ったWASM化も検証しましたが、4モジュール合計で 約9,100 KB (9.1MB) と非常に巨大になり、モバイル(PWA)の初回ロードオーバーヘッドとして許容できませんでした。
3. TinyGo という定番の選択肢とその限界
Go言語のWASM軽量化と聞いて、エンジニアなら誰もが真っ先に思い浮かべるのが TinyGo です。実際に検証すると標準Goより遥かに小さくなりますが、独自のLLVMバックエンドによる言語仕様の制約や、大容量ストリーミングにおける簡易GCの挙動・カクつきの懸念が残りました。
そんな試行錯誤の最中で出会ったのが、数KBレベルの圧倒的な軽さと快適な開発体験を両立した新言語 「Hike」 でした。
採用の決断:WASMだからこその「実質ゼロリスク」
新興のコンパイラ・言語を実務システムに導入するにあたり、「WASM環境やブラウザ互換性のリスクはないのか?」という懸念がありましたが、次のように判断して実装に踏み切りました。
-
多重防御によるフォールバック:
万が一Wasm未対応ブラウザや予期せぬ不具合に遭遇した場合でも、wasm_loader.jsを介して自動的に従来の第1世代JS(WebCrypto / 各種ライブラリ等)へ安全に切り替わる仕組みを構築。これによりリスクは実質的にほぼゼロに。
Hikeの使い心地と開発体験(VSCodeデバッグ & AI連携)
-
Goに近い書き味:
構文がGoライクなため、既存コードからの移植が非常にスムーズでした。 -
VSCodeでのソースレベルデバッグ:
hikecに-gフラグを渡してLLVM DWARFメタデータを出力させることで、VS Code上からGDBやLLDBを使ったソースレベルのブレークポイント、シングルステップ実行、変数検査が可能に。低レイヤのバイナリ処理で不具合が出た際も、原因特定が劇的にスムーズでした。 -
AI(Gemini)との相性の良さ:
コードの壁打ちやリファクタリング、最適化をAIのサポートを受けながら進められたため、開発スピードが圧倒的に加速しました。
結果:バイナリサイズ99.8%以上の圧縮と圧倒的スピードアップ
導入した4つのWebAssemblyモジュールについて、「① 元のJS単体」「② 標準Go WASM」「③ Hike WASM(現在)」の3世代を比較します。
全モジュール総括比較
| モジュール名 | ① 元の JS 単体 | ② 標準 Go WASM | ③ Hike WASM (現在) | 主な役割 |
|---|---|---|---|---|
crypto_pipeline |
5.1 KB (JS) / WebCrypto | ~2,500 KB | 5.8 KB (🔻99.7%) | 100GBストリーミングSHA-256、Base32、XOR暗号 |
zip |
97.6 KB (jszip.min.js) |
~2,300 KB | 4.2 KB (🔻99.8%) | 複数ファイル高速ZIP生成、CRC-32計算 |
qr |
56.7 KB (qrcode.js) |
~2,200 KB | 3.1 KB (🔻99.8%) | 端末連携QR生成、カメラ映像からの二値化スキャン |
image_opt |
Canvas再描画 / 手動スライス | ~2,100 KB | 2.3 KB (🔻99.9%) | JPEG EXIF / PNG メタデータ除去、解像度判定 |
| 合計バイナリ | 約 160 KB | ~9,100 KB (9.1MB) | 15.4 KB (JSグルー完全不要) | 全モジュール計 |
💡 バイナリ削減率について: 標準 Go WASM(約9,100 KB)から Hike WASM(15.4 KB)への移行により、トータルでのバイナリサイズ削減率は 約 99.83% を達成しています。
パフォーマンス・実行特性の比較
| 評価項目 | ① 元の JS 単体 | ② 標準 Go WASM | ③ Hike WASM (現在) |
|---|---|---|---|
| 総ダウンロード時間 (4G) | 約 130ms | 約 7,300ms (7.3秒) | 約 12ms (即座に完了) |
| 総ダウンロード時間 (3G) | 約 1.3秒 | 約 72.8秒 (1分以上) | 約 120ms (0.12秒) |
| コールドスタート (起動) | 10ms 〜 30ms | 150ms 〜 450ms | 1ms 〜 5ms (即時ストリーミング) |
| メモリ消費 (フットプリント) | 不定 (GC任せ、数十MB) | 25MB 〜 60MB | 64KB 〜 1MB (固定リニアメモリ) |
| JSグルーコード依存 | なし | 必須 (wasm_exec.js 18KB) |
完全不要 (0KB) |
| SHA-256 ストリーミング | 15〜25 MB/s | 30〜45 MB/s | 55〜85 MB/s (最高速) |
| ZIP 生成スループット | 30〜40 MB/s | 60〜80 MB/s | 120〜150 MB/s |
| カメラQR解析 (640x480) | 15〜35ms (30fps割れ) | 10〜20ms | 3〜5ms (60fps超安定) |
| 100GB 連続処理時の安定性 | OOMクラッシュ多発 | ヒープ肥大化リスク | 定数メモリで1秒の遅延もなく完走 |
結論:なぜ Hike WASM なのか
-
JS 単体:
メモリ上限(OOM)とGCの壁があり、大容量ストリーミング処理に耐えられない。 -
標準 Go WASM:
機能・安全性は完璧だが、4モジュールで 9.1 MB という致命的なロードオーバーヘッドがある。 -
Hike WASM:
C-ABIとLLVM最適化により 4モジュール計 15.4 KB(JSグルーコード完全不要) を達成。「ミリ秒起動」「ゼロGC・ゼロコピーによる 100GB 定数メモリ完走」のすべてを完璧に両立。
GitHub初コントリビューション(Issue&バグ修正の話)
実務での検証を進める中で、右シフト(lshr)に関するLLVMコード生成の挙動で一つ不具合に遭遇しました。
これまでROM専だった筆者ですが、思い切って初めてGitHubのIssueとしてバグ報告と修正パッチを投稿。すると、作者の kanryu さんに非常に迅速にマージしていただけただけでなく、おまけで多値構文モードまで追加実装していただくという、言葉にならないほどの嬉しいサプライズを経験しました。オープンソース開発の温かさとスピード感を肌で実感する最高の体験でした!
今後の展望:他システムへの応用
今回の導入で得られた圧倒的な軽量化の手応えを受け、現在は社内の別システムにおける重いフロントエンド処理へのHike適用も現在進行中です。
完全な社内クローズドシステムを扱う性質上、詳細なコードを露出できないのがもどかしいところですが、また社外秘にならない範囲でシェアできる知見やOSSとして切り出せるネタが生まれたら、続編としてQiitaに書きに来きたいと思います。
謝辞
今回はじめてのQiita投稿、そして初めてのOSSコントリビューションとなりましたが、このような素晴らしい新言語「Hike」を開発・公開してくださった kanryu さん、そしてコミュニティの皆様に心より感謝申し上げます。
また、本記事の構成案の壁打ちや技術検証のサポートには生成AI(Gemini)を活用しました。AIの強力なアシストを受けながら、スムーズに検証から執筆までをやり切ることができました。
軽量なWasm環境やエッジな開発、AIを活用した開発に興味がある方は、ぜひ Hike を触ってみてください!
※「QRコード」は株式会社デンソーウェーブの登録商標です。