LuaJITはPythonより優れたLLMランタイムだ

Dev.to / 2026/5/8

💬 オピニオンDeveloper Stack & InfrastructureTools & Practical UsageModels & Research

要点

  • 著者は「クリックベイト」だと前置きしつつ、低いフットプリントでのLLM推論パイプラインにはLuaJITがPythonより適していると主張しています。
  • ion7-coreというLuaJIT FFIのバインディングを開発し、llama.cppのAPIをLuaから1:1で呼び出せるようにして、JSON/HTTPなどの層を避けつつ1トークン処理のホットパスのオーバーヘッドを減らすことを狙いました。
  • ベンチマークの前提として、ion7はモデル本体の実行を速くするのではなく同一の下位ライブラリを呼ぶため、差が出るのは呼び出しコスト、アイドル時のメモリ負荷、割り当ての挙動など「それ以外の部分」だと説明しています。
  • CPUバックエンドでMinistral 8B(Q8)を用いたベンチマークでは、生成スループットはネイティブのllama.cppと同程度である一方、ピークRSSはlla­ma-cpp-pythonよりion7のほうが大幅に低い結果が示されています。

タイトルは釣りです。許してね。クリックしたってことは、読む時間です :D

このブログ記事をどう始めるか正直よく分からないので、とりあえず思いつくままに書きます。

メモリは32 GB、Ryzen 9 9950X、そしてRTX 3060です。

CPUオフロードなしだとQ8で14Bを動かすには足りませんし、モデルが読み込まれる前にPythonにメモリの半分を食われるような余裕ももちろんありません。私はほぼ14年、Luaを書いています。そして、システムに大きな痕跡を残さないツールが好きです。つまり、パッケージ数が少ない、RAMが少ない、騒音が少ない。だいたいPythonエコシステムとは真逆です。

そこで、こうした制約に合う推論パイプラインを探しつつ、いつもの面々を順に見ていきました。スタック最下層のllama.cpp、そのすぐ上のllama-cpp-python、そして聖杯……llama-cpp-lua……だめでした。真面目なLuaラッパーも完成したパイプラインもなく、GitHubの片隅で古い放置POCが腐っているだけ。

変だな……。LuaJITにFFIを組み合わせたものなら、今日Cの高速なバインディングを書くならまさにそれを選ぶはずです。オーバーヘッドはほぼゼロ、構文はきれい、攻めたJIT。

よし。私はつらい構成には慣れています。AzerothCore、Eluna、WoWのmod制作……もう何年も前からずっと。

とはいえ、1日18時間かけて最初からシステムを作るのは気が進みませんでした。最近は生の実装よりもアーキテクチャのほうに興味があるので。幸運なことに、面倒で退屈な部分を速くするための美しいツールが私たちにはあります。AIです。そこで相棒のClaudeと、さまざまなプロンプトを大量に使って、土台を作りました。

ion7-core

その結果がion7-coreです。LuaJITのFFIバインディングで、llama.cppをLuaに1:1で公開します。210以上のドキュメント付き関数、トークンごとのホットパスではmallocなし、行列計算にはFFI経由でOpenBLASを差し込み。きれいです。HTTPなし、JSONシリアライズなし、モデルとあなたの間にPythonもなし。

土台ができたので、正直なベンチマークが必要でした。先に釘を刺しておきます。ion7はPythonより「速く」モデルを動かすわけではありません
それは数学的に不可能です。同じライブラリをまったく同じ呼び出しで使っているからです。トランスフォーマのフォワードパスは譲れません。必要なだけ必要、それで終わりです。

ただ、交渉可能なのはそれ以外のすべてです。バインディング操作の呼び出しコスト。アイドル状態のメモリ負荷。割り当てがあちこちに散らばること。そこには実際にギャップがあります。

CPUバックエンド、Ministral 8B Q8。バックエンドごとに新規実行3回(毎回プロセスを再起動):

指標 ion7 lcpp native lcpp-python
gen_tps (tok/s) 15.80 15.85 15.52
ピークRSS (MB) 3,969 3,962 6,953
detokenize (calls/s) 7.58 M 9.71 M 55.97 k
piece (calls/s) 676 M 500 M 2.88 M
is_eog (calls/s) 331 M 1.00 G 4.09 M
page-faults / 1k tok 26.8 k 26.7 k 185.8 k

ベンチ注意点—トマトを投げる前に読んでね
これは自作ベンチなので、いつもの「多少の誤差はあるだろう」くらいの温度感で見てください。方法論は改善の余地があるし、ハードウェアは1セットだけ(Ryzen 9 9950X / RTX 3060 / 32 GB DDR5)で、そして、これらの数値として成立する形なら、どんな批評や再現も歓迎します。

バックエンドごとに3回、毎回新規プロセス、同じGGUFファイル、同じプロンプト、適用できる場合は同じシード。ティア3のマイクロベンチ(detokenizepieceis_eog)は、束縛層(binding layer)で測定したJITウォーム済みの1呼び出しコストです。


とはいえ、明らかに3つの傾向が見えていて、話す価値があります。

RAM。 llama-cpp-python はピーク時の使用量が明らかに多く、この実行では ion7 より約3 GB多いです。同じジョブで。ですが、それはモデルのせいではありません。モデルはllama.cppが管理するGGUFバッファに存在しており、3つのケースで同一です。ではこの3 GBは何か。CPythonインタプリタ、そのインポート、参照カウント(ref-counting)、そのエコシステムです。Raspberry Piで、ゲームmodで、デスクトップのバイナリとして出荷したい状況では問題になります。

バインディング層の呼び出しコスト。 デトークナイズはLuaJITで1秒あたり数百万回、Pythonでは数万回程度。桁違いです(人為的なミスが混ざっている可能性もあります)。とはいえ魔法ではありません。PythonはトークンごとにPython ↔ Cの文字列往復を支払うのに加えて、GIL、そして参照カウントを毎回行っています。LuaJITはFFI経由でCバッファを直接操作し、JITは可能な範囲でインライン化します。

ページフォールト。 Pythonは1kトークンあたりで明らかに多く発生させています。およそこの実行では7倍です。割り当てて解放しているだけで意味はなく、GPUはただ列車が通り過ぎるのを眺めている状態です。

そして、ion7がネイティブのlcppを上回らないのは、まさに期待通りです。同じライブラリで、ただPythonラッパーより薄いバインディングを使っているだけ。ここで奇跡は起こしていません。きちんと呼べているCをやっているだけです。

ちなみに、ion7とネイティブのlcppを同じ比率で横並びにしたVulkanベンチもありますが、この投稿では意図的に出しません。私が使ったllama-cpp-pythonのビルドはVulkan対応でコンパイルされていなかったので、GPUバックエンド同士の比較をするのは不公平です。それに、壊れたグラフはあまり好きじゃありません。

The stack on top

Luaからllama.cppにアクセスできるのは良いことです。でも、それだけだと実用的なものはほとんど何もできません。なので、その上に3つのモジュールが乗っています。

フランス語のことわざにもある通り:La technique c'est sympathique, mais tout seul .. ça pue de la gueule.

ion7-llmは推論ループです。停止条件、サンプリング、スライディングコンテキスト処理、KVキャッシュ。意図的に最小限にしています。チェーンも、エージェントも、フレームワークがあなたの代わりに考えるようなものもありません。仕事をきっちりやるきれいなループで、あなたがやるべきことに道を譲ります。

ion7-grammarは出力をトークン単位で制約します。llama.cpp側でGBNFを扱い、Lua側ではきれいに公開します。100%有効なJSON、きちんと整形されたSQL、あるいは構文として尊重される任意の形式言語が欲しいなら、文法を記述するだけで、サンプラが機械的に違法なトークンをブロックします。

ion7-ragは保存と取り出しを担当します。密なベクトルストアと、SQLiteの上に載せたFTS5 BM25。融合はRRF(Reciprocal Rank Fusion)で行います。

これら4つのモジュールが揃うことで、完成したパイプラインになります。モデルを読み込み、会話し、制約を与え、メモリも渡す。Luaの中で。Pythonなしで。

And then what?

パイプラインは動いていました。次に、それを何かに組み込みたくなったのです。

私はCyberpunk 2077が大好きです。マジで、このゲームは素晴らしい。そして面白い癖があります。CET(Cyber Engine Tweaks)が、ゲームのプロセス内にLuaランタイムを公開しているのです。そしてion7はLuaにあります。なので。

私はパイプラインをmodとしてパッケージ化し、Ministral 8B Q8をGPU-CPUオフロード付きで組み込みました(正気の範囲です)。さらに、ゲームアーカイブから取り出したJudy Alvarezの文章全編を食わせました。会話、メッセージ、日誌です。

いつもの統合ポイントで数時間の摩擦(CET側のブロッキングループ、スレッディング、非同期呼び出しの取り扱い)。

そして動きます。かなり良いです。レイテンシは問題なく、モデルはキャラクターらしさを保ち、全スタックがゲームプロセスの中で動いています。横に推論サーバーはありません。ローカルエンドポイントへのWebSocketもありません。何もありません。LLMはCyberpunkの中にいるのです。

Who's it for?

結論みたいなものはあまりありません。そういうのをファンでもないです。

マシンへの負荷をできるだけ小さくするLLMパイプラインを探しているなら――ゲームのMOD、エッジへのデプロイ、Raspberry Pi上での推論、デスクトップ用バイナリへの組み込み、エディタプラグイン、そういった用途なら――ion7を試してみてください。

Luaで書かれていて、小さく、動きます。そして、起動するのにコーヒー3杯分くらいかかるようなPython環境は必要ありません。

コードはこちら:ion7-labs.github.io

コードやプロジェクト自体について、ぜひフィードバックを送ってください。
私はそれを受け取っています。

元々フランス語で書かれています。英語版はClaudeで翻訳されました。