llama.cpp の CPU 使用率:400% の CPU 消費を ggml のホットな関数まで追跡
llama.cpp が CPU 上で LLaMA 2 モデルを実行すると、CPU コアの 400% を簡単に消費してしまいます。その CPU 時間は実際にどこで使われているのでしょうか?量子化された 7B モデルを実行中の、修正を加えていない llama.cpp プロセスを OpenResty XRay でプロファイリングした結果、CPU 使用率は 2 つの支配的なコードパスに集中していることが判明しました:ggml_compute_forward_mul_mat(汎用行列乗算)と ggml_vec_dot_q4_K_q8_K(k_quants.c 内の量子化ベクトルの内積)です。
本記事では、高い CPU 使用率の観測から、その原因となるソースファイルと行番号の特定まで、プロファイリングの全プロセスを紹介します。
llama.cpp を実行して 400% の CPU 使用率を観測
まず、プロファイラが関数名とソースコードの位置を解決できるように、デバッグシンボルを有効にして llama.cpp をコンパイルします:
cd llama.cpp
make -j4 OPT="-g -O3"
次に、量子化された LLaMA 2 7B モデルを指定して main プログラムを実行します。LLaMA 2 は Meta が公開したオープンソースの大規模言語モデルです。ここでは Q4_K_M 量子化バリアントを使用しており、7B モデルを約 4 GB まで圧縮できます。これにより、llama.cpp で CPU のみでの実行が可能になります:
./main -m models/llama-2-7b-chat.ggmlv3.q4_K_M.bin -n 4096 -p "Linux"
-n 4096 オプションは生成する token 数を指定し、-p "Linux" はコンテンツ生成に使用するプロンプトを指定します。
プロセスはモデルをロードし、テキストの生成を開始します。出力の system_info 行にご注目ください。n_threads = 4、AVX = 1、AVX2 = 1、BLAS = 0 と表示されており、このビルドが AVX2 SIMD 命令を使用している一方、外部 BLAS ライブラリは使用していないことがわかります:
別のターミナルで top を実行すると、main プロセスが 400% 近い CPU を消費し、4 つのコアすべてを飽和させていることがわかります:
問題は、llama.cpp 内部のどの C++ 関数がこの CPU 使用率の原因になっているのか、ということです。
OpenResty XRay による llama.cpp の CPU 使用率のプロファイリング
その疑問に答えるために OpenResty XRay を使用します。OpenResty XRay は動的トレーシングによって、実行中の修正されていないプロセスにアタッチし、CPU フレームグラフを生成できます。再コンパイルもインストルメンテーションも不要です。
OpenResty XRay の Web コンソールで「Guided Analysis」ページに移動し、問題タイプとして「High CPU usage」を選択してから、プロセスリストで llama.cpp の main プロセスを選択します。コンソールには CPU 使用率 320.4% と、完全なコマンドラインが表示されています:
アプリケーションタイプを「C/C++」に設定し、最大分析時間はデフォルトの 300 秒のままにします。「Start analyzing」をクリックすると、システムは複数ラウンドのサンプリングを実行します。このケースでは 2 ラウンドで十分です。分析を停止すると、レポートが自動的に生成されます。
分析結果:最もホットな C++ コードパス
生成されたレポートは 3 つのホットな C++ コードパスを特定し、それぞれについて総 CPU 時間に占める割合と完全な呼び出しチェーンを表示します:
第 1 位:ggml_compute_forward_mul_mat — CPU 時間の 21.1%
最もホットなコードパスは ggml.c 内の ggml_compute_forward_mul_mat に行き着きます。これは ggml ライブラリの汎用行列乗算(GEMM)カーネルであり、Transformer 推論におけるアテンション機構とフィードフォワード層の両方を支える中核的な演算です。その呼び出し元の関数 ggml_graph_compute_thread は、単一スレッドでの計算を担当しています:
「More」をクリックして詳細を展開すると、このコードパスの導出元となった CPU フレームグラフが表示されます。フレームグラフは呼び出しスタック全体を可視化したもので、バーの幅が広いほど多くの CPU 時間を消費していることを表します:
OpenResty XRay は、このコードパスに関する自動生成された説明と最適化の提案も提供します。ggml_compute_forward_mul_mat が 2 つのテンソルの行列乗算を計算していることを特定し、並列化、メモリアクセスの最適化、BLAS や LAPACK などの最適化済みライブラリの利用といった戦略を提案しています:
なかでも BLAS の提案は特に的を射ています。先ほどの system_info 出力に BLAS = 0 とあったことを思い出してください。これは llama.cpp が最適化された BLAS ライブラリではなく、独自の GEMM 実装を使用していることを意味します。コンパイル時に OpenBLAS や Intel MKL を有効にすれば、このホットパスの CPU 占有率を大幅に削減できる可能性があります。
第 2 位:ggml_vec_dot_q4_K_q8_K — CPU 時間の 17.4%
2 番目にホットなパスは k_quants.c 内の ggml_vec_dot_q4_K_q8_K に行き着きます:
この関数は 2 つの量子化ベクトルの内積を計算します。K は 4 ビット量子化(q4_K)、Q は 8 ビット量子化(q8_K)です。量子化された内積こそが、llama.cpp が高速化とメモリ節約を実現し、そもそも 7B モデルを CPU 上で実行可能にしている仕組みです:
プロファイリングレポートからソースコードへ
OpenResty XRay は各ホット関数を、そのソースファイルと行番号にマッピングします。レポート内の ggml_vec_dot_q4_K_q8_K の関数ボックスにマウスカーソルを合わせると、ツールチップに正確な位置が表示されます:llama.cpp/k_quants.c の 2608 行目です:
k_quants.c を開き、レポートのツールチップで示されたとおり 2608 行目に移動します:
この行のコードは、C 言語のビット演算を使用して配列の要素に対する操作を行っていることが確認できます。12 バイトにパックされた scales 表現から量子化スケール係数を取り出す処理です:
ステータスバーでは、この行がレポートで示されたとおり ggml_vec_dot_q4_K_q8_K 関数内にあることも確認できます:
実行中のプロセスから CPU を消費している正確なソース行まで到達できるこの精度こそが、LLM 推論のような計算集約型アプリケーションの CPU 時間の内訳を把握するうえで、動的トレーシング型プロファイラが価値を発揮する理由です。
よくある質問
llama.cpp はなぜこれほど多くの CPU を消費するのですか?
llama.cpp における大規模言語モデルの推論は、主に行列乗算と量子化ベクトルの内積によって行われます。LLaMA 2 7B の実行をプロファイリングした結果、CPU 消費の第 1 位は ggml ライブラリの汎用行列乗算カーネルである ggml_compute_forward_mul_mat で、第 2 位は 2 つの量子化ベクトルの内積を計算する ggml_vec_dot_q4_K_q8_K でした。これらの演算は本質的に計算集約型であり、CPU 上での推論は複数コアにまたがって容易に 400% の使用率に達します。
llama.cpp の CPU 使用率をプロファイリングするにはどうすればよいですか?
1 つの方法は、OpenResty XRay のような動的トレーシングベースのプロファイラを使用することです。実行中の修正されていない llama.cpp プロセスにアタッチし、CPU フレームグラフを生成できます。フレームグラフは、どの C++ 関数が最も多くの CPU 時間を消費しているかを、ソースファイルと行番号のレベルまで正確に明らかにします。実行中のアプリケーションを修正したりインストルメンテーションを加えたりする必要はありません。
llama.cpp で最もホットな C++ 関数は何ですか?
量子化された LLaMA 2 7B モデルを実行する llama.cpp を分析した結果、最もホットなコードパスの第 1 位は ggml_graph_compute_thread から呼び出される ggml_compute_forward_mul_mat(CPU 時間の 21.1%)でした。第 2 位は k_quants.c(2608 行目)の ggml_vec_dot_q4_K_q8_K(17.4%)で、量子化配列の要素に対するビット演算を行っています。第 3 位のパス(13.4%)と合わせると、これら 3 つのコードパスだけで全 CPU 時間の半分以上を占めています。
OpenResty XRay について
OpenResty XRay は動的トレーシング製品であり、実行中のアプリケーションを自動的に分析して、パフォーマンスの問題、動作の問題、セキュリティの脆弱性を診断し、実行可能な提案を提供します。基盤となる実装において、OpenResty XRay は弊社の Y 言語によって駆動され、Stap+、eBPF+、GDB、ODB など、様々な環境下で複数の異なるランタイムをサポートしております。
本チュートリアルがお役に立ちましたら、当ブログおよび YouTube チャンネルのご購読をよろしくお願いいたします。ありがとうございます!
著者について
章亦春(Zhang Yichun)は、オープンソースの OpenResty® プロジェクトの創始者であり、OpenResty Inc. の CEO および創業者です。
章亦春(GitHub ID: agentzh)は中国江蘇省生まれで、現在は米国ベイエリアに在住しております。彼は中国における初期のオープンソース技術と文化の提唱者およびリーダーの一人であり、Cloudflare、Yahoo!、Alibaba など、国際的に有名なハイテク企業に勤務した経験があります。「エッジコンピューティング」、「動的トレーシング」、「機械プログラミング」 の先駆者であり、22 年以上のプログラミング経験と 16 年以上のオープンソース経験を持っております。世界中で 4000 万以上のドメイン名を持つユーザーを抱えるオープンソースプロジェクトのリーダーとして、彼は OpenResty® オープンソースプロジェクトをベースに、米国シリコンバレーの中心部にハイテク企業 OpenResty Inc. を設立いたしました。同社の主力製品である OpenResty XRay(動的トレーシング技術を利用した非侵入型のプロファイリングおよびトラブルシューティングツール)と OpenResty Edge(マイクロサービスおよび分散トラフィックに最適化された多機能ゲートウェイソフトウェア)は、世界中の多くの上場企業および大企業から高い評価を得ております。OpenResty 以外にも、章亦春は Linux カーネル、Nginx、LuaJIT、GDB、SystemTap、LLVM、Perl など、複数のオープンソースプロジェクトに累計 100 万行以上のコードを寄与し、60 以上のオープンソースソフトウェアライブラリを執筆しております。
翻訳
英語版の原文と日本語訳版(本文)をご用意しております。読者の皆様による他の言語への翻訳版も歓迎いたします。全文翻訳で省略がなければ、採用を検討させていただきます。心より感謝申し上げます!































