CPU1コアでのLLM推論における量子化6段の比較と、Flash Attentionが逆に働く条件
CPUの1コアだけでLLMを動かし、モデル4段・量子化6段・Flash Attentionの有無を測った59回の記録
手元のPCの1コアだけを使って常駐のAIを動かせるか、という問いから始めた。測ってみると、1コアはマシン全体のメモリ帯域の約4分の1を引けており、14分の1ではなかった。詰まるのは文字を出す速さではなく読む速さだった。そして量子化を6段比べたところ、6段のうち3段は他のどれかに速さでも質でも負けていた。小さく詰めるほど速いという前提が成り立たない。原因は、出す速さはサイズどおりに決まるのに、読む速さは型ごとの最適化の有無で決まりサイズと無関係なことにある。加えて、llama.cppの既定であるFlash Attentionの自動選択は、CPUでは文脈が深いほど損になり、4096トークンで1.8倍の差がついた。
| 方法 | Intel Core i5-14600KF(高性能コア6本と高効率コア8本、DDR4-3200を2枚で理論帯域51.2GB/s)の上で、llama.cpp b10991 のCPU版を使った。どの論理プロセッサが高性能側かは推測せず、整数の反復計算を1本ずつ固定して実測して決めた。プロセスの割り当ては ProcessorAffinity で設定し、設定後に実際の値を毎回読み直して記録した。わざと不正な値を入れて拒否されることも確かめ、確認が素通りでないことを担保した。高性能コアと高効率コアの比較は、裏で動く他の作業の変動が差に化けないよう、交互に繰り返して測った。モデルはQwen3の0.6B・1.7B・4B・8Bを同じ系列でそろえ、量子化は4Bについて6段を比べた。質は困惑度の絶対値では分解できなかったため、同じ文章に対する語ごとの確率のずれを基準の量子化と突き合わせる方法に切り替えた。 |
|---|---|
| 結果 | 1コアが引けた帯域は高性能コアで約13.5GB/s、高効率コアで約5.5GB/s。前者は理論値51.2GB/sの26%にあたる。出す速さはモデルのサイズにほぼ反比例し、この関係から8Bを2.8トークン毎秒と予言して実測は2.83だった。量子化6段のうちQ2_K・Q3_K_M・Q5_K_Mの3段は、他のどれかに速さでも質でも負けていた。Flash Attentionは文脈4096トークンで、切ったほうが1.80倍速かった(切る4.65、入れる2.58、いずれも3回の平均で幅は0.07以内)。スレッド優先度とミニバッチの大きさは効果が無かった。 |
| 知見 | 「小さく詰めれば速くなる」は1コアのCPUでは成り立たない。出す速さはメモリ帯域をサイズで割った値で決まるので確かにサイズどおりだが、読む速さはその型に最適化された計算経路が実装に用意されているかどうかで決まり、サイズとは無関係に動く。1コアでは読む側が往復時間の半分以上を占めるため、サイズを削って得た利得を読む側の遅さが食い潰す。実際にQ5_K_Mは全6段で読む速さが最下位で、Q8_0より、Q2_Kより遅かった。 |
| 限界 | 裏で他の作業が動いている状態で測っているため、値はそのまま静かな環境には移せない。とくに高効率コアはWindowsが常駐の裏方を寄せていて測定中も75〜89%埋まっており、高効率コアの数字は素の実力ではなく混み合った状態での取り分である。モデルはQwen3の一系列しか試しておらず、他の構造で同じ傾向が出るかは分からない。質の測定は16チャンクにとどめており、量子化どうしの順位は分離できたが絶対値としては粗い。コアを2つ3つと増やしたときにどこで頭打ちになるかは測っていない。llama.cppの版が変われば、型ごとの最適化の有無も変わりうる。 |
手元のPCには14個のコアがある。そのうち1個を、常に動いているAIに明け渡したらどうなるか。残りの13個は普段どおり使えるのだから、もし実用になる速さが出るなら悪い取引ではない。この問いから測り始めた。
条件が一つあった。他のプロセスを止められない。ブラウザも、配信の道具も、壁紙のプログラムも動いたままである。だから1コアずつしか使わない設計にし、裏の混み具合も毎回記録した。以下の値はすべて「他の作業が動いている裏で動かしたときの速さ」である。常駐用途を想定しているので、それでよい。
まず、コアは均質ではなかった
使ったCPUは高性能なコアが6本と、高効率なコアが8本という構成である。どの論理プロセッサがどちらかは、推測せずに測って決めた。同じ整数の反復計算を1本ずつに固定して走らせると、はっきり二つに分かれた。
| 論理プロセッサ | 所要 | 正体 |
|---|---|---|
| 0〜11 | 715〜800 ミリ秒 | 高性能コア6本(2スレッドずつ) |
| 12〜19 | 1680〜2110 ミリ秒 | 高効率コア8本 |
差は約2.4倍。この数字を後で何度も使うことになる。
ここで先に一つ分かったことがある。測定のために何も動かしていない状態でコアごとの使用率を見たところ、高効率コアは最初から75〜89%埋まっていた。Windowsが常駐の裏方をそちらへ寄せているためである。つまり「1コアを明け渡す」と言うとき、高効率コアはもう空いていない。空いているのはむしろ高性能コアのほうだった。
1コアは、マシン全体の4分の1を引ける
文字を出す処理は、1文字ごとにモデルの重みを全部読み直す。だから速さは計算力ではなくメモリの帯域で決まる。上限は単純で、帯域をモデルのバイト数で割った値になる。
このマシンの理論帯域は51.2GB/s。では1コアはそのうちどれだけ引けるのか。測ると、高性能コア1スレッドで約13.5GB/s、高効率コア1スレッドで約5.5GB/sだった。前者は全体の26%にあたる。
14分の1ではない。1本のコアは単独では全帯域を引けないが、コアの数に比例して減るわけでもない。だから1コアでも、思ったより大きなモデルが動く。
この関係の強さを確かめるため、モデルを4段に振った。0.6Bから8Bまで、実サイズにして12倍の幅がある。
| モデル | 実サイズ | 読む | 出す | 引けた帯域 |
|---|---|---|---|---|
| Qwen3-0.6B | 0.37 GB | 158.8 | 39.45 | 14.58 GB/s |
| 1.7B | 1.03 GB | 51.2 | 13.14 | 13.55 |
| 4B | 2.33 GB | 19.3 | 4.94 | 11.49 |
| 8B | 4.68 GB | 10.2 | 2.83 | 13.24 |
単位はトークン毎秒。高性能コア1スレッドでの値である。引けた帯域が12倍のサイズ差をまたいでほぼ動かないことが、この式の裏付けになる。
実際に予言もできた。1.7Bまで測った時点で「帯域13.5GB/sが一定なら8Bは2.8になる」と書き、実測は2.83だった。ただしこの予言は一度捨てかけている。その話は後で書く。
詰まるのは、喋る速さではなく読む速さ
上の表で、読む速さと出す速さが別々に並んでいることに意味がある。文字を読む処理は帯域ではなく計算で決まるので、別の法則で動く。
高性能コアと高効率コアの差を、両方について見るとこうなった。
| 仕事の種類 | 高性能コア ÷ 高効率コア |
|---|---|
| 整数の反復計算 | 2.4倍 |
| 文字を出す(メモリを流す) | 2.4〜2.6倍 |
| 文字を読む(ベクトル演算) | 4.1〜5.2倍 |
読む側だけ差が倍近い。最初に測った2.4倍は整数計算の値で、読む処理の主役であるベクトル演算の差を過小評価していた。高効率コアはベクトル演算の器が特に弱い。
これが常駐AIの設計に直接効く。8Bを1コアに置くと、1000字のプロンプトを読むだけで100秒かかる。喋り始めるまでに100秒である。つまり限界は喋る速さではなく読む速さのほうにあり、設計は「長い文脈を渡さない」方向に寄せるべきことになる。
同じコアの2本目のスレッドは、ただで2割速い
高性能コアは1本の物理コアで2つのスレッドを持つ。物理コアを増やさずに、その2本目も使ったらどうなるか。
| 4B・物理コア1つ | 読む | 出す | 引けた帯域 |
|---|---|---|---|
| 1スレッド | 19.3 | 5.24 | 12.2 GB/s |
| 2スレッド | 21.5 | 6.32 | 14.7 GB/s |
3回ずつ取って、ばらつきより明らかに大きい差が出た。物理コアは1つのままで出す速さが21%上がる。「1コアを明け渡す」の中身は、その1コアのスレッドを両方使うのが正解だった。以下の量子化の比較はすべてこの構成で測っている。
量子化6段:小さくしても速くならなかった
ここが本題である。モデルを小さく詰めれば、読むバイト数が減って速くなるはずだ。出す速さは帯域÷サイズなのだから、これは測るまでもない。そう思って6段を並べた。
速さだけ測っても意味がないので、質も測った。困惑度の絶対値では、16チャンクで誤差が±0.92になり、量子化どうしの差である0.3を下回れなかった。そこで同じ文章に対して語ごとの確率のずれを基準の量子化と突き合わせる方法に切り替えたところ、誤差は±0.005まで落ちて完全に分離できた。絶対値で測れないものは、対で測る。
| サイズ | 読む | 出す | 1往復 | 基準と同じ語を選ぶ率 | |
|---|---|---|---|---|---|
| Q2_K | 1.55 GB | 13.3 | 8.35 | 62秒 | 73.6% |
| Q3_K_M | 1.93 GB | 13.2 | 7.23 | 66秒 | 84.7% |
| IQ4_XS | 2.11 GB | 25.7 | 6.93 | 48秒 | 89.7% |
| Q4_K_M | 2.33 GB | 21.5 | 6.14 | 56秒 | 90.7% |
| Q5_K_M | 2.69 GB | 9.9 | 5.94 | 84秒 | 94.1% |
| Q8_0 | 3.99 GB | 14.9 | 4.24 | 81秒 | 基準 |
「1往復」は500字を読んで200字を返すのにかかる秒数である。
出す速さの列はきれいにサイズどおりに並んでいる。問題は読む速さの列で、まったく並んでいない。最下位はQ5_K_Mで、Q8_0より、Q2_Kより遅い。サイズは関係なく、その型に最適化された計算経路が実装に用意されているかどうかで決まっている。
1コアでは読む側が往復時間の半分以上を占める。だからサイズを削って得た利得を、読む側の遅さが食い潰す。結果として、6段のうち3段は他のどれかに速さでも質でも負けた。
| 負けている段 | 勝っている段 | |
|---|---|---|
| Q2_K(62秒・73.6%) | IQ4_XS(48秒・89.7%) | 遅いうえに質も大幅に下 |
| Q3_K_M(66秒・84.7%) | IQ4_XS(48秒・89.7%) | 同上 |
| Q5_K_M(84秒・94.1%) | Q8_0(81秒・100%) | 小さいのに遅く、質も下 |
選ぶ価値があるのはIQ4_XS、Q4_K_M、Q8_0の3段だけだった。
詰めるほど、1バイトあたりの計算代が高い
もう一つ、同じ表の右端から読める事実がある。実際に引けた帯域が、サイズが大きいほど高い。12.98から16.92まで、きれいに上がっていく。
帯域がモデルによらず一定なら、この値は水平になるはずだった。そうならないのは、小さく詰めるほど展開に計算を食うからである。だから「帯域÷サイズ」の予測に対して、Q2_Kは88%しか出ず、Q8_0は115%出た。小さい量子化は、サイズから期待されるほど速くならない。
既定の設定が、CPUでは損をしていた
Flash Attentionは、文脈が長いときの注意機構を効率化する手法である。GPUでは大きな勝ち手なので、llama.cppの既定は自動選択で、これを有効にする。
CPUで測ると逆だった。
| 抱えている文脈 | 入れる | 切る | 差 |
|---|---|---|---|
| 0 | 7.51 | 7.26 | ほぼ無し |
| 1,024 | 5.14 | 6.40 | 切るほうが25%速い |
| 4,096 | 2.58 | 4.65 | 切るほうが1.80倍速い |
4096の行は3回ずつ繰り返して測った。切る側が4.63・4.69・4.64、入れる側が2.51・2.64・2.58で、ばらつきは0.07以内である。差は揺るがない。
そして自動選択が何を選ぶかも確かめた。文脈4096で自動にすると2.66となり、入れた場合の値に一致した。何も指定しないと、本来の58%の速さで動くことになる。
キャッシュを8ビットに量子化する手も試したが、これはFlash Attentionが有効でないと起動しない。有効にしたうえで使っても3.16にとどまり、切って何もしない場合の4.65には届かなかった。
効かなかった手
試して効かなかったものも書いておく。やってみないと「効かない」も分からない。
| 試したこと | 結果 |
|---|---|
| スレッドの優先度を上げる | 7.29 → 7.11 → 7.03。上げるとむしろ微減 |
| ミニバッチの大きさを変える | 21.0 / 21.9 / 22.3 / 21.9。誤差の範囲 |
読む速さが文脈の長さでどう落ちるかも見たが、こちらは崖が無かった。入力128で26.1、512で25.5、2048で22.0、4096で18.4。32倍に伸ばして3割減である。
予測を3回立てて、3回とも外した
測る前に予測を書き残しておく作法をとった。結果は3戦3敗だった。
| 立てた予測 | 実際 |
|---|---|
| 出す側の高性能/高効率の差は2.4倍より小さい(帯域律速だから) | 2.4〜2.6倍。ほぼ同じだった |
| 読む側の差は2.4倍に近い | 4.1〜5.2倍。倍近く違った |
| IQ4_XSは展開の計算代で予測を下回る | 予測ちょうど。下回ったのはQ2_Kのほうだった |
外れ方には筋が通っていて、3つとも仕事の種類によって効く資源が違うという一つの説明に収まる。整数計算とメモリ流しでは2.4倍差、ベクトル演算では4〜5倍差。最初に測った2.4倍を全部の仕事に当てはめたのが間違いだった。
もう一つ、記録しておくべき失敗がある。4Bで引けた帯域が11.49と低く出たとき、これを傾向と読んで「8Bは2.2くらいまで落ちる」と予測を下方修正した。実測は2.83で、修正前の予測のほうが正しかった。4Bが外れ値だっただけである。繰り返しを取る前に1点の動きを傾向と読んだ結果、当たっていた予測を捨てかけた。
全部を合わせるとどうなるか
最後に、何も考えない設定と、分かったことを全部入れた設定を正面から比べた。文脈1024トークンを抱えた状態である。
| 読む | 出す | |
|---|---|---|
| 素のまま(Q4_K_M・1スレッド・既定) | 16.9 / 17.0 | 4.37 / 4.27 |
| 全部盛り(IQ4_XS・2スレッド・Flash Attentionを切る) | 20.2 / 20.1 | 6.47 / 6.44 |
| +19% | +50% |
2周とも同じ値になった。1000字を読んで200字を返す1往復が、106秒から81秒になる。
効いた手は3つしかない。量子化をIQ4_XSにすること、同じ物理コアの2スレッドを使うこと、そしてFlash Attentionを明示的に切ること。優先度もミニバッチも効かなかった。
で、1コアに常駐AIは置けるのか
置ける。ただし選べるモデルには線がある。
| モデル | 1往復 | 判定 |
|---|---|---|
| 0.6B | 8秒 | 速いが頭が弱い |
| 1.7B | 25秒 | 軽い仕事なら十分 |
| 4B(IQ4_XS) | 48秒 | 質と速さの折り合いが一番よい |
| 8B | 190秒 | 1コアには重すぎる |
高性能コア1本の実用線は1.7Bから4Bまで。8Bは読むだけで時間を使い切る。高効率コアは、速度が半分以下なうえWindowsが既に埋めているので選択肢から外れる。
測り方について
この回で効いた作法を残しておく。
- どの論理プロセッサが高性能側かは推測せず実測して決めた。番号の並びは環境によって変わりうる
- コアへの固定は、設定した後に実際の値を読み直して毎回記録した。使った道具にも固定用の指定はあるが、それはプロセスの割り当てを変えていなかった
- わざと不正な値を入れて拒否されることを確かめた。正しい値で成功しただけでは、確認が素通りしているのと区別がつかない
- 高性能コアと高効率コアは交互に繰り返して測った。まとめて測ると、裏で動く他の作業の変動が差に化ける
- 質は絶対値ではなく対で測った。絶対値では誤差が差を上回った
測定はすべて、他の作業が動いている裏で行っている。静かな環境の数字ではない。常駐用途を想定した測定なので意図的にそうしたが、値をそのまま他所に持っていくことはできない。
限界
- 裏の負荷がある状態での値であり、静かな環境には移せない。とくに高効率コアの数字は素の実力ではなく混み合った状態での取り分である
- Qwen3の一系列しか試していない。他の構造で同じ傾向が出るかは分からない
- 質の測定は16チャンクにとどめた。量子化どうしの順位は分離できたが、絶対値としては粗い
- コアを2つ3つと増やしたときにどこで頭打ちになるかは測っていない
- 型ごとの最適化の有無は実装の版に依存する。llama.cppが更新されれば、順位は変わりうる
資料
測定59回の生の記録と、図を作り直すための一式を添えた。図は手で描かず、記録から生成している。