修正: 推論時にシステムRAMをすべて使うデュアルIntel Arc GPU(原因を特定し、動作する修正を提示)(llama.cpp SYCL)

Reddit r/LocalLLaMA / 2026/4/8

📰 ニュースDeveloper Stack & InfrastructureIdeas & Deep AnalysisTools & Practical Usage

要点

  • この記事では、SYCLバックエンドでllama.cppを実行するデュアルIntel Arc GPUにより、モデルがVRAMに収まっていても極端なシステムRAM使用が発生し得ることを説明しています。
  • 原因は特定のSYCL呼び出し `sycl::malloc_device()` で、これによりIntelのxeカーネルドライバが、割り当て時にDMA-buf/TTMのステージングを介してGPUの割り当てをシステムRAMへミラーリングします。
  • ベンチマークでは、 `sycl::malloc_device()` はシステムRAMへの影響がほぼ同程度(例: 4 GiBのVRAMに対して約+4,112 MiB)であるのに対し、Level Zeroの `zeMemAllocDevice()` は同条件で影響が最小限(約+8 MiB)であることが示されています。
  • 提案されている修正は、 `sycl::malloc_device()` の代わりにLevel Zeroの割り当て経路( `zeMemAllocDevice()` )を使用することです。SYCLカーネルは生成されたポインタを互換性の問題なく読み取れるため、この変更を活用します。
  • 報告されている症状には、システムRAMが100%に到達すること、OOM killerがデスクトッププロセスを終了させること、複数GPUでモデルを読み込む際にシステムの不安定化やログイン画面へのドロップが発生すること、などがあります。

llama.cppでデュアルのIntel Arc GPUを使っていて、マルチGPU推論中にシステムRAMが上限まで張り付く(モデルはVRAMに収まっているのに)場合、この投稿ではその理由と直し方を説明します。

私はllama.cppのSYCLバックエンドで、ローカルLLM推論のためにデュアルArc Pro B70(各32GB、合計64GBのVRAM)を動かしています。毎回、両方のGPUにモデルを分割しようとすると、私の64GBのシステムRAMが100%まで上がり、OOM killerがシステムをクラッシュさせるかログイン画面に落とされるまで、デスクトップのプロセスを次々に殺し始めました。これはあらゆるモデルサイズで起きました。15 GiBのQ4_K_Mモデルが、システムRAMを46 GiB食っていました。意味がわかりません。

調べてみると、これは設定の問題でも、VRAMの問題でもなく、モデルサイズとも関係ありません。llama.cppのSYCLバックエンドにある特定のAPI呼び出しが、Intelのxeカーネルドライバにおいて間違ったメモリ経路を引き起こしているのです。

実際に起きていること

SYCLバックエンドで sycl::malloc_device() を呼ぶたびに、xeカーネルドライバがDMA-buf/TTMステージング経由で、GPU確保領域の1:1ミラーをシステムRAM上に作成します。これは推論中ではなく、確保(allocation)時に発生します。GPU上に確保されるすべてのテンソル、すべてのKVキャッシュバッファ、すべての計算用スクラッチバッファが、システムRAMを同量ずつ消費していきます。

私は、的を絞ったテストでこれを確認しました:

Allocation Method 4 GiB on GPU System RAM Impact
sycl::malloc_device() 4 GiB VRAM +4,112 MiB system RAM
zeMemAllocDevice() 4 GiB VRAM +8 MiB system RAM

同じVRAM確保、同じGPU、同じドライバです。呼び出すAPIによって、システムRAM使用量が500倍も違います。

xeドライバには、デバイスメモリ用の内部カーネル経路が2つあります:

  1. DMA-buf/TTM - VRAMをシステムRAMにミラーします。これが sycl::malloc_device() が引き起こす経路です。
  2. SVM/P2P - 直接PCIe BARアクセスで、ほぼシステムRAMを使いません。Level Zeroの zeMemAllocDevice() が使う経路です。

SYCLカーネルは zeMemAllocDevice のポインタをゼロの問題なく読み取れます。完全な相互運用で、互換性の問題はありません。違いは裏側でどちらのカーネル経路がトリガーされるかだけです。

見覚えのある症状

  • 2つのGPUにまたがってモデルをロードすると、モデルがVRAMに収まっているのにシステムRAMが100%まで上がる
  • OOM killerがデスクトップのプロセス(pipewire, nautilus, wireplumber)を潰し始める
  • システムが応答不能になったり、ログイン画面に落とされたりする
  • スワップを追加すると「助かる」が、推論があまりに遅くなる
  • 誰かに「デュアルGPUなら128GB RAMが必要だ」と言われた
  • シングルGPUは問題なく動くが、デュアルGPUだとクラッシュする

対処方法

llama.cppのSYCLバックエンド全体で sycl::malloc_device()zeMemAllocDevice() に置き換えてください。私は、自動フォールバック付きの集中管理(centralized)ヘルパ関数を書きました:

static void * ggml_sycl_malloc_device(size_t size, sycl::queue &q) { void *ptr = nullptr; try { auto ze_ctx = sycl::get_native<sycl::backend::ext_oneapi_level_zero>(q.get_context()); auto ze_dev = sycl::get_native<sycl::backend::ext_oneapi_level_zero>(q.get_device()); ze_device_mem_alloc_desc_t alloc_desc = {ZE_STRUCTURE_TYPE_DEVICE_MEM_ALLOC_DESC}; ze_result_t r = zeMemAllocDevice(ze_ctx, &alloc_desc, size, 64, ze_dev, &ptr); if (r == ZE_RESULT_SUCCESS && ptr) return ptr; } catch (...) {} return sycl::malloc_device(size, q); // fallback } 

この修正は4つのファイルに及び、3つの確保(allocation)箇所と3つの解放(free)箇所を置き換え、ze_loaderにリンクします。何らかの理由でLevel Zeroの相互運用が利用できない場合でも、元の sycl::malloc_device の挙動に自動的にフォールバックします。

修正前と修正後

Q4_K_M(15.6 GiBモデル)、48Kコンテキスト、デュアルGPU:

Metric Before After
Peak system RAM 60,034 MiB (100%), OOM crash ~6.7 GiB (10%), flat
Prompt processing crash 782 t/s
pp512 speed 348 t/s 359 t/s
tg128 speed 17.92 t/s 17.92 t/s

Q8_0(26.6 GiBモデル)、32Kコンテキスト、デュアルGPU:

Metric Before After
Peak system RAM 100%, OOM crash flat, no issue
Prompt processing crash 915 t/s

システムRAMは、デュアルGPUテスト中ずっと約10%のままです。OOMなし、クラッシュなし、性能低下なし。出力はシングルGPUとデュアルGPUの間でバイト単位で完全に同一です(seed=42で検証済み)。

動かしてみたがダメだったこと

本当の原因を見つける前に、これらには何時間も費やしました。どれも問題を解決しません:

  • IOMMUを無効化(GRUBで iommu=off) - 効果なし
  • 直接SYCLのデバイス間memcpy(ホストのバウンスバッファを置き換え) - 転送は速くなるが、同じRAM使用量
  • NEOのデバッグキー(UseKmdMigration=0 など) - 効果なし
  • cgroupメモリ制限 - TTMの確保はカーネル側で行われるため、プロセスのcgroupには課金されない
  • PCIeルートポートのACSを無効化 - 効果なし
  • Level Zero IPCハンドル(zeMemGetIpcHandle) - これらもシステムRAMを消費する

唯一の解決策は、確保関数そのものを置き換えることです。

なぜNvidiaとAMDにはこの問題がないのか

CUDAとROCmには、カーネルの汎用DMA-buf経路を通らない独自のピアツーピアメモリ管理があります。Intelのxeドライバにはカーネル7.0以降で動作するP2P/SVM経路がありますが、sycl::malloc_device() はそれを使わず、古いDMA-bufエクスポート経路をトリガーしてしまいます。Intel自身のマルチGPU推論スタック(llm-scaler。vLLMを使う)では、Level ZeroのAPIを直接使うことでこの問題を回避しています。

システム情報

  • 2x Intel Arc Pro B70(各32 GB、Battlemage/Xe2)
  • AMD Ryzen 5 9600X、64 GB DDR5-4800
  • Ubuntu 26.04、kernel 7.0.0-12-generic、xeドライバ、compute-runtime 26.09
  • llama.cpp SYCLバックエンド(commit 69c28f1)
  • AMD Radeon iGPUでディスプレイ表示、両方のB70は計算専用
  • モデル:Qwen3.5-27B(Q4_K_M, Q5_K_M, Q6_K, Q8_0でテスト)

次にやること

私はこれをllama.cppへのPRとして提出する予定です。もしこの問題に遭遇していて、ローカルで直したいなら、完全なパッチとテストプログラムを共有する用意があります。

これは、llama.cppに限らずSYCLベースの推論エンジンでIntelのマルチGPUを使っている人に広く影響しているはずです。根本原因は、llama.cpp固有ではなく、SYCLの確保関数がxeドライバとどう相互作用するかにあります。

また、解決策を見つける前に初期の調査結果をX上に投稿しました。リアルタイムの調査を見たい場合はそちらをご覧ください。

submitted by /u/Katostrofik
[link] [comments]