Skip to content

Commit 1cb7439

Browse files
tools/mllm-llm-benchmark: add llama benchmark template
1 parent fe6c481 commit 1cb7439

2 files changed

Lines changed: 154 additions & 5 deletions

File tree

tools/mllm-llm-benchmark/models/All.hpp

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,35 @@
44

55
#include <memory>
66
#include <algorithm>
7+
#include <string>
8+
#include <cctype> // for std::tolower
79

8-
#include "Qwen3_W4A32_KAI.hpp"
910
#include "BenchmarkTemplate.hpp"
11+
#include "Qwen3_W4A32_KAI.hpp"
12+
#include "Llama.hpp"
1013

11-
std::shared_ptr<BenchmarkTemplate> createBenchmark(const std::string& model_name) {
14+
inline std::shared_ptr<BenchmarkTemplate> createBenchmark(const std::string& model_name) {
1215
auto tolower = [](const std::string& str) {
1316
std::string result = str;
14-
std::transform(result.begin(), result.end(), result.begin(), ::tolower);
17+
// NOTE: std::tolower expects unsigned char cast to avoid UB for negative char values.
18+
std::transform(result.begin(), result.end(), result.begin(),
19+
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
1520
return result;
1621
};
22+
1723
auto normalized_model_name = tolower(model_name);
18-
if (normalized_model_name.find("qwen3") != std::string::npos && normalized_model_name.find("w4a32") != std::string::npos
19-
&& normalized_model_name.find("kai") != std::string::npos) {
24+
25+
if (normalized_model_name.find("qwen3") != std::string::npos &&
26+
normalized_model_name.find("w4a32") != std::string::npos &&
27+
normalized_model_name.find("kai") != std::string::npos) {
2028
return std::make_shared<Qwen3_W4A32_KAI_Benchmark>();
2129
}
30+
31+
if (normalized_model_name.find("llama") != std::string::npos ||
32+
normalized_model_name.find("tinyllama") != std::string::npos ||
33+
normalized_model_name.find("tiny_llama") != std::string::npos) {
34+
return std::make_shared<Llama_Benchmark>();
35+
}
36+
2237
return nullptr;
2338
}
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// Copyright (c) MLLM Team.
2+
// Licensed under the MIT License.
3+
#pragma once
4+
5+
#include <memory>
6+
#include <chrono>
7+
#include <string>
8+
9+
#include "BenchmarkTemplate.hpp"
10+
11+
#include <mllm/mllm.hpp>
12+
#include <mllm/models/llama/modeling_llama.hpp>
13+
#include <mllm/models/llama/configuration_llama.hpp>
14+
15+
class Llama_Benchmark final : public BenchmarkTemplate {
16+
public:
17+
void init(const std::string& cfg_path, const std::string& model_path, int32_t cache_length) override {
18+
cfg_ = std::make_unique<mllm::models::llama::LLaMAConfig>(cfg_path);
19+
20+
// LLaMA config uses max_position_embeddings as KV-cache upper bound
21+
if (cache_length > 0) {
22+
cfg_->max_position_embeddings = cache_length;
23+
}
24+
25+
model_ = std::make_unique<mllm::models::llama::LlamaForCausalLM>("", *cfg_);
26+
27+
// NOTE:
28+
// tinyllama-fp32.mllm used in examples is a V1 parameter file.
29+
// Loading it as V2 will assert on magic number mismatch.
30+
// We keep V1-only here to make the benchmark runnable; V2 support can be added later
31+
// once we have either:
32+
// (1) a reliable file-version probe, or
33+
// (2) a CLI flag to select model file version.
34+
auto param = mllm::load(model_path, mllm::ModelFileVersion::kV1);
35+
model_->load(param);
36+
37+
mllm::print("Model initialized successfully");
38+
}
39+
40+
void printModelInfo() override {
41+
if (!cfg_) return;
42+
mllm::print("========== Model Information ==========");
43+
mllm::print("Model Type : LLaMA / TinyLlama");
44+
mllm::print("Hidden Size :", cfg_->hidden_size);
45+
mllm::print("Num Layers :", cfg_->num_hidden_layers);
46+
mllm::print("Num Heads :", cfg_->num_attention_heads);
47+
mllm::print("Num KV Heads :", cfg_->num_key_value_heads);
48+
// NOTE: Defensive guard (shouldn't happen with valid configs, but keeps benchmark robust).
49+
int32_t head_dim = (cfg_->num_attention_heads > 0) ? (cfg_->hidden_size / cfg_->num_attention_heads) : 0;
50+
mllm::print("Head Dim :", head_dim);
51+
mllm::print("Intermediate Size :", cfg_->intermediate_size);
52+
mllm::print("Vocab Size :", cfg_->vocab_size);
53+
mllm::print("Max Pos Embeddings :", cfg_->max_position_embeddings);
54+
mllm::print("=======================================");
55+
}
56+
57+
void warmup() override {
58+
if (!model_) return;
59+
60+
const int32_t warmup_length = 8;
61+
const int32_t warmup_gen = 4;
62+
63+
auto input_ids = mllm::Tensor::empty({1, warmup_length}, mllm::kInt64, mllm::kCPU)
64+
.setMemType(mllm::kNormal)
65+
.alloc();
66+
auto ptr = input_ids.ptr<mllm::mllm_int64_t>();
67+
for (int i = 0; i < warmup_length; ++i) ptr[i] = 1;
68+
69+
mllm::models::ARGenerationOutputPast inputs;
70+
inputs["sequence"] = input_ids;
71+
72+
mllm::models::ARGenerationArgs args;
73+
args["max_length"] = mllm::AnyValue((int)warmup_gen);
74+
args["do_sample"] = mllm::AnyValue(false);
75+
76+
model_->generate(inputs, args);
77+
mllm::print("Warmup completed");
78+
}
79+
80+
void clear() override {
81+
// TODO: expose a public KV-cache reset API for LlamaForCausalLM (if needed).
82+
// For now, keep it as no-op to minimize API changes in PR1.
83+
}
84+
85+
BenchmarkTemplateResult run(int32_t pp, int32_t tg) override {
86+
if (!model_) return {0.f, 0.f, 0.f};
87+
88+
auto input_ids = mllm::Tensor::empty({1, pp}, mllm::kInt64, mllm::kCPU)
89+
.setMemType(mllm::kNormal)
90+
.alloc();
91+
auto ptr = input_ids.ptr<mllm::mllm_int64_t>();
92+
for (int i = 0; i < pp; ++i) ptr[i] = 1 + (i % 100);
93+
94+
mllm::models::ARGenerationOutputPast inputs;
95+
inputs["sequence"] = input_ids;
96+
97+
mllm::models::ARGenerationArgs args;
98+
args["max_length"] = mllm::AnyValue((int)tg);
99+
args["do_sample"] = mllm::AnyValue(false);
100+
101+
auto prefill_start = std::chrono::high_resolution_clock::now();
102+
auto decode_start = prefill_start;
103+
auto decode_end = prefill_start;
104+
105+
bool first_token = true;
106+
int token_count = 0;
107+
108+
model_->streamGenerate(inputs, args, [&](int64_t /*token_id*/) {
109+
if (first_token) {
110+
decode_start = std::chrono::high_resolution_clock::now();
111+
first_token = false;
112+
}
113+
token_count++;
114+
decode_end = std::chrono::high_resolution_clock::now();
115+
});
116+
117+
auto prefill_us = std::chrono::duration_cast<std::chrono::microseconds>(decode_start - prefill_start).count();
118+
auto decode_us = std::chrono::duration_cast<std::chrono::microseconds>(decode_end - decode_start).count();
119+
120+
BenchmarkTemplateResult r;
121+
r.ttft = prefill_us / 1000.0f;
122+
r.prefill_speed = (prefill_us > 0) ? (static_cast<float>(pp) / prefill_us) * 1e6f : 0.f;
123+
// NOTE: decode_us is measured from first token timestamp; exclude that first token from decode throughput.
124+
int decode_tokens = (token_count > 0) ? (token_count - 1) : 0;
125+
r.decode_speed = (decode_us > 0 && decode_tokens > 0)
126+
? (static_cast<float>(decode_tokens) / decode_us) * 1e6f
127+
: 0.f;
128+
return r;
129+
}
130+
131+
private:
132+
std::unique_ptr<mllm::models::llama::LLaMAConfig> cfg_;
133+
std::unique_ptr<mllm::models::llama::LlamaForCausalLM> model_;
134+
};

0 commit comments

Comments
 (0)