本記事でやりたいこと
Intel の GPU カード「Arc Pro B60」を最近購入(ナンデ)したのですが、 そろそろSLMを動かしてみようと思います。(腰が重い)
llama.cppの実装を見ると、こんな環境変数が出てきます。
ONEAPI_DEVICE_SELECTOR=level_zero:gpu # Level Zero 経由
ONEAPI_DEVICE_SELECTOR=opencl:gpu # OpenCL 経由
どちらも同じ GPU を使うのですが、GPUを動作させるミドルウェアが違うようです。
Arc Pro B60をPCマザボに直刺しして + Ubuntu 26.04 + xe driver という組み合わせで使った記事が意外とない(人気がn。。。)
なので、今回ちょっと測ってみました。
OpenCL と Level Zero のルーツを辿ってみる
OpenCL とは
OpenCL(Open Computing Language)は 2008 年ごろから使われているオープンな GPU 計算の標準規格です。Khronos Group という業界団体が決めたルールに沿って作られているそう。
私はIntelとNVIDIAをどちらも使っています。(でもNVIDIA全方位すぎて苦手)
llama.cpp あぷり
↓
ggml-sycl SYCL用らいぶらり
↓
SYCL / oneAPI Unified Runtime SYCL
↓
OpenCL backend ナニコレ
↓
Intel OpenCL NEO / compute-runtime ナニコレ
↓
xe driver ドライバだろ!
↓
Arc Pro B60
- 良いところ:どのメーカーの GPU でも動く。長年使われてきた安定した実装がある
- 惜しいところ:抽象化されている実装は変更しづらく、低レベルの最適化がしにくい
Level Zero とは
Level Zero は Intel が 2020 年ごろに公開した、自社 GPU 専用の低レベル API です。NVIDIA で言えば「CUDA Driver API」に近い位置付けです。
llama.cpp あぷり
↓
ggml-sycl SYCL用らいぶらり
↓
SYCL / oneAPI Unified Runtime SYCL
↓
Level Zero backend ナニコレ
↓
Intel Level Zero driver / compute-runtime ナニコレ
↓
xe driver ドライバだろ!
↓
Arc Pro B60
- 良いところ:ちょっと今のところ分からない
- 惜しいところ:Intel GPU 専用なので他のメーカーでは使えない、AMDやNVIDIAのGPUに使う気が起きないだけかもだけど。
※Intel 自身が oneAPI ドキュメントの中で Level Zero を SYCL ランタイムの推奨下位経路として位置付けているみたいですが、なぜか不明
llama.cpp での切り替え方
llama.cpp の SYCL backend は内部でどちらかの API を使います。環境変数 1 つで切り替えられるのは初心者に優しいと思いましたね。
検証環境
ハードウェア
| デバイス | 商品名や特徴のメモ |
|---|---|
| GPU | Intel Arc Pro B60(Battlemage、VRAM 24GB) |
| CPU | AMD Ryzen 9 9900X(12コア / 24スレッド) |
| RAM | 64GB DDR5-5600 |
ソフトウェア
| ソフトウェア | 名称や特徴のメモ |
|---|---|
| OS | Ubuntu 26.04 LTS |
| カーネル | 7.0 |
| GPU ドライバ | xe(Intel Arc ネイティブドライバ) |
| llama.cpp | commit e674b1279(b9432) |
| oneAPI | /opt/intel/oneapi |
検証した内容とコマンド
検証内容
今回はllama-benchを使ってトークン生成にかかる時間を計測しました。
音声を生成したり、thinkingしたり、モデルのタスクが違うのでモデル間も比較はせず、あくまでモデルを適当にbenchしたらドライバでどれぐらい違うのかを測ってます。
PPはざっくり入力読み込み、TGは最終的な出力速度として測ってみました。ただ、入力トークンは一括処理できるけど、出力トークンは逐次出力ってとこがポイントです。
ここがまとめて出てくるといいんだろうな・・・
コマンド例
下記はコマンドの例です。実際はこれをshellにまとめて定期的に実行しました(連続実行すると部屋が暑くなるし、GPU温度が高くなっていってしまう。。。30秒ぐらい間を空けています。)
# Level Zero 経由
ONEAPI_DEVICE_SELECTOR=level_zero:gpu \
./build/bin/llama-bench \
-m /models/MODEL.gguf \
-ngl 999 \
-p 512,2048,4096 \
-n 128 \
-b 512 -ub 512 \
-r 3 \
--output csv
さて、例によって解説。もういらんかもだけど。書きます。
-ngl 999 全レイヤーを GPU にオフロード
-p 512,2048,4096 プロンプトの長さ(トークン数)
-n 128 生成するトークン数
-b 512 -ub 512 バッチサイズ
-r 3 3回繰り返して平均を取る
OpenCL 経由は ONEAPI_DEVICE_SELECTOR=opencl:gpu に変えるだけで、それ以外は同じです。
測定したモデル
| モデル | 量子化 | ファイルサイズ |
|---|---|---|
| LFM2.5-8B-A1B | Q6_K | 6.5GB |
| LFM2.5-8B-A1B | Q8_0 | 7.9GB |
| LFM2.5-Audio-1.5B-JP | F16 | 2.8GB |
| LFM2.5-Audio-1.5B-JP | Q8_0 | 1.6GB |
| LFM2.5-Audio-1.5B-JP | Q4_0 | 900MB |
| Qwen3-14B | Q4_K_M | 8.3GB |
個人的には、日本語音声モデルを使えるようにしていきたいので、日本語出力に対応しているモデルを測定しました。
検証結果
| モデル | 量子化 | PP 差分(平均) | TG 差分 | 勝者 |
|---|---|---|---|---|
| LFM2.5-8B-A1B | Q6_K | +3.2% | +10.6% | OpenCL |
| LFM2.5-8B-A1B | Q8_0 | +3.0% | +12.5% | OpenCL |
| LFM2.5-Audio-1.5B-JP | F16 | +0.3% | +1.9% | 同等 |
| LFM2.5-Audio-1.5B-JP | Q8_0 | +0.5% | +10.1% | OpenCL |
| LFM2.5-Audio-1.5B-JP | Q4_0 | +13.4% | +4.9% | OpenCL |
| Qwen3-14B | Q4_K_M | +3.5% | +3.5% | OpenCL |
| 全体平均 | — | +4.2% | +7.3% | OpenCL |
差分はすべて「OpenCL - Level Zero」で、プラスが OpenCL 有利です。なんかOpenCLで十分な気もしている。
考察
ふう。次はちょっと考えてみましょう。事実から得られた傾向は下記っと
- OpenCL の方が全モデルで速かった(F16 はほぼ同じ)
- 特に量子化している場合は特に速い傾向があった(Q4_0 で PP +14%)
- テキスト生成(TG)の差は PP より大きく出た(Q8_0 で TG +12.5%)
ちなみにTGは50~60tok/s んんん、速度は比較してみるかな・・・
なぜ OpenCL が速かったのか
正直、初心者ど素人なので理由はわからん!笑
というのも今回の実装は1行も書いてないし、仕様がわからない。とりあえず事実はそう出たってことで・・・。
達成できたことと次回の Todo
達成できたこと
- やりたかったこと(Level Zero vs OpenCL を Arc Pro B60 上で定量比較する)は達成できました
- 6 モデル × 2 バックエンド × 複数プロンプト長で測定し、OpenCL が全モデルで優位という明確な結果が得られました
次回の Todo
ベンチマークはやっぱりよくわからん!笑
ということで、次からは、いろんなモデルファミリーを動かしていって、比較してみようと思います。
その時に多分下記をやらんといかんのやろな・・・
- llama.cppのマルチモーダル改造(特に音声出力)
- 対応していないモデル用の無理やり推論実装
- いい感じに共通の指標で測るためのテスト要件
- Intelさんの理解
なんか、大変だなぁ..fableにやってほしかったのになぁ(筆者はアメリカから禁止令が出たことでより一層やる気が出たけど、生産者不在みたいな謎状況。Opus+手とかでまずは実装します)
今回はアプリの実測はしてません。