When llama.cpp runs a LLaMA 2 model on CPU, it can easily consume 400% of available CPU cores. Where does all that CPU time actually go? We profiled a live, unmodified llama.cpp process running the quantized 7B model with OpenResty XRay and traced the CPU usage to two dominant code paths: ggml_compute_forward_mul_mat (general matrix multiplication) and ggml_vec_dot_q4_K_q8_K (quantized dot product in k_quants.c).

This walkthrough shows the complete profiling process — from observing the high CPU usage to pinpointing the exact source file and line number responsible.

Running llama.cpp and Observing 400% CPU Usage

First, compile llama.cpp with debug symbols enabled so the profiler can resolve function names and source locations:

cd llama.cpp
make -j4 OPT="-g -O3"

Then run the main program with the quantized LLaMA 2 7B model. LLaMA 2 is an open-source large language model released by Meta. Here we use the Q4_K_M quantization variant, which reduces the 7B model to about 4 GB — small enough to run entirely on CPU via llama.cpp:

./main -m models/llama-2-7b-chat.ggmlv3.q4_K_M.bin -n 4096 -p "Linux"

The -n 4096 option specifies the number of tokens to generate, and -p "Linux" sets the prompt used to generate the content.

The process loads the model and begins generating text. Note the system_info line in the output — it shows n_threads = 4, AVX = 1, AVX2 = 1, and BLAS = 0, meaning this build uses AVX2 SIMD instructions but no external BLAS library:

llama.cpp generating text, showing model load info and system capabilities (AVX2 enabled, BLAS disabled, 4 threads)

In a separate terminal, top reveals the main process consuming nearly 400% CPU — saturating all 4 cores:

top showing llama.cpp main process at 396% CPU usage

The question is: which C++ functions inside llama.cpp are responsible for this CPU usage?

Profiling llama.cpp CPU Usage with OpenResty XRay

We use OpenResty XRay to answer that question. OpenResty XRay can attach to a running, unmodified process and generate CPU flame graphs through dynamic tracing — no recompilation or instrumentation required.

In the OpenResty XRay web console, navigate to Guided Analysis, select High CPU usage as the problem type, then choose the llama.cpp main process from the process list. The console shows it at 320.4% CPU with the full command line visible:

OpenResty XRay process list showing llama.cpp main at 320.4% CPU, with full command line and model path

Set the application type to C/C++ and leave the maximum analysis time at the default 300 seconds. After clicking Start analyzing, the system runs multiple rounds of sampling. Two rounds are sufficient for this case. Stopping the analysis triggers automatic report generation.

Findings: The Hottest C++ Code Paths

The generated report identifies three hot C++ code paths, each showing the percentage of total CPU time and the full call chain:

OpenResty XRay analysis report showing three CPU hot paths for llama.cpp: #1 at 21.1%, #2 at 17.4%, #3 at 13.4%

#1: ggml_compute_forward_mul_mat — 21.1% of CPU time

The hottest code path leads to ggml_compute_forward_mul_mat in ggml.c. This is the general matrix multiplication (GEMM) kernel in the ggml library — the core operation behind both the attention mechanism and the feed-forward layers in transformer inference. Its caller, ggml_graph_compute_thread, is responsible for performing the computation in a single thread:

The #1 hottest C++ code path highlighted: ggml_compute_forward_mul_mat at 21.1%, called from ggml_graph_compute_thread

Clicking More expands the details, revealing the CPU flame graph that this code path was derived from. The flame graph gives a visual representation of the entire call stack, with wider bars indicating more CPU time:

CPU flame graph for llama.cpp showing ggml_compute_forward_mul_mat as the widest bar at the bottom of the call stack

OpenResty XRay also generates an automated explanation and optimization suggestions for this code path. It identifies that ggml_compute_forward_mul_mat computes the matrix multiplication of two tensors, and suggests strategies including parallelization, memory access optimization, and using optimized libraries like BLAS or LAPACK:

Automated explanation and optimization suggestions: parallelization, memory access optimization, algorithmic optimization, and BLAS/LAPACK library usage

The BLAS suggestion is particularly relevant here — recall that the system_info output showed BLAS = 0, meaning llama.cpp is using its own GEMM implementation rather than an optimized BLAS library. Enabling OpenBLAS or Intel MKL at compile time could significantly reduce this hot path’s CPU share.

#2: ggml_vec_dot_q4_K_q8_K — 17.4% of CPU time

The second hottest path leads to ggml_vec_dot_q4_K_q8_K in k_quants.c:

The #2 hottest C++ code path highlighted: ggml_vec_dot_q4_K_q8_K at 17.4%, with the #3 path at 13.4% below

This function computes the dot product of two quantized vectors — K in 4-bit quantization (q4_K) and Q in 8-bit quantization (q8_K). Quantized dot products are how llama.cpp achieves the speed and memory savings that make running a 7B model on CPU feasible in the first place:

Explanation of #2 code path: ggml_vec_dot_q4_K_q8_K computes the dot product of quantized vectors K and Q, defined in k_quants.c

From profiling report to source code

OpenResty XRay maps each hot function back to its source file and line number. Hovering over the ggml_vec_dot_q4_K_q8_K function box in the report reveals a tooltip showing the exact location: llama.cpp/k_quants.c, line 2608:

Tooltip showing source location: File: llama.cpp/k_quants.c, Line: 2608

Opening k_quants.c and jumping to line 2608 as shown in the report tooltip:

Source code at k_quants.c:2608, inside the #elif defined AVX2 branch, with the hot line marked

We can see that this line of code is using bitwise operations in C to perform some operations on the elements of an array — unpacking quantized scale factors from the packed 12-byte scales representation:

The hot line at k_quants.c:2608 highlighted: bitwise operations on the utmp array elements

We see in the status bar that this line is in the function ggml_vec_dot_q4_K_q8_K, as shown in the report:

The editor status area highlighting the enclosing function ggml_vec_dot_q4_K_q8_K()

This level of precision — from a running process to the exact source line consuming CPU — is what makes dynamic-tracing profilers valuable for understanding where CPU time goes in compute-intensive applications like LLM inference.

Frequently Asked Questions

Why does llama.cpp use so much CPU?

llama.cpp performs inference on large language models primarily through matrix multiplications and quantized vector dot products. In our profiling of a LLaMA 2 7B run, the top CPU consumer was ggml_compute_forward_mul_mat — the general matrix multiplication kernel in the ggml library — followed by ggml_vec_dot_q4_K_q8_K, which computes the dot product of two quantized vectors. These operations are inherently compute-intensive; running inference on CPU easily reaches 400% utilization across multiple cores.

How can I profile llama.cpp CPU usage?

One approach is to use a dynamic-tracing profiler like OpenResty XRay, which can attach to a running, unmodified llama.cpp process and generate CPU flame graphs. The flame graph reveals exactly which C++ functions consume the most CPU time — down to the source file and line number — without modifying or instrumenting the running application.

What are the hottest C++ functions in llama.cpp?

In our analysis of llama.cpp running the quantized LLaMA 2 7B model, the #1 hottest code path was ggml_compute_forward_mul_mat (21.1% of CPU time), called from ggml_graph_compute_thread. The #2 path was ggml_vec_dot_q4_K_q8_K (17.4%) in k_quants.c (line 2608), performing bitwise operations on quantized array elements. Together with the #3 path (13.4%), these three code paths account for over half of all CPU time.

What is OpenResty XRay

OpenResty XRay is a dynamic-tracing product that automatically analyzes your running applications to troubleshoot performance problems, behavioral issues, and security vulnerabilities with actionable suggestions. Under the hood, OpenResty XRay is powered by our Y language targeting various runtimes like Stap+, eBPF+, GDB, and ODB, depending on the contexts.

If you like this tutorial, please subscribe to this blog site and/or our YouTube channel. Thank you!

About The Author

Yichun Zhang (Github handle: agentzh), is the original creator of the OpenResty® open-source project and the CEO of OpenResty Inc..

Yichun is one of the earliest advocates and leaders of “open-source technology”. He worked at many internationally renowned tech companies, such as Cloudflare, Yahoo!. He is a pioneer of “edge computing”, “dynamic tracing” and “machine coding”, with over 22 years of programming and 16 years of open source experience. Yichun is well-known in the open-source space as the project leader of OpenResty®, adopted by more than 40 million global website domains.

OpenResty Inc., the enterprise software start-up founded by Yichun in 2017, has customers from some of the biggest companies in the world. Its flagship product, OpenResty XRay, is a non-invasive profiling and troubleshooting tool that significantly enhances and utilizes dynamic tracing technology. And its OpenResty Edge product is a powerful distributed traffic management and private CDN software product.

As an avid open-source contributor, Yichun has contributed more than a million lines of code to numerous open-source projects, including Linux kernel, Nginx, LuaJIT, GDB, SystemTap, LLVM, Perl, etc. He has also authored more than 60 open-source software libraries.