3行でいうと

  • 速さは2つです。入力を読む「prefill(pp)」と、続きを作る「decode(tg)」。単位はどちらもトークン/秒です。
  • llama-benchの既定は、pp512、tg128、5回繰り返しの平均です。トークン化とサンプリングの時間は含まれません。
  • Ollamaの eval rate は、生成したトークン数を生成にかかった時間で割った値です。
  • Appleの記事は、最初のトークンまでは計算が律速、その後の生成はメモリ帯域が律速だと説明しています。

この記事は、実際に使って確かめた記録ではありません。この記事は公式の文書や論文を読んでまとめたものです。数字の計算は、公式の例から僕らが行ったものです。

先に結論

測るなら、次の2つを別々に記録してください。

1つの「tokens/s」だけが書かれた記事や表は、どちらを指すのか、どの長さの入力で測ったのかが分からないと、比べられません。自分のマシンでは、llama-benchで同じ条件を揃えて測るのが、公式の道具としては一番はっきりしています。

前提:用語

なぜ2つに分けるのか

Appleの機械学習チームの記事(MLXとM5、2025年11月19日)は、「最初のトークンの生成は計算が律速で、2つ目以降のトークンはメモリ帯域が律速」と説明しています。記事には、M4のメモリ帯域が120GB/s、M5が153GB/sで28%高い、とあります。生成速度の向上が19〜27%だったのは、この帯域の差による、と記事は述べています。測定の条件は、プロンプト4,096トークン、追加で128トークンの生成です。

僕らの計算です。「生成1トークンごとに、モデルの重みをほぼ全部読む」と仮定すると、生成速度の上限は、メモリ帯域÷モデルのサイズで見積もれます。たとえば、153GB/sで、Q4_K_Mの8Bモデル(公式の表で4.58GiB、約4.92GB)なら、約31トークン/秒が理論上の上限です。実際の速さは、これ以下になります。この見積もりは、上の記事の説明からの僕らの推測で、記事が式を示しているわけではありません。

llama-bench

llama.cppに付属する測定専用の道具です。READMEによる主な点です。

公式READMEの例です。この記事では、実行していません。

./llama-bench -m model.gguf -p 512,2048 -n 128 -d 0,4096
./llama-bench -m model.gguf -ngl 10,20,30,35
./llama-bench -o csv > result.csv

1行目は、プロンプトの長さ2通りと、深さ2通りを組み合わせて測ります。2行目は、GPUに載せる層の数を変えた比較で、READMEにもこの形の例があります。3行目は、表計算に取り込むためのCSV出力です。

主なオプション(READMEの一覧)です。

オプション 意味 既定
-ngl GPUに載せる層の数 -1
-t CPUのスレッド数 環境による
-b / -ub バッチの大きさ / 内部の小バッチ 2048 / 512
-fa フラッシュアテンション(on/off/auto) auto
-ctk / -ctv KVキャッシュの型 f16
-r 繰り返し回数 5

CSVやJSONには、使ったCPUとGPUの名前、ビルド番号、各設定が一緒に記録されます。後で見比べるときのために、この出力を残しておくのが便利です(見立てです)。

READMEの例の数字から読めること

READMEの例は、NVIDIAのGPU(CUDA)での結果です。Macの値ではありません。

  • 7Bの4ビットモデルでGPUに載せる層を10から35まで増やす例では、tgが13.45から131.66トークン/秒へ、ppが373.36から2400.01トークン/秒へ上がります。全層を載せた最後の段で大きく伸びます。
  • バッチの大きさを128から1024にする例では、pp1024が1436.51から2498.61トークン/秒に上がります。
  • 深さの例(qwen2 7B Q4_K)では、深さ0でpp512が7340.20、tg128が120.60です。深さ512では、それぞれ6425.91、116.71です。文脈が増えると少し遅くなります。
  • CPUのスレッド数の例では、16から32スレッドで、ppは33.52から59.00に伸びる一方、tgは15.32から16.41とほとんど変わりません。

僕らの計算です。深さ512でのppの低下は約12%(6425.91÷7340.20)、tgの低下は約3%(116.71÷120.60)です。

Ollama

Ollamaは、応答のAPIの結果に、時間と数を載せます。ドキュメントの項目は、次のとおりです(時間はすべてナノ秒)。

項目 意味
total_duration 応答全体にかかった時間
load_duration モデルを読み込んだ時間
prompt_eval_count / prompt_eval_duration 入力のトークン数 / キャッシュにない入力の処理時間
prompt_eval_cached_count キャッシュから読んだ入力のトークン数
eval_count / eval_duration 出力のトークン数 / 生成にかかった時間

コマンドでは、ollama run モデル名 --verbose で、応答のあとに時間が表示されます(コマンドの説明に「Show timings for response」とあります)。表示の計算は、ソースコードを読むと次のとおりです。

APIの例から、速さを計算する

ドキュメントの応答の例(gemma4、短い質問)の数字です。

  • prompt_eval_count:11、prompt_eval_duration:13,074,791 ns
  • eval_count:18、eval_duration:52,479,709 ns
  • load_duration:101,397,084 ns、total_duration:174,560,334 ns

僕らの計算です。

  • 入力の速さ:11÷0.013074791≒841トークン/秒
  • 生成の速さ:18÷0.052479709≒343トークン/秒
  • 読み込みは約101ms、全体は約175msです。

ただし、入力11トークン、出力18トークンという短さでは、数字の誤差が大きいはずです。この例は、公式の説明用のもので、実測の目安ではありません。

LM Studio

LM Studioの新しいREST API(POST /api/v1/chat)の応答には、stats が付きます。項目は、input_tokens、total_output_tokens、reasoning_output_tokens、tokens_per_second(生成速度)、time_to_first_token_seconds、model_load_time_seconds(モデルが未読み込みのときだけ)です。

見立てです。この統計は、入力の速さそのものは出さず、TTFTと入力トークン数から推し量る形になっています。LM Studioのアプリ画面に出る統計の表示は、今回読んだ文書では確認できませんでした。

llama-serverとMLX

llama-server:応答の timings に、cache_n(キャッシュから再利用した入力トークン)、prompt_n、prompt_per_second、predicted_n、predicted_per_second などが入ります。文書の例は、prompt_n が1、cache_n が236で、prompt_per_second は32.3です。これは、キャッシュを使って1トークンしか処理しなかった結果なので、「入力を読む速さ」としては参考になりません。キャッシュが効いている応答の prompt_per_second を、そのままprefillの速さと読まないでください。

MLX(Mac):mlx_lm.benchmark というコマンドがあり、ソースコードで確認できる既定は、プロンプト512トークン(-p)、生成1,024トークン(-g)、バッチ1、5回(-n)です。1回の測定ごとに、prompt_tps、generation_tps、peak_memory を出し、最後に平均を出します。ランダムなトークンを入力にし、途中で止まらないよう終了トークンを無効にしています。ピークメモリも出る点は、他の道具との違いです。

測るときの手順

見立てです。

  1. モデルと量子化を固定する:同じGGUFで比べます。Q4_K_MとQ8_0では、生成の速さが大きく変わります(llama.cppの量子化READMEの表で、Llama-3.1-8Bの生成が約72と約51トークン/秒。測定機材は書かれていません)。
  2. 短い入力と長い入力の両方で測る:ppは -p 512,4096、深さは -d 0,4096 のように。
  3. GPU/CPUの割り当てを確認する:llama-benchの表の backend と ngl の列を見ます。
  4. 回数を重ねる:標準偏差が大きいときは、その値を信用しません。
  5. 設定ごと残す:CSVかJSONで保存します。

よくある失敗

気をつけること

出典

更新履歴