mcpField
S
serving-llms-on-epyc

serving-llms-on-epyc

github.com/amdクラウド・DevOps470858 回インストール2 日前に更新
リポジトリ

serving-llms-on-epyc について

AMD EPYC CPU上で単一のvLLM OpenAIエンドポイントを稼働させるスキルです。ハードウェアに応じて構成を調整し、モデルとメモリの適合性を確認して、単一ソケットに固定します。失敗時は原因とログを報告して停止します。

主な機能

  1. 01EPYCの型番と命令セットの検出
  2. 02ランタイムとスタック互換性の検証
  3. 03モデル対応状況とホストメモリの確認
  4. 04ソケット単位のCPUスレッドとメモリ調整
  5. 05vLLMエンドポイントの起動と検証

活用シーン

  1. 01EPYCホストでのCPU推論エンドポイント提供
  2. 02モデルが利用可能なRAMに収まるかの確認
  3. 03デュアルソケットホストで単一ソケットを選択

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/amd/skills --skill serving-llms-on-epyc

インストール後、Claude Code で/serving-llms-on-epycを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

.federated.json
reference.md
skill-card.md
data/
epyc.json
evals/
evals.json
machine.yml
scripts/
check_model.py
cpu_tune.py
detect.py
estimate_memory.py
validate.py

詳細

AMD EPYC™ で LLM をサービングする(vLLM + zentorch、CPU)

zentorch CPU バックエンドを使用して、AMD EPYC™ ホスト上にハードウェアに合わせた単一の vLLM OpenAI エンドポイントを起動します。コンテナーを優先し(Docker または Podman)、conda/ホストは代替手段です。AMD Instinct GPU がインストールされていても、このホストは対象外にはなりません。エンドポイント自体を EPYC CPU 上で実行する場合は、このスキルを選択してください。

これは単一ソケットでのサービングです。 1 つのインスタンスを 1 つのソケットとそのメモリに固定します(vLLM はソケット間でのスケーリングが不得意なため、ソケットをまたいで実行しません)。デュアルソケットのホストでは、単一のソケット上で実行します。マルチソケットでの方法はソケットごとに 1 つの複数インスタンスですが、この単一インスタンス向けレシピの対象外です。

このスキルの厳守事項:失敗した場合は、原因とログを報告して停止してください。再試行やデバッグはしないでください。(デバッグは別のワークフローです。)

エージェント自身がサービングのフロー(プル、設定、起動、ポーリング)を実行し、実行時の validate.py レポートを使用します。サービングのたびに実行するコマンドをユーザーに提示しないでください。serving-llms-on-instinct と同様に、アクセス可能なコンテナーランタイムは一度限りの前提条件です。validate.py が見つけられない場合は、一度限りの解決策(docker を利用可能にする/podman をインストールする/conda 環境を用意する)を報告して停止してください。sudo や権限昇格は試みないでください。

データファイル

data/epyc.json を直接読み取ってください。このファイルには、コンテナーイメージ、必須の CPU 実行フラグ、サポート対象の精度、モデルサポートポリシー、デフォルトモデル、確認済みのスループットフラグに関する注意点が含まれています。vllm_version とイメージタグは、検証済みのデフォルトスタックを構成するため、対応させたままにし、記憶を頼りにどちらもハードコードしないでください。

ステップ 1:CPU を検出する

python3 scripts/detect.py            # add --host user@box for a remote host

次の情報を返します:cpu_model、is_amd_epyc、epyc_generation (Naples/Rome/Milan/Genoa/Bergamo/Siena/Turin/Venice または EPYC 4004/4005)、 zen_arch、is_supported_epyc、avx512、logical_cores、physical_cores、 sockets、numa_nodes、memory_gb。

detect.py の結果に基づいてサービング方法を選択します。

  • is_amd_epyc が false -> 停止します。このスキルの対象は AMD EPYC です(ほかの x86 でも動作する可能性はありますが、ここではサポート対象外です)。
  • avx512 が false -> この CPU では zentorch を実行できません(bf16 パスには AVX-512 BF16、avx512_bf16 が必要で、これは Zen4 以降でのみ利用できます)。この CPU は Zen4 より前の EPYC(Naples/Rome/Milan、7000 シリーズ)です。ここで行き詰まらないでください。引き続き EPYC ホストであるため、標準の vLLM CPU パスを提示します(zentorch による高速化はありません。速度は低下しますが、EPYC 7763/Milan で動作確認済みです)。ユーザーの明示的な承認を得た場合のみ、公式の標準 vLLM CPU イメージを起動して続行します(ステップ 6 の「標準 vLLM」バリアント)。ユーザーが辞退した場合は停止してください。
  • is_supported_epyc が false で、avx512 が true(例:Bergamo/Siena/EPYC 4004/4005)の場合 -> この世代では zentorch パスは未検証です。未検証の zentorch を試す明示的な承認を得るか、同じ標準 vLLM の選択肢を提示してください。
  • それ以外(AVX-512 BF16 対応の9000 シリーズ、Genoa/Turin/Venice)-> 検証済みの zentorch パスです。続行してください。

validate.py(ステップ 2)は zentorch_capable を報告し、標準パス/未検証パスには requires_confirmation を設定するため、この振り分けもそこで強制されます。

後続のフェーズでも epyc_generation/avx512 を引き継いでください。たとえば Venice はソケットあたり最大 256 コアを搭載するため、ステップ 5 のスレッドバインディングはこの値に基づいて調整します。

ステップ 2:ランタイムと環境を検証する

python3 scripts/validate.py --image <image from data/epyc.json> --generation <epyc_generation from detect> --avx512 <avx512 from detect>

これにより、AVX-512 の必須条件も強制されます。AVX-512 に対応していない CPU では、validate.py は処理をブロックする error(ready: false)を返すため、ステップ 1 の説明にかかわらず、ここでフローが停止します。イメージのプルも起動も行われません。(--avx512 が省略された場合はローカル CPU を直接読み取るため、値が渡されていなくても必須条件が適用されます。)

ready、requires_confirmation、zentorch_capable(false の場合、zentorch は実行できません。ステップ 6 では標準の vLLM バリアントを使用してください)、runtime(docker、podman、または null)、runtime_detail、conda_path_available、stack、compatibility、hf_cache(解決済みの HF キャッシュのマウント先。起動時には hf_cache.mount を使用してください)、ram_gb、および errors/warnings/advisories が返されます。requires_confirmation が設定されている場合(標準/未検証のパス)は、その旨を提示し、起動前にユーザーの承認を得てください。パスを選択します。

  • runtime が docker または podman -> コンテナパス(ステップ 6)。そのまま使用します。
  • runtime が null で、conda_path_available: true -> conda/ホストパス。
  • runtime が null で、conda もない -> ready は false です。1 回限りのオンボーディング用 fix(docker へのアクセスを可能にする/podman をインストールする/conda 環境を用意する)を報告して停止します。

ready が false の場合は、先に進まないでください。

スタック互換性のゲート。 validate.py は、選択されたランタイムで正確な vllm/zentorch/torch のバージョンと、アクティブな vLLM プラットフォームを調べてから、compatibility.status を設定します。

  • proceed -> スタックは検証済みのデフォルト(または Zen プラットフォーム上の検証済みファミリー)です。続行します。
  • blocked -> 標準の CPU プラットフォームがアクティブなため、zentorch の高速化は有効ではありません(エラー)。compatibility.message を報告して停止します。
  • confirmation_required(requires_confirmation: true)-> Venice で、固定のデフォルト以外の vLLM を使用しています。このレシピは、そのバージョンの Venice では検証されていません。compatibility.message を提示し、data/epyc.json の固定 vllm_version イメージを推奨してから、起動前にユーザーから明示的な実行可否の確認を得るまで停止します。固定のデフォルト vLLM では、Venice は警告なしで続行されます。

ゲートが実行されるのは、イメージがローカルにある場合のみです。validate.py がイメージはプルされていないと報告した場合は、イメージをプルする(またはステップ 6 でプルさせる)とともに、validate.py を再実行して、タグだけでなく実際のスタックを調べるようにしてください。

Step 3: モデルを解決して検証する

ユーザーがモデルを指定していない場合は、data/epyc.json の default_model (Qwen/Qwen3-0.6B -- ゲートなし、小規模、高速で最初の成功を得られます)を使用します。指定がある場合は、そのモデルを使用します。

vLLM が実際にそのモデルをサポートしているか確認します(マルチモーダルモデルを一律に除外しないでください)。 モデルの実行に実際に使われる vLLM バージョンを渡します。validate.py の実行時に stack.vllm が確認されている場合はそれを使用します(conda 環境のバージョンは固定バージョンと異なる場合があります)。それ以外の場合は data/epyc.json の vllm_version を使用します。

python3 scripts/check_model.py --model-id <model> --revision <rev or main> --vllm-version <stack.vllm from validate, else vllm_version from data/epyc.json>
  • 終了コード 0 = vLLM が生成エンドポイントとしてモデルを提供できる、またはサポート状況を判定できない (ゲートあり/オフライン)場合です。続行してください。起動時に確認されます。
  • 終了コード 1 = 停止してください。アーキテクチャが vLLM のレジストリにない、pooling/埋め込み/リランキングモデルである (チャット/補完エンドポイントではない)、または使用可能なチャットテンプレートがないマルチモーダルモデル (launchable: false)です。出力された message を報告して停止してください。

結果には、モデルがサポートするクライアントエンドポイントも含まれます。

  • primary_endpoint: "chat_completions" -- 使用可能なチャットテンプレートがあります (chat_template.status: present)。/v1/chat/completions を提供し、引き継ぎ先として渡します。
  • primary_endpoint: "completions" -- 使用可能または自動選択可能なテンプレートがありません (absent/ambiguous/unknown)。生の prompt を使って /v1/completions を提供し、引き継ぎ先として渡します。 --chat-template <file> を渡せばチャットを有効にできます(ambiguous の場合は chat_template.names のいずれかを選択します)。テンプレートを決して捏造しないでください。
  • primary_endpoint、supported_endpoints、chat_template は、検証(Step 7)と引き継ぎ(Step 8)まで引き継いでください。
  • マルチモーダルモデルも使用できます。vLLM がサポートするマルチモーダルアーキテクチャでも、CPU では GPU 専用カーネルに遭遇する場合があり、その場合は読み込み時に表面化します(その際は再試行しないルールが適用されます)。

精度/dtype:CPU がネイティブでサポートする dtype は bf16(デフォルト)、fp16、fp32 です。ユーザーから別の指定がなければ bfloat16 を使用します。

ゲート付きモデル(Llama、Gemma)では、HF_TOKEN を設定し、HuggingFace でライセンスに同意している必要があります。そうでない場合は停止して、その旨を伝えてください。

Step 4: ホスト RAM に収まるか確認する

CPU では RAM が上限になります(重みと KV キャッシュの両方が RAM 上に置かれます)。1 行で実行します。

python3 scripts/estimate_memory.py --model-id <model> --revision <rev or main> --ram-gb <memory_gb from detect> --max-model-len <4096 or user value> --num-prompts <1 or desired concurrency>

終了コード 0 = 収まる、終了コード 1 = 収まりません。fit.fits が false の場合は、起動しないでください。 required_gb と ram_gb、および出力された fit.action をユーザーに伝えてください。--max-model-len を fit.suggested_max_model_len に減らして再試行するか、より小さいモデルを使用します。KV の容量を変えるパラメーターは --max-model-len と --num-prompts の 2 つです。 追加フラグ:モデルに HF メタデータがない場合(まれです)、--weight-gb N で重みのサイズを上書きできます。zentorch CPU の KV キャッシュは bf16 のみ対応しています(fp8 KV には対応していません)。

Step 5: ハードウェアに合わせて CPU ランタイムのサイズを設定する

eval "$(python3 scripts/cpu_tune.py)"      # or --format json to inspect

単一のインスタンスは、1 つのソケットとそのメモリを使って実行されます(vLLM はソケット間でのスケーリングが不得意です)。cpu_tune.py は VLLM_CPU_OMP_THREADS_BIND(選択されたソケットの物理コア)と VLLM_CPU_KVCACHE_SPACE(システム全体ではなく、そのソケットのローカル RAMを基に設定されるため、KV プールがソケット内に収まります)をエクスポートします。 OMP_NUM_THREADS(vLLM が設定値を導出します)や VLLM_CPU_NUM_OF_RESERVED_CPU(vLLM 独自のデフォルト値)は 設定しません。

デュアルソケットホストでのソケット選択(負荷を考慮):ソケットごとの CPU 使用率を約 0.5 秒間サンプリングし、空いているソケットを優先します。両方空いている場合はソケット 0、片方が空いている場合はそのソケットを選択します。 両方がビジーの場合(使用率 ≥ --busy-threshold、デフォルトは 15%)は、warning を出して負荷の少ないソケットで続行します。--socket N で選択を強制できます。シングルソケットホストではソケット 0 を使用します。

選択したソケットについて、メモリに合わせたバインド設定も出力します。コンテナ向けの container_cpuset (--cpuset-cpus=<cores> --cpuset-mems=<nodes>)と、conda 向けの conda_launch_prefix (numactl --cpunodebind/--membind、使用できない場合は CPU のみを指定する taskset、どちらのツールもない場合は注記付きの空文字列)です。warning が設定されている場合は、ユーザーに伝えてください。NPS2/NPS4 では 1 つのソケットが複数の NUMA ノードにまたがり、メモリはそれらのノードにバインドされます。より細かくバインドすれば性能が向上する可能性があることを nps_note が示します。

ステップ 6:プランを確認してから起動する(コンテナ優先)

起動前にこの概要を提示し、ユーザーの確認を待ってください。事前の確認なしに起動してはいけません。これは、処理を実行する前の人間による確認手順です。

項目値
モデル/種類<model> -- check_model.py の結果に基づく text または multimodal
バックエンドzentorch による高速化(Zen4 以降)、または 標準の vLLM CPU/zentorch なし(zentorch_capable:false -- 高速化なし。Milan で動作確認済み)
パスコンテナ(<runtime>、data/epyc.json のイメージ)または conda/ホスト
精度bfloat16(またはユーザーが選択した値)
適合性必要量 <required_gb> GB、RAM <ram_gb> GB
CPU の割り当てソケット <chosen_socket>(<socket_choice_reason>)、バインド <VLLM_CPU_OMP_THREADS_BIND>、KV <VLLM_CPU_KVCACHE_SPACE> GB(ソケット内)、ノードにバインドされたメモリ <numa_nodes_on_socket>
ハードウェアEPYC <epyc_generation>(<zen_arch>)、<physical_cores> コア、AVX-512 <avx512>
ポート<port>

cpu_tune.py が warning(例:すべてのソケットが使用中)を返した場合は、ユーザーが確認する前にそれが分かるよう、ここに含めてください。

明確な「go」がある場合にのみ進めてください。ユーザーが拒否した場合や変更(モデル、--max-model-len、ポート)を希望した場合は、停止して調整してください。起動してはいけません。

data/epyc.json の設定に基づいて起動コマンドを組み立てます。CLI は vllm serve <model> です。 vLLM 0.20 以降では --device cpu を渡さないでください。zentorch プラグインが CPU プラットフォームを自動選択し、vllm serve はこのフラグを拒否します。vllm serve --help に表示される場合(古い vLLM)のみ追加してください。

まず空きポートを選んでください。 --network=host ではポートがホストに直接バインドされるため、ポートが使用中だと 致命的なエラー になります(ポートの再割り当ては行われません)。空いているポートを選択してください。例:PORT=8000; while ss -ltn "sport = :$PORT" | grep -q LISTEN; do PORT=$((PORT+1)); done。起動、ヘルスチェック、引き継ぎには $PORT を使用してください。

validate.py が解決した HF キャッシュをマウントしてください。 ~/.cache/huggingface をそのまま使うのではなく、validate.py の hf_cache.mount を使用してください(シンボリックリンクをたどり、NFS/root-squash を検出します)。そうしないと、NFS 上のホームディレクトリでコンテナ起動時にバインドマウントが失敗する場合があります。

コンテナパス(validate.py の runtime)。エージェントがプルも含め、以下を実行します。RT は解決済みのランタイムをそのまま使用します。

RT="<runtime from validate.py: docker | podman>"
$RT rm -f vllm-epyc 2>/dev/null               # clear any leftover container from a prior run (name collision otherwise)
$RT pull <image from data/epyc.json>          # agent pulls; do not ask the user to
$RT run -d --name vllm-epyc \
  <run_flags from data/epyc.json>            # --ipc=host --network=host --cap-add=SYS_NICE (SYS_NICE = NUMA membind; NO --shm-size with --ipc=host)
  <hf_cache.mount from validate.py> \        # resolved real path, e.g. -v /scratch/you/hf:/root/.cache/huggingface
  <container_cpuset from cpu_tune>             # --cpuset-cpus=<cores> --cpuset-mems=<nodes>
  --env VLLM_CPU_OMP_THREADS_BIND="$VLLM_CPU_OMP_THREADS_BIND" \
  --env VLLM_CPU_KVCACHE_SPACE=$VLLM_CPU_KVCACHE_SPACE \
  --env HF_TOKEN=${HF_TOKEN} \
  <image from data/epyc.json> \
  vllm serve <model> --dtype bfloat16 --port <port> --max-model-len <len>

Conda/ホストパス(コンテナランタイムなし、conda_path_available が true)。cpu_tune を eval すると環境変数はすでにエクスポートされています。メモリを選択したソケットにバインドするため、起動コマンドの前に cpu_tune の conda_launch_prefix を付けてください(空の場合はバインドされません。その旨を注記してください)。

<conda_launch_prefix from cpu_tune> vllm serve <model> --dtype bfloat16 --port <port> --max-model-len <len> &
# e.g. numactl --cpunodebind=0 --membind=0 vllm serve ...

標準 vLLM パス(zentorch なし) -- validate.py が zentorch_capable: false(Milan のような Zen4 以前の EPYC)を報告し、ユーザーが高速化なしのフォールバックを確認した場合のみ使用してください。zentorch イメージではなく、公式の標準 vLLM CPU イメージ vllm/vllm-openai-cpu:latest-x86_64 を使用します(ENTRYPOINT は vllm serve なので、引数として <model> --dtype ... --port ... を渡します)。上記のコンテナ起動と同じ環境設定とフラグ(VLLM_CPU_OMP_THREADS_BIND、--cpuset-cpus/--cpuset-mems、--cap-add=SYS_NICE、--ipc=host --network=host、解決済みの HF キャッシュマウント)を使用します。

RT="<runtime>"
$RT rm -f vllm-epyc 2>/dev/null
$RT run -d --name vllm-epyc \
  --ipc=host --network=host --cap-add=SYS_NICE \
  <container_cpuset from cpu_tune> <hf_cache.mount from validate.py> \
  --env VLLM_CPU_OMP_THREADS_BIND="$VLLM_CPU_OMP_THREADS_BIND" \
  --env VLLM_CPU_KVCACHE_SPACE=$VLLM_CPU_KVCACHE_SPACE --env HF_TOKEN=${HF_TOKEN} \
  vllm/vllm-openai-cpu:latest-x86_64 \
  <model> --dtype bfloat16 --port <port> --max-model-len <len>

このパスは 高速化なし(zentorch なし)ですが、EPYC 7763(Milan/Zen3)で --dtype bfloat16 を指定して 動作確認済みです。bf16 は AVX-512 がなくても標準の vLLM CPU で実行できるため、fp32 は不要です。それでも読み込みに失敗した場合は、再試行しないルールを適用してください(報告して停止)。

スループット用のオプションフラグはオプトインで、必ずまとめて設定してください(「注意事項」を参照)。TORCHINDUCTOR_FREEZING=1 + VLLM_USE_AOT_COMPILE=0(+ ZENTORCH_WEIGHT_PREPACK=1)。基本の起動設定では、これらを一切設定しません。

ステップ 7:起動して応答するまでポーリングする

読み込み中に 503 が返るのは正常です。サーバーが応答するまで /health をポーリングし、提供されているモデルが一覧にあることを確認してから、選択したエンドポイント(ステップ 3 の primary_endpoint)が動作することを確かめます。CPU での初回トークンのコンパイルには 1、2 分かかることがあります。タイムアウトを失敗として扱い、処理をそのまま続行しないよう、healthy フラグを管理してください。

# 1. container alive (conda: process alive) + /health, with a real timeout
healthy=""
for i in $(seq 1 120); do
  $RT inspect -f '{{.State.Running}}' vllm-epyc 2>/dev/null | grep -q true || { echo "FAILED: container exited"; $RT logs --tail 50 vllm-epyc; break; }
  curl -sf http://localhost:<port>/health >/dev/null 2>&1 && { healthy=1; echo "HEALTHY"; break; }
  sleep 3
done
[ -n "$healthy" ] || { echo "FAILED: not healthy before timeout"; $RT logs --tail 50 vllm-epyc; }

# 2. the served model is registered
curl -sf --max-time 30 http://localhost:<port>/v1/models

次に、モデルが実際にサポートするエンドポイントをテストします。スモークチェックでは、決定論的なサンプリングを使い、出力上限を小さく設定してください。

# primary_endpoint == chat_completions
curl -sf --max-time 180 http://localhost:<port>/v1/chat/completions -H 'Content-Type: application/json' \
  -d '{"model":"<served-model>","messages":[{"role":"user","content":"hi"}],"max_tokens":16,"temperature":0}'

# primary_endpoint == completions  (no chat template)
curl -sf --max-time 180 http://localhost:<port>/v1/completions -H 'Content-Type: application/json' \
  -d '{"model":"<served-model>","prompt":"Hello, world","max_tokens":16,"temperature":0}'

レスポンスが JSON であり、エラーではない choices[0](チャットの場合は message.content、補完の場合は text)を含むことを確認してください。error ペイロードを含む HTTP 200 は成功ではありません。リソースの状態確認(検証リスト):$RT stats --no-stream vllm-epyc。

サーバーが正常な状態にならない場合、/v1/models にモデルが表示されない場合、またはエンドポイントがエラーや空の choices を返す場合:コンテナ/プロセスのログを出力し、失敗したフェーズを明記して、停止してください。再試行しないでください。デバッグループを開始しないでください。

ステップ 8:成功したらエンドポイントを引き継ぐ

サーバーを呼び出すために必要な情報をすべてユーザーに伝えます。接続情報の表を出力してください。

項目値
ベース URLhttp://localhost:<port>/v1(末尾の /v1 が重要です)
提供モデル<served-model>(/v1/models の ID)
エンドポイント/v1/chat/completions または /v1/completions(primary_endpoint に基づく)
理由chat = チャットテンプレートがある場合、completions = テンプレートがない場合(生のプロンプト)
ランタイム/ポート<runtime> / <port>
サイズ設定OMP スレッド数、KV GB、--max-model-len、ソケット/NUMA のピン留め
停止$RT rm -f vllm-epyc(コンテナ)または kill <pid>(conda)

次に、選択したエンドポイントですぐに実行できる例を示します。

チャットモデル(primary_endpoint: chat_completions):

curl -s http://localhost:<port>/v1/chat/completions -H 'Content-Type: application/json' \
  -d '{"model":"<served-model>","messages":[{"role":"user","content":"Hello"}],"max_tokens":128,"temperature":0.7}'

ベース/プロンプトモデル(primary_endpoint: completions):

curl -s http://localhost:<port>/v1/completions -H 'Content-Type: application/json' \
  -d '{"model":"<served-model>","prompt":"Hello, world","max_tokens":128,"temperature":0.7}'

OpenAI Python クライアント(base_url をローカルサーバーに設定します。SDK では空でないキーが必要なため、サーバーに認証がなければ任意のプレースホルダーを使用できます):

from openai import OpenAI

client = OpenAI(base_url="http://localhost:<port>/v1", api_key="EMPTY")
model = client.models.list().data[0].id

# chat model:
r = client.chat.completions.create(
    model=model,
    messages=[{"role": "user", "content": "Hello"}],
    max_tokens=128, temperature=0.7,
)
print(r.choices[0].message.content)

# base/prompt model:
r = client.completions.create(model=model, prompt="Hello, world", max_tokens=128)
print(r.choices[0].text)

渡す引数についてのガイダンス(全リストは reference.md を参照してください):

  • max_tokens は出力の上限です。prompt_tokens + max_tokens は --max-model-len 以下である必要があります。
  • temperature(0 = 決定論的/貪欲、高いほどランダム性が増します)。top_p または temperature のどちらか一方を調整し、両方は調整しないでください。
  • stream: true を指定すると、ブロッキング応答ではなくトークンがストリーミング(SSE)されます。
  • モデルの generation_config.json でサンプリングのデフォルト値が設定されている場合があります。確実にするには明示的な値を渡してください。

オフライン(単一インスタンスのバッチ処理)

サーバーではなく、オフラインで一度だけ実行する場合は、ステップ 6~8 を、同じサイズ設定の環境での単一の vllm bench throughput(またはオフラインの LLM.generate)に置き換え、完了を待ってメトリクスを報告してください。再試行/デバッグをしないルールは同じです。

注意事項

全項目についてはreference.mdをご覧ください。特に重要なものは次のとおりです。

  • --device cpu は vLLM >= 0.20 の vllm serve から削除されています。 zentorch プラグインが CPU を自動選択します。これを渡すと、vllm serve は「unrecognized arguments: --device cpu」というエラーになります。
  • TORCHINDUCTOR_FREEZING=1 だけを設定すると、vLLM 0.23/zentorch 2.11 では engine-core の初期化時にクラッシュします(AssertionError: expected OutputCode, got function)。VLLM_USE_AOT_COMPILE=0 と併せて設定した場合にのみ動作します。片方だけを設定しないでください。
  • /dev/shm には --shm-size ではなく --ipc=host を使用してください。 vLLM には大きな /dev/shm が必要です(コンテナーのデフォルトである 64MB では小さすぎます)。基本レシピでは --ipc=host を使用し、ホストの大きな共有メモリを共有します。--shm-size も併せて渡さないでください。podman では「cannot set shmsize when running in the host IPC Namespace」というエラーになり、docker では重複する設定です。IPC を分離する場合(--ipc=host を外す場合)は、代わりに --shm-size=16g を追加してください。両方を同時に指定することはできません。
  • NUMA/ソケット:1 つのインスタンスは、CPU のバインドと --cpuset-mems(コンテナー)/numactl --membind(conda)を使って、1 つのソケットとそのメモリに固定します。KV のサイズは、そのソケットのローカル RAM に基づいて設定します。デュアルソケットのホストでは、cpu_tune.py が負荷に応じて空いているソケットを選び、両方が使用中の場合は warning を出します。NPS2/NPS4(マルチノードソケット)では、ノード単位でより細かくバインドすると追加の性能向上が見込めることを示す nps_note が出ます。
  • rootless podman と --cpuset-cpus/--cpuset-mems:これらは cgroup の制限であり、cpuset cgroup の委譲がない rootless podman(cgroup v1、またはコントローラーが委譲されていない v2)では無視されるか、拒否される場合があります。致命的な問題ではありません。コンテナー内では VLLM_CPU_OMP_THREADS_BIND による CPU スレッドのバインドが引き続き適用されます。失われるのはコンテナーレベルでのメモリ固定だけです(NUMA ローカリティが低下します)。cpuset フラグが原因でエラーになった場合は、それらを外して続行してください。起動失敗として扱わないでください。
  • HF キャッシュのマウント:デフォルトでは ~/.cache/huggingface をマウントします。HF_HOME が別の場所(共有ホストでは /proj/.../vllm など)を指している場合は、そのパスを代わりに /root/.cache/huggingface にマウントしてください。そうしないと、コンテナー内でモデルが再ダウンロードされます。
  • コンテナー名の再利用:前回の実行で残った vllm-epyc があると、run は「name already in use」というエラーで失敗します。Step 6 では、先に $RT rm -f vllm-epyc でこれを削除します。

コメント