Skip to content

Commit c96ea48

Browse files
feat: add CPU benchmark tool with context sweep
- Add benchmark tool for LLaMA/TinyLLaMA models - Support automated context length sweep (256-4096) - Add KV cache memory estimation - Include plotting script for visualization Tested on WSL2 Ubuntu 24.04 with AMD Ryzen 7 6800H
1 parent 87e5d92 commit c96ea48

3 files changed

Lines changed: 295 additions & 186 deletions

File tree

tools/mllm-llm-benchmark/README.md

Lines changed: 83 additions & 186 deletions
Original file line numberDiff line numberDiff line change
@@ -1,217 +1,114 @@
1-
# MLLM LLM Benchmark Tool
1+
# CPU Benchmark Tool
22

3-
## Overview
3+
Added a benchmark tool for measuring prefill/decode latency across different context lengths on CPU.
44

5-
This is a benchmark tool for measuring MLLM model performance, including:
6-
- **TTFT (Time To First Token)**: Time to first token latency
7-
- **Prefill Speed**: Prefill speed (tokens/s)
8-
- **Decode Speed**: Decode generation speed (tokens/s)
5+
## Why
96

10-
## Build
7+
There wasn't a unified way to benchmark mllm performance with varying context lengths. The existing benchmark tools had prefill length and decode length settings, but no automated sweep across contexts. So I put together this tool + a bash script to run sweeps automatically.
8+
9+
## Test Setup
1110

12-
Build from the mllm_v2 project root directory:
11+
Ran on my machine:
12+
- **System**: WSL2 Ubuntu 24.04
13+
- **CPU**: AMD Ryzen 7 6800H with Radeon Graphics
14+
- **Arch**: x86_64
15+
- **Threads**: 8
16+
- **Model**: TinyLLaMA fp32 (not quantized)
1317

18+
## Build
1419
```bash
15-
mkdir -p build && cd build
16-
cmake ..
17-
make mllm-llm-benchmark
20+
cmake -S . -B build -G Ninja \
21+
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
22+
-DBUILD_TESTING=OFF \
23+
-DMLLM_ENABLE_TOOLS=ON \
24+
-DCMAKE_CXX_FLAGS="-march=native"
25+
26+
cmake --build build -j$(nproc)
1827
```
1928

2029
## Usage
2130

22-
### Basic Usage
23-
31+
### Single run
2432
```bash
25-
./mllm-llm-benchmark \
26-
-n qwen3-w4a32-kai \
27-
-m /path/to/model.mllm \
28-
-c /path/to/config.json \
29-
-t 4 \
30-
-pp 64,128,256 \
31-
-tg 100,200,300 \
32-
-cl 2048
33+
./build/bin/mllm-llm-benchmark \
34+
-n tiny_llama \
35+
-m /path/to/tinyllama-fp32.mllm \
36+
-c /path/to/config_tiny_llama.json \
37+
-pp 254 -tg 2 -cl 256 -t 8
3338
```
3439

35-
### Parameters
36-
37-
| Parameter | Long Format | Description | Example |
38-
|-----------|-------------|-------------|---------|
39-
| `-n` | `--model_name` | Model name (used to select the correct benchmark implementation) | `qwen3-w4a32-kai` |
40-
| `-m` | `--model_path` | Model weight file path | `/path/to/model.mllm` |
41-
| `-c` | `--config_path` | Model configuration file path | `/path/to/config.json` |
42-
| `-t` | `--threads` | Number of CPU threads | `4` |
43-
| `-pp` | `--prompt_length` | Prompt length list (comma-separated) | `64,128,256` |
44-
| `-tg` | `--test_generation_length` | Generation length list (comma-separated, must match pp count) | `100,200,300` |
45-
| `-cl` | `--cache_length` | Maximum KV cache length | `2048` |
40+
### Context sweep
4641

47-
### Examples
42+
The script uses environment variables for configuration:
43+
```bash
44+
cd tools/mllm-llm-benchmark
45+
chmod +x scripts/sweep_context_v2.sh
4846

49-
#### Testing Qwen3-0.6B Model
47+
export BIN=../../build/bin/mllm-llm-benchmark
48+
export MODEL=/path/to/tinyllama-fp32.mllm
49+
export CFG=../../examples/llama/config_tiny_llama.json
5050

51-
```bash
52-
./mllm-llm-benchmark \
53-
-n qwen3-w4a32-kai \
54-
-m ../models/Qwen3-0.6B-w4a32kai/model.mllm \
55-
-c ../models/Qwen3-0.6B-w4a32kai/config.json \
56-
-t 4 \
57-
-pp 64,128,256 \
58-
-tg 100,100,100 \
59-
-cl 2048
51+
./scripts/sweep_context_v2.sh
6052
```
6153

62-
#### Quick Test (Single Configuration)
54+
Output goes to `bench_context/context_sweep_v2.csv`. You can tweak other params like `THREADS`, `RUNS`, `COOLDOWN` if needed - they have sensible defaults.
6355

56+
### Plot results
6457
```bash
65-
./mllm-llm-benchmark \
66-
-n qwen3-w4a32-kai \
67-
-m ../models/Qwen3-0.6B-w4a32kai/model.mllm \
68-
-c ../models/Qwen3-0.6B-w4a32kai/config.json \
69-
-t 8 \
70-
-pp 128 \
71-
-tg 128 \
72-
-cl 2048
58+
python3 scripts/plot_sweep.py bench_context/context_sweep_v2.csv snapshots/
7359
```
7460

75-
## Output Example
61+
## Benchmark Results
7662

77-
```
78-
MLLM Build Version : abc123def456
79-
ARCH : ARM64
80-
FP16 : true
81-
...
82-
83-
Create Benchmark: qwen3-w4a32-kai
84-
Model Info
85-
========== Model Information ==========
86-
Model Type : Qwen3 W4A32 KAI
87-
Hidden Size : 1024
88-
Num Layers : 28
89-
...
90-
=======================================
91-
92-
Warmup Run
93-
Warming up with 8 tokens prefill and 4 tokens generation...
94-
Warmup completed
95-
96-
========================================
97-
Starting Benchmark Tests
98-
========================================
99-
100-
----------------------------------------
101-
Test Configuration:
102-
Prompt Length (PP) : 128
103-
Generation Length (TG): 128
104-
----------------------------------------
105-
Run 1 of 3...
106-
TTFT : 902.38605 ms
107-
Prefill Speed: 141.84618 tokens/s
108-
Decode Speed : 78.11022 tokens/s
109-
Cooling down for 5 seconds...
110-
Run 2 of 3...
111-
TTFT : 911.94403 ms
112-
Prefill Speed: 140.3595 tokens/s
113-
Decode Speed : 77.60929 tokens/s
114-
Cooling down for 5 seconds...
115-
Run 3 of 3...
116-
TTFT : 923.905 ms
117-
Prefill Speed: 138.54239 tokens/s
118-
Decode Speed : 76.48289 tokens/s
119-
120-
========== Average Results ==========
121-
Configuration: PP= 128 TG= 128
122-
Average TTFT : 912.74506 ms
123-
Average Prefill Speed: 140.24936 tokens/s
124-
Average Decode Speed : 77.4008 tokens/s
125-
=====================================
126-
127-
128-
========================================
129-
Benchmark Tests Completed
130-
========================================
131-
```
63+
Tested TinyLLaMA fp32 on Ryzen 7 6800H (WSL2):
13264

133-
## Test Workflow
134-
135-
Each test configuration executes the following steps:
136-
137-
1. **Clear Cache** - Ensures each test starts from a clean state
138-
2. **Run 3 Test Rounds** - 5-second sleep between rounds to avoid overheating
139-
3. **Calculate Average** - Average results from 3 rounds
140-
4. **Output Results** - Display TTFT, Prefill Speed, Decode Speed
141-
142-
## Adding New Model Support
143-
144-
### 1. Create New Benchmark Class
145-
146-
Create `YourModel_Benchmark.hpp` in the `models/` directory:
147-
148-
```cpp
149-
#include "BenchmarkTemplate.hpp"
150-
#include <mllm/models/yourmodel/modeling_yourmodel.hpp>
151-
152-
class YourModel_Benchmark final : public BenchmarkTemplate {
153-
public:
154-
void init(const std::string& cfg_path, const std::string& model_path, int32_t cache_length) override {
155-
// Initialize your model
156-
}
157-
158-
void printModelInfo() override {
159-
// Print model information
160-
}
161-
162-
void warmup() override {
163-
// Warmup run
164-
}
165-
166-
void clear() override {
167-
// Clear KV cache
168-
}
169-
170-
BenchmarkTemplateResult run(int32_t pp, int32_t tg) override {
171-
// Run test and return results
172-
}
173-
174-
private:
175-
std::unique_ptr<YourModelConfig> config_;
176-
std::unique_ptr<YourModel> model_;
177-
};
178-
```
65+
| Context Length | TTFT (s) | Decode Latency (ms/token) | Peak RSS (GB) |
66+
|----------------|----------|---------------------------|---------------|
67+
| 256 | 8.5 | 0.68 | 4.0 |
68+
| 512 | 18.2 | 0.68 | 4.2 |
69+
| 1024 | 50.5 | 0.67 | 4.6 |
70+
| 2048 | 149.8 | 0.73 | 5.9 |
71+
| 4096 | 523.1 | 0.88 | 7.0 |
17972

180-
### 2. Register in All.hpp
181-
182-
```cpp
183-
#include "YourModel_Benchmark.hpp"
184-
185-
std::shared_ptr<BenchmarkTemplate> createBenchmark(const std::string& model_name) {
186-
auto normalized_model_name = tolower(model_name);
187-
188-
// Add your model check
189-
if (normalized_model_name.find("yourmodel") != std::string::npos) {
190-
return std::make_shared<YourModel_Benchmark>();
191-
}
192-
193-
// ... other models
194-
}
195-
```
73+
Prefill time scales roughly quadratically with context length (as expected). Decode latency per token stays pretty stable around 0.7ms. Memory usage grows linearly with context due to KV cache.
19674

197-
## Notes
75+
Note: This is running in WSL2 so performance is slightly worse than native Linux. Also using unquantized fp32 model which is slower than q4/q8 would be.
19876

199-
- Ensure sufficient memory for model loading and inference
200-
- 5-second sleep between test rounds to avoid device overheating affecting results
201-
- TTFT is in milliseconds, speed is in tokens/s
202-
- Each configuration runs 3 rounds and averages results for more stable measurements
77+
## Test Modes
20378

204-
## Troubleshooting
79+
The sweep script runs two modes for each context length:
80+
- `prefill_ttft`: Measures time to first token (prompt length = CL-2, generates 2 tokens)
81+
- `decode_heavy`: Measures decode throughput (prompt length = CL-256, generates 256 tokens)
20582

206-
### Error: Model not initialized
207-
- Check if model path is correct
208-
- Verify configuration file format is correct
83+
## Configuration
84+
85+
Environment variables for `sweep_context_v2.sh`:
86+
- `BIN`: Path to benchmark binary (required)
87+
- `MODEL`: Path to model file (required)
88+
- `CFG`: Path to config json (default: ./examples/llama/config_tiny_llama.json)
89+
- `THREADS`: Number of threads (default: 8)
90+
- `RUNS`: How many runs to average (default: 1)
91+
- `COOLDOWN`: Seconds to wait between runs (default: 0)
92+
- `CLS`: Context lengths to test (default: "256 512 1024 2048 4096")
93+
- `TG_DH`: Generate length for decode_heavy mode (default: 256)
94+
- `TG_TTFT`: Generate length for prefill_ttft mode (default: 2)
95+
- `OUTDIR`: Output directory (default: bench_context)
96+
97+
## Code Structure
98+
```
99+
tools/mllm-llm-benchmark/
100+
├── main.cpp # CLI entry point
101+
├── models/
102+
│ ├── BenchmarkTemplate.hpp # Base interface
103+
│ ├── All.hpp # Model factory
104+
│ └── Llama.hpp # LLaMA/TinyLLaMA implementation
105+
├── scripts/
106+
│ ├── sweep_context_v2.sh # Context sweep script
107+
│ └── plot_sweep.py # Plotting script
108+
```
209109

210-
### Error: Benchmark not found
211-
- Check if model name is correct (using `-n` parameter)
212-
- Confirm corresponding model registration exists in `All.hpp`
110+
The benchmark tool supports multiple models through the `BenchmarkTemplate` interface. Currently implemented:
111+
- LLaMA/TinyLLaMA
112+
- Qwen3 (w4a32 with KAI)
213113

214-
### Performance Anomalies
215-
- Check CPU thread count setting (`-t` parameter)
216-
- Confirm no other programs are consuming system resources
217-
- Check if correct quantization scheme is enabled
114+
Adding new models just requires implementing the template interface.

0 commit comments

Comments
 (0)