1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PR: NECイノベーションコミュニティ
技術で挑む価値づくり

Splash とは?「モデルに合わせてエンジンを作る」Apple シリコン向けローカル推論エンジンの仕組み

1
Last updated at Posted at 2026-10-09

グラレコ

Splash の要点をまとめたグラレコ

上のグラレコは、この記事で扱う Splash の要点を1枚にまとめたものです。「モデルに合わせて作る」という設計思想から、DFlash 2 による投機的デコード、専用カーネル、キャッシュ再利用、メモリ計画、エージェント連携、使うときの注意点までを8つのブロックに並べています。右上の数字は、Qwen3.8-27B での Inco 自身の計測値です。細かい話は本文で1つずつ見ていくので、まずは全体の地図として眺めてみてください 🗺️

はじめに

Splash は、Inco AI が開発している Apple シリコン Mac 向けのオープンソース LLM 推論エンジンです。ライセンスは Apache-2.0 で、GitHub の incoai/splash で公開されています。リポジトリの説明文は「A local inference engine for Apple silicon, built around the model.(モデルを中心に作られた、Apple シリコン向けのローカル推論エンジン)」です。

一般的な推論エンジン(llama.cpp、Ollama、MLX 系のサーバーなど)は、どんなモデルでも動かせる汎用性を重視して作られています。Splash はこの前提を逆にしていて、サポートするモデルを絞り、そのモデル専用にカーネル・ドラフトモデル・メモリ計画を作り込むというアプローチを取っています。Inco はデータセンター GPU 向けの推論プラットフォームでも同じ考え方を使っていて、それを Mac に持ち込んだのが Splash です。

Inco の発表ブログでは、Qwen3.8-27B で「計測した中で次に速いエンジンの2倍のデコード速度」、4つのサブエージェントを並列に走らせると「ほぼ4倍」という数字が示されています。ローカルでコーディングエージェントを動かしたい人にとっては、かなり気になる数字です 👀

この記事では、次の3つのソースをもとに、Splash の設計と性能、使い方を整理します。

この記事でわかること 📌

  • Splash の設計思想「エンジンをモデルに合わせて作る」と、何が特化されて何が共有されているか
  • スケジューリング、プレフィックスキャッシュ、メモリ計画の仕組み
  • 投機的デコードの DFlash 2 が、デコード経路にどう組み込まれているか
  • Inco が公開しているベンチマーク(デコード・プリフィル・キャッシュ再利用・並列実行)の読み方
  • インストールから、Claude Code・Codex・OpenCode などのエージェントをつなぐまでの手順
  • 1.1.0 で広がったモデル対応(GGUF・MLX・24GB Mac)と、使うときの注意点

想定読者は、Mac でローカル LLM を動かしている、またはこれから動かしたい開発者の方です。特に、コーディングエージェントのバックエンドをローカルモデルにしたい方に役立つ内容になっています。

Splash の設計思想:エンジンをモデルに合わせて作る 🧵

汎用エンジンと特化エンジンの違い

発表ブログの冒頭は、次の一文から始まります。

ほとんどの推論エンジンは、最大の柔軟性のために、どんなモデルでも動かせるように作られています。私たちのアプローチはこの前提をひっくり返します。最大の効率のために、エンジンをモデルに合わせて作るのです。

汎用エンジンと Splash の違いを図にすると、次のようになります。

この図のポイントは、Splash が「汎用性」を意図的に手放している点です。対応モデルは、現時点で Qwen3.8-27B と Qwen3.6-35B-A3B の2系統だけです。その代わり、それぞれのモデルに対して専用の部品を用意しています。

発表ブログには「設計上、汎用のマルチモデルランタイムも、フォールバック経路も、手動で調整するものもない」と書かれています。ユーザーが触る設定は、同じ Mac で他のアプリも動かすための「上限」(--max-memory や --max-context)くらいです。

何が特化されて、何が共有されているか

発表ブログの Figure 1 では、Splash の構成要素を「モデルが決める事実(FIXED BY THE MODEL)」「モデルごとに作り直す部分(REBUILT PER MODEL)」「共有部分(SHARED)」の3列に分けて説明しています。表にまとめるとこうなります。

モデルが決める事実 → モデルごとに作り直す部分
アーキテクチャとテンソルの形状 その形状にぴったり合わせて融合・サイズ調整した Metal カーネル
このモデルの重みと状態のサイズ このマシン向けに算出したメモリ予算
ターゲットモデルそのもの そのモデル用に学習した DFlash 2 ドラフト

そして右側の共有部分は「1つのサービングループ(One serving loop)」で、バッチ処理、ページ化された KV キャッシュ、ターン間でメモリに常駐する状態、といった要素が描かれています。本文では、共有されるのは「ランタイム、スケジューラ、キャッシュ、API」で、モデルごとに作り込むのは「形状特化の融合カーネル、学習済みドラフト、最適化されたメモリ計画、固定された性能ベースライン」だと説明されています。

この「左の事実から、真ん中の部品を作る」という対応関係が、Splash の性能の源泉です。発表ブログ(v1.0 時点)では、Inco が「複雑さはすべてこちらで引き受けるので、ユーザーは生の MLX や Transformers のチェックポイントではなく、1つの Splash パッケージで性能を享受できる」と説明していました(後で触れる通り、1.1.0 からは MLX 4bit や GGUF のチェックポイントを直接指定しても、対応するカーネルとドラフトが自動で使われます)。

図にすると、次のような関係になります。

オレンジの部分がモデルごとに作る部品、緑が共有部分です。「エンジンの外側(API やスケジューラ)は共通にしておき、内側の計算部品だけをモデルに合わせて差し替える」という構造になっています。

動かすための環境と始め方 🚀

仕組みの話に入る前に、どうやって動かすのかを先に見ておきます。

動作要件

README(1.1.0 時点)の要件は次の通りです。

項目 要件
チップ Apple M3 以降
OS macOS 26.4 以降
パッケージマネージャ Homebrew
メモリ(4bit の例) 36GB 以上のユニファイドメモリ(48GB 以上を推奨)
メモリ(小さい GGUF) 24GB の Mac でも、小さい量子化バリアントなら利用可能
ディスク Qwen3.8-27B の 4bit 例で最大約40GB、Qwen3.6-35B-A3B で約48GB(ダウンロード分と準備済みの重みの合計)

48GB を推奨している理由は、後のメモリ計画のセクションで出てきます。ざっくり言うと、「タスクを走らせながらエディタとブラウザを開いておける余裕」を見込んだ数字です。

インストールと起動

1.1.0 の README にあるクイックスタートは、次の2行です。

brew install incoai/tap/splash
splash serve --model unsloth/Qwen3.8-27B-GGUF:UD-Q4_K_M

初回の起動では、モデル本体とそれに対応するドラフトモデルをダウンロードし、重みを準備してから 127.0.0.1:8000 でサーバーを立ち上げます。2回目以降は準備済みの重みを再利用します。Ready と表示されたら、そのターミナルは開いたままにしておきます。

📝 発表ブログ(v1.0 時点)では、splash serve --model incoai/Qwen3.8-27B-Splash のように、Inco が用意した Splash パッケージを指定する手順になっていました。1.1.0 からは Hugging Face 上の Unsloth GGUF や MLX 4bit のチェックポイントを直接指定できるようになり、README のクイックスタートもそちらに変わっています。Splash パッケージは「レガシー」扱いですが、--model には引き続き指定できます。

エージェントをつなぐ

Splash の便利なところは、主要なコーディングエージェント用のランチャーが同梱されていることです。エージェント本体をインストールしておけば、サーバーを起動したまま、別のターミナルで次のように実行するだけでつながります。

splash opencode    # OpenCode
splash claude      # Claude Code
splash codex       # Codex
splash hermes      # Hermes
splash pi          # Pi(1.1.0 で追加)

発表ブログには「ほかのセットアップや設定は不要」と書かれています。ブラウザで http://127.0.0.1:8000 を開けば、組み込みのチャットページも使えます 💬

API として使う

アプリケーションから呼ぶ場合は、OpenAI 互換と Anthropic 互換の API が使えます。README(1.1.0)によると、対応しているのは次の API です。

  • OpenAI Chat Completions
  • OpenAI Responses
  • OpenAI Completions
  • Anthropic Messages

Chat Completions・Responses・Anthropic Messages では、ストリーミング、ツール呼び出し、JSON Schema 出力、画像入力、インライン PDF が使えます。Completions は OpenAI のレガシーなテキスト補完用で、チャットテンプレート・ツール・画像は適用されません。呼び出し例は次の通りです。

curl http://127.0.0.1:8000/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "unsloth/Qwen3.8-27B-GGUF:UD-Q4_K_M",
    "messages": [{"role": "user", "content": "Explain speculative decoding in one sentence."}]
  }'

推論(thinking)はモデルの既定に従います。リクエストで "reasoning_effort": "none" を指定すると推論をオフにできます。サーバー全体の既定を変えたい場合は、--default-reasoning-effort(または環境変数 SPLASH_DEFAULT_REASONING_EFFORT)を使います。なお、Anthropic Messages API の thinking はプロトコルの意味に従い、省略時はオフになります。

全体の流れを図にすると、次のようになります。

LM Studio Bionic から使う

Inco の発表ブログには、LM Studio と協力して「day zero(初日)」から Splash を LM Studio Bionic で使えるようにした、と書かれています(LM Studio 側のブログは9月18日付です)。LM Studio のブログによると、手順は次の通りです。

  1. LM Studio Bionic 1.1.5 以降をインストールして起動する
  2. Settings > Runtime を開き、Experimental backends の中にある Splash (Metal) の Download をクリックする
  3. Settings > Explore で、incoai/Qwen3.8-27B-Splash または incoai/Qwen3.6-35B-A3B-Splash を検索してダウンロードする
  4. 新しいセッションを開き、ローカルモデルのピッカーからモデルを選ぶ

CLI に慣れていない方や、GUI でモデルを管理したい方には、こちらの方が入りやすいと思います。なお、LM Studio のガイドは記事公開時点(9月18日)の内容なので、モデルは Splash パッケージの指定になっています。

スケジューリングとメモリ 🧠

ここからは、Splash の内部の仕組みを見ていきます。最初はスケジューリングとメモリです。

プリフィルとデコードのバランス

LLM の推論には、大きく2つのフェーズがあります。

  • プリフィル(prefill): プロンプト全体を読み込んで、内部状態(KV キャッシュ)を作るフェーズ。大きなバッチで一気に計算する
  • デコード(decode): 1トークンずつ(投機的デコードなら数トークンずつ)出力を生成するフェーズ

Splash は、届いたリクエストを順次バッチにまとめつつ、プリフィルとデコードのバランスを取るようにスケジューリングします。新しいリクエストはすぐに処理が始まり、すでに動いているリクエストはストリーミングを続けられる、という狙いです。

この配分は --decode-share(既定 0.5)で調整できます。値を大きくすると、長いプロンプトのプリフィル中でも他のリクエストの出力が速く保たれ、その代わりにプリフィルが遅くなります。

プレフィックスでインデックスされた KV キャッシュ

Attention の状態は、プレフィックスでインデックスされたページ単位の KV キャッシュに保存されます。以前のリクエストと同じプレフィックスを持つリクエストは、そのページを再計算せずに再利用します。

コーディングエージェントでは、これがとても効きます。エージェントは「システムプロンプト+これまでの会話履歴+新しい入力」を毎ターン送り直すので、前半の大部分は前のターンと同じだからです。

この図のポイントは、2ターン目以降は「追記された部分だけ」を計算すればよいということです。後のベンチマークで、キャッシュ再利用の効果が一番大きく出ているのはこのためです。

ハイブリッドモデルの GDN 状態もスナップショット

Qwen3.8-27B のようなハイブリッドモデルには、通常の Attention 層に加えて Gated DeltaNet(GDN) という再帰的な層が含まれています。再帰層の状態は、トークンごとの KV をページに並べる Attention とは持ち方が違います(筆者の補足です)。

Splash は、プレフィックスの境界で GDN の状態をスナップショットしておくことで、再帰層もキャッシュ再利用の対象にしています。再帰層を最初からリプレイし直す必要がない、ということです。

起動時に決めるメモリ予算

Splash のメモリ管理で面白いのが、起動時にメモリ予算を決めてしまう点です。特定のモデルについては、次のことが事前にわかっています。

  • 重みのサイズ(固定)
  • ドラフトモデルのサイズ(固定)
  • 各リクエストの状態が、1トークンあたりどれだけメモリを使うか(既知)

そこで Splash は、Metal が推奨するワーキングセットの上限から、重みとドラフトのサイズを引いた分を、KV キャッシュなどの予算として起動時に確保します。

発表ブログ(v1.0 の Splash パッケージ時点)によると、Qwen3.8-27B の場合、KV キャッシュの前に重みで 15 GiB、ドラフトで 1.2 GiB を使います(GGUF など別の量子化を選ぶと、この値は変わります)。発表ブログには「推奨の 48GB はここから来ている。タスクを走らせながら、エディタとブラウザを開いておける余裕を残すため」と書かれています。コンテキストが伸びてメモリが足りなくなってきたら、キャッシュされた状態を回収したり、スケジューリングを調整したりして対応します。

この「モデルのサイズが事前にわかっている」ことが、並列実行のベンチマークで効いてきます。後で詳しく見ますが、汎用エンジンが慎重にリクエストの受け付けを絞るところで、Splash はマシンのメモリをほぼ使い切る計画を安全に立てられるからです。

DFlash 2:投機的デコードが「標準の経路」⚡

投機的デコードのおさらい

投機的デコード(speculative decoding)は、小さなドラフトモデルがトークンのブロックを推測し、大きな本体モデル(ターゲット)がそのブロックを1回のフォワードパスでまとめて検証する手法です。推測が当たれば1回のパスで複数トークンが進み、外れたぶんは捨てるだけなので、出力の分布は変わりません。

Splash では「オプション」ではなく「経路そのもの」

発表ブログで強調されているのは、Splash では投機的デコードがオプションではなく、デコード経路そのものだという点です。対応モデルにはそれぞれ、そのモデル専用に学習した DFlash 2 のドラフトが付いてきます。

DFlash は Inco が2026年1月に公開したドラフト手法で、ドラフトもブロック全体を1パスで並列に予測するのが特徴です(従来のドラフトは、ドラフト自身も1トークンずつ自己回帰的に生成していました)。発表ブログによると、DFlash 2 のドラフトは、モデル自身のマルチトークン予測(MTP)よりも多くのトークンが検証を通過するそうです。

DFlash 2 は何を改良したのか

DFlash 2 の中身は、2026年8月18日付の別記事「DFlash 2: Keep Drafting Parallel」で解説されています。ポイントは2つです。

  1. パス選択器(path selector): DFlash は各位置のトークンを独立に予測するので、個々には妥当でも、並べると噛み合わないことがあります。DFlash 2 は各位置の上位16候補を残し、隣り合う候補のペアをスコア付けして、筋の通った1本の経路を選びます
  2. 2タップの動的畳み込み: ブロックの後ろの位置ほど予測精度が落ちる「suffix decay」に対して、各位置が1つ前の位置の表現を参照する、軽量な畳み込みを入れています

研究設定(Qwen3-4B の5層 DFlash、GSM8K)での数字を少しだけ紹介すると、先頭位置で DFlash の第1候補が正解である割合は 85.4% ですが、上位16候補の中に正解が含まれる割合は 99.5% です。「正解はすでに候補の中にある。選び方を工夫すればよい」というのが、パス選択器の出発点になっています。パス選択器の追加は 2.0M パラメータ、ドラフト–検証サイクルの遅延増は 0.6% で、受理長を 4.27 から 4.61(T=0)に伸ばしています。

📝 これらの数字は DFlash 2 の研究記事での小型モデルの計測値で、Splash 上での計測値ではありません。

Splash がドラフトに合わせて作り込んだ3つのこと

発表ブログでは、エンジンのうち3つの部分が、このドラフトを中心に設計されていると説明されています。

工夫 内容 ねらい
デコードステップを1単位で実行 スキーマ制約がない出力では、ドラフト生成・検証・受理・状態更新をステップごとではなくまとめて投入する ドラフトによる高速化が、ステップごとのオーバーヘッドで消えずに API まで届くようにする
投機をバッチの中で行う 各リクエストが自分のドラフト状態を持ち、受理されたトークンに合わせて進む 投機的デコードが、並列実行やキャッシュ再利用と競合せず協調するようにする
長いコンテキストでもドラフトを小さく保つ ドラフトはスライディングウィンドウ Attention を使い、長いプリフィルでは生成とスナップショットに必要なドラフト状態だけを計算する コンテキストがどれだけ伸びても、ドラフトのメモリを一定以内に抑える

2つ目の「投機をバッチの中で行う」は、地味ですが重要なポイントです。発表ブログは、この設計によって「投機的デコードが並列実行やキャッシュ再利用と競合せず、協調して働く」と説明しています。Splash は各リクエストにドラフト状態を持たせることで、並列実行時にも投機の恩恵を受けられるようにしています。後で見る「並列にすると差が広がる」という結果は、この設計が効いていると考えられます。

形状に特化したカーネルの自動生成 🛠️

プリフィルとデコードは、計算の性質が違うワークロードです。

  • プリフィル: 大きなバッチでまとめて計算する
  • ドラフトの検証: 小さなブロック(数トークン)を計算する

Splash は、それぞれのワークロードに対して、モデルの正確な次元に合わせた 4bit 行列積カーネルと Attention カーネルを別々に用意しています。これを手で書いてチューニングするのは量が多すぎるので、Inco 社内のカーネルエージェント(in-house kernel agents)が生成しているそうです。

発表ブログで例として挙げられているカーネルは次の3つです。

  • 8bit の KV キャッシュを直接読み、クエリヘッドと検証トークンの間で再利用するデコード用カーネル
  • 再帰状態をチップ上に保持したまま計算する GDN プリフィル用カーネル
  • MoE(Mixture of Experts)モデルのルーティングされたエキスパート用の専用カーネル

ユーザーにとって嬉しいのは、図の一番下です。カーネルはすべてプリコンパイル済みで、GPU ファミリー・コア数・ワークロードごとのプリセット付きで配布されます。README にも「Xcode もローカルでのチューニングも不要」と書かれています。インストールして起動するだけで、自分の Mac 向けに調整済みのカーネルが使われる、ということです。

ベンチマーク:Inco の計測を読む 📊

ここからは、発表ブログに掲載されているベンチマークを見ていきます。改めて、すべて Inco 自身の計測値です。

何を、どう測ったか

発表ブログは、コーディングエージェントのループを4つの観点に分けて測っています。

観点 何を表すか
デコード 会話が伸びていく中で、トークンがどれだけ速く返ってくるか
プリフィル リポジトリを初めて読み込むのにどれだけかかるか
キャッシュ再利用 コンテキストを読み込んだ後の、次のターンのコスト
並列実行 複数のリクエストをまとめてどれだけ速く処理できるか、いくつ同時に収まるか

計測条件は次の通りです。

  • マシン: M5 Pro(16コア GPU、48GB)
  • モデル: Qwen3.6-35B-A3B と Qwen3.8-27B(Splash パッケージ)を HTTP 経由で提供、各エンジンの推奨設定
  • プロンプト: NVIDIA の SPEED-Bench から選んだコーディングタスク(最大32Kトークン)、出力上限は1,024トークン
  • 推論(reasoning)は両モデルともオン(27B は medium)。デコードの数値には推論トークンを含む
  • 単一リクエストの数値は中央値

比較対象のエンジンは、oMLX、Lily、uzu、Ollama です。発表ブログによると、oMLX は多くのモデルに対応した汎用サーバーで、バッチ処理とキャッシュ再利用を備えた「一番近い比較対象」です。Lily と uzu は1リクエストずつ処理するエンジンで、uzu の Qwen3.8-27B パッケージにはドラフトがありません。

発表ブログ自身が、次のように注意書きをしています。

各エンジンはそれぞれの推奨設定で動かしているので、これらはエンドツーエンドの比較です。エンジンをモデルに特化させたことの総合的な効果を示すもので、個々の部品の寄与を示すものではありません。

サマリー

README にあるサマリー表がわかりやすいので、まずはこちらを見ます。カッコ内の倍率は、その指標で次に速かったエンジンとの比です(比較相手は指標によって変わります)。

指標 Qwen3.6-35B-A3B Qwen3.8-27B
デコード(短いプロンプト) 210 tok/s(1.7×) 74 tok/s(2.0×)
プリフィル(32Kプロンプト) 2,011 tok/s(1.3×) 363 tok/s(1.2×)
キャッシュ時の最初のトークンまで(32K リプレイ) 123 ms(6.6×) 282 ms(7.3×)
合計デコード(短いプロンプト4並列) 357 tok/s(2.0×) 170 tok/s(3.9×)

ぱっと見て目立つのは、**キャッシュ再利用(6.6〜7.3倍)**と **27B の4並列(3.9倍)**です。逆に、プリフィルの差は 1.2〜1.3 倍と控えめです。それぞれ詳しく見ていきます。

デコード:コンテキストが伸びても差が保たれる

単一リクエストのデコード速度(tok/s)を、プロンプトの長さ別に並べたのが次の表です。

Qwen3.6-35B-A3B

エンジン 短い 8K 16K 32K
Splash 210 156 149 143
oMLX 126 107 98 83
Lily 118 105 94 85
Ollama 75 68 65 57

Qwen3.8-27B

エンジン 短い 8K 16K 32K
Splash 74 55 55 54
oMLX 38 33 29 28
Ollama 24 21 21 19
uzu 19 18 17 15

発表ブログは、この結果を「エージェントにとっては、履歴が深くなったセッションでも、他のどのエンジンで新しく始めたセッションより速く動くということ」とまとめています。実際、Splash の 32K 時点(143 / 54 tok/s)は、他のエンジンの「短いプロンプト」の値を上回っています。

プリフィル:差はあるが、コールドリードは依然として重い

リポジトリを最初に読み込むときのプリフィル速度(入力 tok/s)です。

Qwen3.6-35B-A3B

エンジン 8K 16K 32K
Splash 2,575 2,373 2,011
oMLX 1,495 1,435 1,221
Lily 2,036 1,826 1,572
Ollama 1,194 982 719

Qwen3.8-27B

エンジン 8K 16K 32K
Splash 398 395 363
oMLX 122 119 110
Ollama 265 241 199
uzu 344 337 313

32K のプリフィルでは、次に速かったのが 35B では Lily、27B では uzu なので、サマリー表の倍率(1.3× / 1.2×)はそれらとの比になっています。oMLX と比べると差はもっと大きく、最初のトークンまでの待ち時間は 35B で 29秒 → 17秒、27B で 317秒 → 96秒に縮まっています。

ただし、発表ブログはここで正直に書いています。

大幅な短縮ですが、大きなリポジトリをコールドで読むのは、私たちのエンジンも含めて、テストしたすべてのエンジンにとって依然として高コストです。

27B で 32K のプロンプトを読むと、Splash でも最初のトークンまで約96秒かかります。ただ、エージェントがこのコストを払うのはリポジトリを最初に読むときであって、毎ターンではありません。そこで効いてくるのが、次のキャッシュ再利用です。

キャッシュ再利用:7倍速い

エージェントにとって一番よくあるのは「次のターン」です。同じコンテキストに少し追記して送り直すので、変わっていない部分はすでに読み込まれています。発表ブログによると、Splash の差が一番大きく出るのがここです。

モデル oMLX Splash 倍率
Qwen3.6-35B-A3B 816 ms 123 ms 6.6×
Qwen3.8-27B 2,049 ms 282 ms 7.3×

32K のコンテキストがキャッシュされている状態で、Splash は 35B で 123ms、27B で 282ms で最初のトークンを返しています。コールドリードの17秒・96秒と比べると、桁が2つ違います。

他のエンジンについては、Lily は 35B のリプレイでキャッシュヒットがなく22秒、uzu は 27B で初回と同じ112秒、Ollama は長いプロンプトでキャッシュがヒットしなかったそうです。発表ブログは「キャッシュ再利用がなければ、毎ターンでコールドリードのコストを払い直すことになる」と書いています。

📝 この計測は、プロンプトを完全に同じ内容でリプレイして、キャッシュ経路だけを切り出したものです。実際のエージェントのターンでは、追記された新しいトークンのぶんのプリフィルも加わります。

並列実行:差がさらに広がる

エージェントがサブエージェントを4つ並列に走らせる状況を想定した計測です。短いプロンプト4並列の合計デコード速度はこうなっています。

モデル oMLX Splash 倍率
Qwen3.6-35B-A3B 177 tok/s 357 tok/s 2.0×
Qwen3.8-27B 43 tok/s 170 tok/s 3.9×

単一リクエストのときの差(1.7× / 2.0×)から、4並列では 2.0× / 3.9× に広がっています。32K のプロンプトで4並列にすると、35B では Splash 236 tok/s に対して oMLX 62 tok/s で、3.8倍の差になるそうです。

発表ブログの説明は「4つのサブエージェントが1つの GPU を共有し、バッチ処理とドラフトが全員を動かし続ける」というものです。先ほどの「投機をバッチの中で行う」設計が、ここで効いていると読めます。

同時に収まるリクエスト数

もう1つ興味深いのが、同時にいくつのリクエストを受け付けられるかという計測です。48GB のマシンで、27B に 32K のリクエストを16個同時に送ったところ、次のような結果になったそうです。

  • 汎用のメモリポリシー: 最初のパスで9個を受け付けた
  • Splash: 16個すべてを受け付けて完了した

これは、先ほどのメモリ計画の効果です。モデルのサイズが事前にわかっているので、Splash はマシンのメモリをほぼ使い切る計画を安全に立てられます。ただし発表ブログは「他のエンジンを設定で同じようにできるかはテストしていない」とも明記しています。

llama.cpp との比較(GGUF)🦙

1.1.0 で GGUF を直接読み込めるようになったことで、README には llama.cpp との同じ重みでの比較が追加されました。こちらは発表ブログのベンチマークとは別の計測で、マシンも違います。

  • マシン: M5 Pro(20コア GPU)と M3 Max(40コア GPU)
  • 重み: Unsloth の UD-Q4_K_M(Splash と llama.cpp で同じファイル)
  • llama.cpp: Metal バックエンド、llama-server の既定設定(投機なし)。27B は Unsloth が配布する MTP ドラフトを使った場合も計測
  • サンプリング: greedy

デコード速度(tok/s)は次の通りです。

モデル エンジン M5 Pro M3 Max
Qwen3.8-27B llama.cpp 16 17
Qwen3.8-27B llama.cpp(MTP あり) 27 20
Qwen3.8-27B Splash 74 92
Qwen3.6-35B-A3B llama.cpp 69 66
Qwen3.6-35B-A3B Splash 175 209

README のまとめでは、35B で 2.5〜3.2倍、27B で 4.5〜5.3倍(MTP ありの llama.cpp に対しては 2.7〜4.6倍)です。なお、GGUF のターゲットは発表ブログのベンチマークとは別のカーネルで動いている点に注意してください(performance.md に明記されています)。

出力は llama.cpp とほぼ一致する

速度だけでなく、出力の一致度も計測されています。散文・コード・チャットの 16,384 位置について、両エンジンが1位に挙げたトークンが一致した割合です。

次トークンの一致率 Qwen3.8-27B Qwen3.6-35B-A3B
llama.cpp:CPU と Metal 97.8% 96.5〜96.9%
llama.cpp:1トークンずつとバッチ 99.65〜99.75% 97.95%
Splash と llama.cpp 99.30〜99.45% 97.83〜98.14%

「llama.cpp 自身の CPU 版と GPU 版の一致率」と同じかそれ以上に、Splash は llama.cpp と一致しています。一致しない位置は、llama.cpp の上位2トークンがほぼ同点(中央値で 0.03〜0.10 nats 差)の箇所だそうです。

📝 リリースノートには「これは次トークンの一致率の計測であり、タスクの精度スコアではない」と明記されています。また、この比較は BF16 KV で計測したもので、既定の INT8 KV キャッシュでは M5 Pro で 99.23〜99.25%(27B)、97.92〜97.94%(35B)です。

1.1.0 で広がったモデル対応 🧩

v1.0 では Inco が用意した Splash パッケージだけが対象でしたが、2026年9月26日リリースの 1.1.0 で、モデルの選択肢がかなり広がりました。

形式 例 備考
Unsloth GGUF unsloth/Qwen3.8-27B-GGUF:UD-Q4_K_M 1〜8bit、混合精度の UD 形式も対応。UD-Q8_K_XL と BF16 のターゲットは非対応
MLX 4bit mlx-community/Qwen3.8-27B-4bit Splash パッケージの重みとほぼ同一に準備される
Prism ML Ternary Bonsai 2 PQ2_0(7.2GB) ビジョン対応
Splash パッケージ(レガシー) incoai/Qwen3.8-27B-Splash 引き続き --model に指定可能

どの形式でも、モデル系統(Qwen3.8-27B / Qwen3.6-35B-A3B)に合った DFlash 2 ドラフトが自動で選ばれます。「対応モデル系統は2つのまま、量子化の選択肢が広がった」と捉えるとわかりやすいと思います。

24GB の Mac で動かす場合

小さい量子化を使うと、24GB の Mac でも動かせます。performance.md には、24GB の M6(12コア GPU)での計測が載っています。

Unsloth GGUF コードのデコード 公称のコンテキスト上限
Qwen3.8-27B-GGUF:UD-IQ3_XXS 43.5 tok/s 73,721 トークン(--language-only で 102,393)
Qwen3.6-35B-A3B-GGUF:UD-Q2_K_XL 145 tok/s 256K トークン

ここで大事な注意があります。performance.md によると、コーディングエージェントには約100Kトークンのコンテキストが必要です(Claude Code 自身のプロンプトだけで約33Kあり、64Kの上限では compaction を繰り返して止まってしまったそうです)。そのため、24GB の Mac でエージェント用に 27B を動かすなら、ビジョンを読み込まない --language-only を付けて、コンテキスト上限を 100K 超まで広げることが勧められています。

24GB 構成については、「Splash は macOS 用に 2 GiB を空けておき、さらに 1 GiB 空いている間だけキャッシュを増やすので、他のアプリの余地はほとんど残らない」という注意も書かれています。メモリを取れなかったリクエストは待機したあと resource_timeout で失敗するので、その場合はメモリを多く使うアプリを閉じるか、--language-only で起動するよう勧められています。

主な設定オプション ⚙️

Splash は「調整するものがない」ことを売りにしていますが、同じ Mac で他の作業もする以上、上限を決めるオプションはいくつかあります。README と DEVELOPMENT.md から、よく使いそうなものをまとめました。

オプション 既定値 用途
--max-memory 28G 自動 Metal のメモリ使用量の上限(プロセス全体の RSS ではない)
--max-context 100K 自動 コンテキストの上限(最大 256K)
--language-only オフ ビジョンを読み込まず、テキストのみ提供(画像・PDF 入力は拒否)。レガシーの Splash パッケージでは使えない
--kv-format bf16 int8 KV キャッシュを BF16 にする。KV キャッシュのメモリが約2倍になり、長いコンテキストでは遅くなることも
--max-cache-disk 16G 0(オフ) KV キャッシュと GDN 状態を必要に応じて SSD に退避
--decode-share 0.5 長いプリフィル中に、他のリクエストのデコードへ割く時間の比率
--queue-size 32 同時に受け付けるリクエスト数(超えると 503 と Retry-After)
--port 8000 ポート番号(SPLASH_PORT でも指定可)
--host 127.0.0.1 バインドするアドレス
--api-key SPLASH_API_KEY またはなし API リクエストに Bearer トークンまたは x-api-key を必須にする
--default-reasoning-effort モデル既定 リクエストで指定がないときの推論 effort

メモリとコンテキストは、指定しなければモデルのネイティブなコンテキストウィンドウまで自動で決まります。日常的に使う Mac では、--max-memory で他のアプリのための余裕を残しておくのがよさそうです。

使うときの注意点 ⚠️

最後に、ドキュメントから拾った「知っておきたい注意点」をまとめます。

既定では認証なしで localhost に待ち受ける

README には「サーバーは既定で、認証なしで localhost に待ち受ける」と書かれています。自分の Mac の中だけで使う分には問題ありませんが、LAN の他のマシンから使いたい場合は、次のように --api-key(または環境変数 SPLASH_API_KEY)を付けて起動するのがおすすめです。

splash serve --model mlx-community/Qwen3.8-27B-4bit --host 0.0.0.0 --api-key YOUR_KEY

DEVELOPMENT.md によると、キーを設定しても、ヘルスチェック・レディネスの確認とチャットページは公開のままです(チャットページで送信するにはキーの入力が必要です)。チャットページ自体が不要なら --no-webui で無効にできます。

また、Splash は HTTP の Host ヘッダーをチェックしていて、想定外の名前でアクセスされると 403 を返します。Mac の .local 名などでアクセスしたい場合は --allowed-host で追加します。DEVELOPMENT.md によると、このチェックは「DNS 名をこのアドレスに付け替える(DNS リバインディング)Web ページを締め出すため」のものです 🔒

ディスク容量を見込んでおく

先ほども触れた通り、ダウンロードしたモデルと準備済みの重みの両方がディスクに残ります。Qwen3.8-27B の 4bit 例で最大約40GB、Qwen3.6-35B-A3B で約48GB です。ダウンロードは Hugging Face のキャッシュ、準備済みの重みは ~/Library/Caches/Splash/weights に置かれます。外付けディスクに置きたい場合は、HF_HUB_CACHE と SPLASH_WEIGHT_CACHE で場所を変えられます。

SSD キャッシュ層は書き込み量に注意

--max-cache-disk で有効にできる SSD キャッシュ層は、メモリが足りないときに KV キャッシュを SSD に退避する仕組みです。便利な一方で、performance.md には、24GB の M6 で Ternary Bonsai 2 を6クライアントの混在トラフィックで30分動かしたところ、16GiB のキャッシュ層が 26GB 書き込んだ(1時間あたり約50GB) という計測が載っています。SSD の書き込み寿命が気になる方は、常用する前に一度様子を見た方がよさそうです。なお、このキャッシュはサーバーを再起動すると消えます。

対応モデルは2系統だけ

これは注意点というより設計思想そのものですが、Splash で動かせるのは Qwen3.8-27B と Qwen3.6-35B-A3B の系統だけです(それぞれの量子化バリアントと、Ternary Bonsai 2 を含みます)。「いろいろなモデルを切り替えて試したい」という用途には、汎用エンジンの方が向いています。Inco は、自分のファインチューンを含めて「このモデルを載せてほしい」という要望を contact@inco.ai で受け付けているそうです。

ベンチマークは「エンドツーエンド・自社計測」

繰り返しになりますが、この記事で紹介した性能値は Inco の計測値で、各エンジンを推奨設定で動かしたエンドツーエンドの比較です。自分の Mac とワークロードで確かめたい場合は、DEVELOPMENT.md の「Local benchmarks」に手順がまとまっています 🧪

Splash が向いている人・向いていない人 🎯

ここまでの内容を、ざっくり整理するとこうなります。

向いている 向いていない
M3 以降・36GB 以上(できれば 48GB 以上)の Mac を持っている Intel Mac や M1 / M2 世代の Mac を使っている
Claude Code・Codex・OpenCode などのエージェントを、ローカルモデルで動かしたい 多くのモデルを切り替えて比較したい
サブエージェントの並列実行や、長い会話のキャッシュ再利用を活かしたい Qwen 系以外のモデルを使いたい
設定を細かく調整せず、入れてすぐ速く動かしたい エンジンの内部パラメータを自分で細かくチューニングしたい

まとめ 🌊

Splash は、「どんなモデルでも動かせる」という推論エンジンの常識をあえて手放し、モデルを2系統に絞って、そのモデル専用にカーネル・ドラフト・メモリ計画を作り込むことで速度を出している推論エンジンです。

Inco の計測では、その効果はコーディングエージェントのループと噛み合う形で出ています。会話が長くなってもデコード速度が落ちにくいこと、2ターン目以降はキャッシュ再利用で最初のトークンが数百ミリ秒で返ってくること、サブエージェントを並列に走らせるほど差が広がること。どれも「エージェントをローカルで回す」という使い方に直結する性質です。一方で、大きなリポジトリを最初に読み込むコールドリードは、Splash でも重いままである点も、発表ブログ自身がはっきり書いています。

この記事で一番持ち帰りたいのは、**「汎用性と効率はトレードオフで、Splash は効率の側に振り切っている」**という捉え方です。対応モデルが少ないことは弱点に見えますが、それと引き換えに「入れてすぐ速い」「設定することがほとんどない」という体験を作っています。

M3 以降で 48GB 以上の Mac をお持ちなら、brew install incoai/tap/splash と splash serve の2行で始められます。まずは splash claude や splash codex で、普段使っているエージェントをローカルモデルにつないでみるところから試してみてください 🙌

参考

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?