文章目录
团队博客: 汽车电子社区
1. 模块概述
examples/ 目录是 llama.cpp 项目的示例程序集合,为开发者提供了从入门到专业级别的完整学习资源。该目录包含了50多个示例程序,涵盖了基础推理、高级优化、生产部署、研究实验等多个维度,是学习LLM推理技术的宝贵资源库。
1.1 核心定位
- 教学资源:从入门到专业的系统化学习路径
- 参考实现:生产就绪的代码示例和最佳实践
- 功能演示:展示llama.cpp的全部特性和能力
- 开发模板:可直接复用的代码模板和架构模式
1.2 设计目标
- 渐进式学习:从简单到复杂的学习曲线
- 完整性:每个示例都是可独立运行的完整程序
- 实用性:提供可直接用于生产的代码实现
- 前沿性:包含最新的研究成果和技术创新
2. 整体架构设计
2.1 目录组织结构
examples/
├── 核心推理示例 (Core Inference)
│ ├── simple/ # 基础推理示例 - 入门教程
│ ├── simple-chat/ # 简单聊天示例 - 交互式应用
│ ├── batched/ # 批处理推理 - 性能优化
│ ├── speculative/ # 投机解码 - 高级技术
│ ├── speculative-simple/ # 简化投机解码
│ ├── parallel/ # 并行推理 - 并发处理
│ └── eval-callback/ # 评估回调 - 底层理解
├── 专用功能示例 (Specialized Features)
│ ├── embedding/ # 文本嵌入 - 向量化服务
│ ├── retrieval/ # 检索增强 - RAG技术
│ ├── passkey/ # 长上下文测试
│ ├── lookahead/ # 前瞻解码技术
│ └── infill/ # 代码填充 - 补全功能
├── 工具和实用程序 (Tools & Utilities)
│ ├── save-load-state/ # 状态保存和加载
│ ├── idle/ # 空闲性能测试
│ ├── gguf/ # GGUF格式操作
│ ├── gguf-hash/ # GGUF文件哈希
│ ├── lookup/ # 查找表操作
│ └── perplexity/ # 困惑度计算 (tools/)
├── 高级特性示例 (Advanced Features)
│ ├── diffusion/ # 扩散模型支持
│ ├── training/ # 模型训练技术
│ ├── model-conversion/ # 模型格式转换
│ └── llm-bench/ # 性能基准测试
├── 跨平台示例 (Cross-Platform)
│ ├── llama.android/ # Android平台支持
│ ├── llama.swiftui/ # Swift UI支持
│ ├── batched.swift/ # Swift批处理
│ └── sycl/ # SYCL加速支持
├── 开发工具 (Development Tools)
│ ├── deprecation-warning/ # 废弃警告处理
│ ├── gen-docs/ # 文档生成工具
│ └── simple-cmake-pkg/ # CMake包示例
└── Python脚本工具 (Python Tools)
├── json_schema_to_grammar.py # JSON Schema转语法约束
├── pydantic_models_to_grammar.py # Pydantic模型转语法
├── convert_legacy_llama.py # 遗留格式转换
└── regex_to_grammar.py # 正则表达式转语法
2.2 构建系统集成
# examples/CMakeLists.txt 核心配置
function(add_llama_example target_name source)
add_executable(${target_name} ${source})
# 依赖管理
target_link_libraries(${target_name} PRIVATE common llama ${CMAKE_THREAD_LIBS_INIT})
# 动态后端支持
if (BUILD_SHARED_LIBS)
set_target_properties(${target_name} PROPERTIES
COMPILE_DEFINITIONS "GGML_DL")
target_link_libraries(${target_name} PRIVATE ${CMAKE_DL_LIBS})
endif()
# GPU层配置
if (LLAMA_GPU)
target_link_libraries(${target_name} PRIVATE ggml-cpu ggml-base)
endif()
endfunction()
# 核心示例构建
add_llama_example(simple simple/simple.cpp)
add_llama_example(batched batched/batched.cpp)
add_llama_example(embedding embedding/embedding.cpp)
add_llama_example(speculative speculative/speculative.cpp)
# 条件构建 (EMSCRIPTEN平台支持)
if (NOT EMSCRIPTEN)
add_llama_example(save-load-state save-load-state/save-load-state.cpp)
endif()
# SYCL后端条件构建
if (GGML_SYCL)
add_subdirectory(sycl)
endif()
3. 核心示例程序深度分析
3.1 Simple 示例 - 入门级教程
3.1.1. 概述
simple/ 是 llama.cpp 的入门示例,展示了最基础但完整的推理流程。仅221行代码,却涵盖了模型加载、分词、推理、生成的全过程。
3.1.2. 核心代码结构
// main函数 - 完整的推理流程
int main(int argc, char ** argv) {
// 1. 参数解析
common_params params;
if (!common_params_parse(argc, argv, params, LLAMA_EXAMPLE_COMMON)) {
return 1;
}
// 2. 初始化后端
common_init();
llama_backend_init();
llama_numa_init(params.numa);
// 3. 加载模型
llama_model * model = llama_load_model_from_file(params.model.c_str(),
common_model_params_to_llama(params));
// 4. 创建上下文
llama_context * ctx = llama_new_context_with_model(model,
common_context_params_to_llama(params));
// 5. 分词输入
std::vector<llama_token> tokens_list = common_tokenize(ctx, params.prompt, true);
// 6. 批处理设置
llama_batch batch = llama_batch_init(tokens_list.size(), 0, 1);
// 7. 推理循环
for (int32_t i = 0; i < params.n_predict; i++) {
// 清空KV缓存(对于第一个token)
if (i == 0) {
llama_kv_cache_clear(ctx);
}
// 设置批处理
llama_batch_set_batch(batch, tokens_list.data(), tokens_list.size(), 0, false);
// 执行推理
llama_decode(ctx, batch);
// 获取logits
llama_token new_token_id = common_sampler_sample(smpl, ctx, -1);
// 输出结果
printf("%s", common_token_to_piece(ctx, new_token_id).c_str());
fflush(stdout);
// 添加到序列
tokens_list.push_back(new_token_id);
// 检查结束条件
if (new_token_id == llama_token_eos(model) ||
tokens_list.size() > params.n_ctx) {
break;
}
}
// 8. 清理资源
llama_batch_free(batch);
llama_free(ctx);
llama_free(model);
llama_backend_free();
return 0;
}
3.1.3. 教学价值分析
1. 概念清晰性 ⭐⭐⭐⭐⭐
- 展示了LLM推理的完整生命周期
- 每个步骤都有详细注释
- 错误处理完整且易懂
2. API使用规范 ⭐⭐⭐⭐⭐
- 演示了标准API调用序列
- 展示了资源管理的最佳实践
- 包含性能监控代码
3. 扩展性指导 ⭐⭐⭐⭐
- 为复杂应用提供了基础模板
- 易于修改和扩展
- 支持多种采样策略
3.1.4. 性能优化技巧
// 时间测量RAII类
struct simple_time_meas {
int64_t & t_acc;
int64_t t_start_us;
simple_time_meas(int64_t & t_acc) : t_acc(t_acc) {
t_start_us = ggml_time_us();
}
~simple_time_meas() {
t_acc += ggml_time_us() - t_start_us;
}
};
// 性能统计输出
printf("llama_print_timings:\n");
llama_print_timings(ctx);
printf("\n");
3.2 Batched 示例 - 批处理优化
3.2.1. 技术亮点
batched/ 示例展示了如何高效处理多个并发生成请求,是生产环境中的关键优化技术。
3.2.2. 核心架构设计
// 批处理管理器
struct batch_manager {
int32_t n_parallel = 1; // 并行序列数
std::vector<std::vector<llama_token>> sequences; // 多个输入序列
std::vector<bool> is_finished; // 完成状态
std::vector<llama_pos> positions; // 位置编码
// 初始化批处理
void initialize(const common_params & params) {
n_parallel = params.n_parallel;
sequences.resize(n_parallel);
is_finished.resize(n_parallel, false);
positions.resize(n_parallel, 0);
}
// 构建批处理对象
llama_batch build_batch() {
int total_tokens = 0;
for (const auto & seq : sequences) {
total_tokens += seq.size();
}
llama_batch batch = llama_batch_init(total_tokens, 0, n_parallel);
int token_idx = 0;
for (int i = 0; i < n_parallel; ++i) {
if (is_finished[i]) continue;
for (size_t j = 0; j < sequences[i].size(); ++j) {
batch.token[token_idx] = sequences[i][j];
batch.pos[token_idx] = positions[i] + j;
batch.n_seq_id[token_idx] = 1;
batch.seq_id[token_idx][0] = i;
batch.logits[token_idx] = (j == sequences[i].size() - 1) ? 1 : 0;
token_idx++;
}
}
batch.n_tokens = token_idx;
return batch;
}
};
3.2.3. 性能优化策略
3.2.3.1. 内存复用
// 重用KV缓存
if (params.kv_unified) {
// 统一KV缓存,所有序列共享
llama_kv_cache_set_unified(ctx, true);
}
3.2.3.2. 动态批处理
// 自适应批大小
if (tokens_generated % 100 == 0) {
// 定期评估性能并调整批大小
evaluate_and_adjust_batch_size();
}
3.2.3.3. 并行化处理
// 多线程处理序列准备
#pragma omp parallel for
for (int i = 0; i < n_parallel; ++i) {
prepare_sequence_data(i);
}
3.2.4. 应用场景
- API服务器:处理多个并发请求
- 批量处理:大批量文本生成任务
- 数据管道:ETL处理中的文本生成
3.3 Embedding 示例 - 向量化服务
3.3.1. 功能完整性
embedding/ 示例提供了完整的文本向量化解决方案,支持多种池化方式和归一化策略。
3.3.2. 核心功能实现
// 嵌入配置
struct embedding_config {
enum pooling_type {
POOLING_NONE = 0,
POOLING_MEAN = 1,
POOLING_CLS = 2,
POOLING_LAST = 3,
POOLING_RANK = 4
} pooling = POOLING_MEAN;
enum normalization_type {
NORM_NONE = 0,
NORM_L1 = 1,
NORM_L2 = 2,
NORM_MAX = 3,
NORM_P_NORM = 4
} normalization = NORM_L2;
float p_norm = 2.0f; // p-norm参数
bool concatenate = false; // 是否拼接多层数据
int n_embd_target = -1; // 目标嵌入维度
};
// 向量化处理
std::vector<float> compute_embedding(llama_context * ctx,
const std::vector<llama_token> & tokens,
const embedding_config & config) {
// 1. 准备输入批处理
llama_batch batch = llama_batch_init(tokens.size(), 0, 1);
for (size_t i = 0; i < tokens.size(); ++i) {
llama_batch_add_seq(batch, tokens[i], i, i == tokens.size() - 1);
}
// 2. 执行推理
llama_decode(ctx, batch);
// 3. 提取隐藏状态
const float * hidden_states = llama_get_embeddings(ctx);
// 4. 应用池化策略
std::vector<float> pooled_embedding = apply_pooling(hidden_states, tokens.size(), config);
// 5. 应用归一化
apply_normalization(pooled_embedding, config);
return pooled_embedding;
}
3.3.3. 高级特性支持
3.3.3.1. 重排序(Reranking)
// 余弦相似度计算
float cosine_similarity(const std::vector<float> & vec1,
const std::vector<float> & vec2) {
float dot_product = 0.0f;
float norm1 = 0.0f, norm2 = 0.0f;
for (size_t i = 0; i < vec1.size(); ++i) {
dot_product += vec1[i] * vec2[i];
norm1 += vec1[i] * vec1[i];
norm2 += vec2[i] * vec2[i];
}
return dot_product / (sqrt(norm1) * sqrt(norm2));
}
3.3.3.2. 分类任务支持
// 文本分类
int classify_text(const std::vector<float> & embedding,
const std::vector<std::vector<float>> & class_prototypes) {
int best_class = 0;
float best_similarity = -INFINITY;
for (int i = 0; i < class_prototypes.size(); ++i) {
float similarity = cosine_similarity(embedding, class_prototypes[i]);
if (similarity > best_similarity) {
best_similarity = similarity;
best_class = i;
}
}
return best_class;
}
3.3.3.3. 输出格式支持
// 多种输出格式
enum output_format {
FORMAT_JSON = 0,
FORMAT_ARRAY = 1,
FORMAT_RAW = 2,
FORMAT_BINARY = 3
};
void output_embedding(const std::vector<float> & embedding,
const std::string & input_text,
output_format format) {
switch (format) {
case FORMAT_JSON:
{
nlohmann::json result;
result["text"] = input_text;
result["embedding"] = embedding;
std::cout << result.dump() << std::endl;
}
break;
case FORMAT_ARRAY:
std::cout << "[";
for (size_t i = 0; i < embedding.size(); ++i) {
if (i > 0) std::cout << ", ";
std::cout << std::fixed << std::setprecision(6) << embedding[i];
}
std::cout << "]" << std::endl;
break;
case FORMAT_RAW:
std::cout.write(reinterpret_cast<const char*>(embedding.data()),
embedding.size() * sizeof(float));
break;
}
}
3.4 Speculative 示例 - 投机解码技术
3.4.1. 技术原理
投机解码是一种创新的推理加速技术,通过使用小模型(draft)预测token,然后用大模型(target)验证,显著提高生成速度。
3.4.2. 架构设计
// 投机解码管理器
struct speculative_decoder {
llama_model * model_target; // 目标模型(大模型)
llama_model * model_draft; // 草稿模型(小模型)
llama_context * ctx_target;
llama_context * ctx_draft;
// 投机参数
int n_draft = 4; // 每次推测的token数
float p_accept = 0.5f; // 接受概率阈值
// 性能统计
int tokens_generated = 0;
int drafts_accepted = 0;
double speedup = 1.0;
bool initialize(const common_params & params) {
// 1. 加载目标模型
model_target = llama_load_model_from_file(params.model.c_str(),
common_model_params_to_llama(params));
ctx_target = llama_new_context_with_model(model_target,
common_context_params_to_llama(params));
// 2. 加载草稿模型
if (!params.draft_model.empty()) {
common_params draft_params = params;
draft_params.model = params.draft_model;
draft_params.n_ctx = std::min(params.n_ctx, 2048); // 限制草稿模型上下文
model_draft = llama_load_model_from_file(draft_params.model.c_str(),
common_model_params_to_llama(draft_params));
ctx_draft = llama_new_context_with_model(model_draft,
common_context_params_to_llama(draft_params));
}
return model_target && ctx_target;
}
};
3.4.3. 投机解码算法
// 投机解码核心算法
llama_token speculative_decode_step(speculative_decoder & spec,
const std::vector<llama_token> & input_ids) {
std::vector<llama_token> draft_tokens;
std::vector<llama_token> accepted_tokens;
// 1. 草稿模型生成预测序列
draft_tokens = generate_draft_sequence(spec.ctx_draft, input_ids, spec.n_draft);
// 2. 目标模型并行验证
for (int i = 0; i <= draft_tokens.size(); ++i) {
// 构建验证批处理
std::vector<llama_token> verify_sequence = input_ids;
verify_sequence.insert(verify_sequence.end(),
draft_tokens.begin(),
draft_tokens.begin() + i);
// 目标模型计算概率
llama_batch batch = build_batch(verify_sequence);
llama_decode(spec.ctx_target, batch);
const float * logits = llama_get_logits(spec.ctx_target);
llama_token target_token = get_most_likely_token(logits);
if (i < draft_tokens.size()) {
// 验证草稿token
if (target_token == draft_tokens[i]) {
accepted_tokens.push_back(target_token);
spec.drafts_accepted++;
} else {
// 拒绝后续所有草稿token
break;
}
} else {
// 最后一个token总是来自目标模型
accepted_tokens.push_back(target_token);
}
}
// 3. 更新统计信息
spec.tokens_generated += accepted_tokens.size();
spec.speedup = double(spec.tokens_generated) / (input_ids.size() + 1);
// 4. 返回第一个接受的token用于流式输出
return accepted_tokens.empty() ?
llama_sample_token greedy(spec.ctx_target, logits) :
accepted_tokens[0];
}
3.4.3. 性能分析
// 性能报告
void print_speculative_stats(const speculative_decoder & spec) {
printf("Speculative Decoding Performance:\n");
printf(" Tokens Generated: %d\n", spec.tokens_generated);
printf(" Drafts Accepted: %d\n", spec.drafts_accepted);
printf(" Accept Rate: %.2f%%\n",
100.0 * spec.drafts_accepted / spec.tokens_generated);
printf(" Speedup: %.2fx\n", spec.speedup);
printf(" Draft Ratio: %.2f\n", double(spec.n_draft));
}
3.5 Server 示例 - HTTP API服务
3.5.1. 生产就绪特性
虽然 server 示例位于 tools/ 目录,但它是 examples 体系的重要组成部分,展示了如何构建生产级的LLM推理服务。
3.5.2. 架构设计
// HTTP服务器架构
class llama_http_server {
private:
struct http_handler {
std::string method;
std::string path;
std::function<bool(const httplib::Request&, httplib::Response&)> handler;
};
std::vector<http_handler> handlers_;
std::unique_ptr<llama_context> ctx_;
std::unique_ptr<llama_model> model_;
std::unique_ptr<common_sampler> sampler_;
// 请求管理
std::mutex request_mutex_;
std::queue<std::function<void()>> request_queue_;
std::condition_variable request_cv_;
std::thread worker_thread_;
public:
bool initialize(const common_params & params);
void start_server(const std::string & host, int port);
void register_handlers();
private:
void setup_openai_compatible_endpoints();
void setup_anthropic_compatible_endpoints();
void process_requests();
};
3.5.3. API端点实现
3.5.3.1. OpenAI兼容接口
// /v1/completions 端点
void handle_completions(const httplib::Request& req, httplib::Response& res) {
try {
nlohmann::json request = nlohmann::json::parse(req.body);
// 解析参数
std::string prompt = request["prompt"];
int max_tokens = request.value("max_tokens", 100);
float temperature = request.value("temperature", 0.7f);
// 生成响应
nlohmann::json response;
response["id"] = generate_uuid();
response["object"] = "text_completion";
response["created"] = std::time(nullptr);
// 执行推理
std::string generated_text = generate_completion(prompt, max_tokens, temperature);
response["choices"] = nlohmann::json::array();
nlohmann::json choice;
choice["text"] = generated_text;
choice["finish_reason"] = "length";
response["choices"].push_back(choice);
response["usage"] = {
{"prompt_tokens", count_tokens(prompt)},
{"completion_tokens", count_tokens(generated_text)},
{"total_tokens", count_tokens(prompt) + count_tokens(generated_text)}
};
res.set_content(response.dump(), "application/json");
} catch (const std::exception& e) {
nlohmann::json error;
error["error"] = {
{"message", e.what()},
{"type", "invalid_request_error"},
{"code", "invalid_request"}
};
res.status = 400;
res.set_content(error.dump(), "application/json");
}
}
3.5.3.2. 流式响应支持
// 流式生成
void handle_completions_stream(const httplib::Request& req, httplib::Response& res) {
res.set_header("Content-Type", "text/event-stream");
res.set_header("Cache-Control", "no-cache");
res.set_header("Connection", "keep-alive");
// 生成流式响应
auto generate_callback = [&](const std::string& chunk, bool finished) {
nlohmann::json event;
event["id"] = generate_uuid();
event["object"] = "text_completion";
event["created"] = std::time(nullptr);
nlohmann::json choice;
choice["text"] = chunk;
choice["finish_reason"] = finished ? "length" : nullptr;
event["choices"] = nlohmann::json::array();
event["choices"].push_back(choice);
std::string sse_data = "data: " + event.dump() + "\n\n";
res.write(sse_data);
if (finished) {
res.write("data: [DONE]\n\n");
}
};
// 执行流式生成
generate_completion_stream(prompt, max_tokens, temperature, generate_callback);
}
3.5.3.3. 多模态支持
// 多模态输入处理
std::vector<llama_token> process_multimodal_input(const nlohmann::json& messages) {
std::vector<llama_token> tokens;
for (const auto& msg : messages) {
// 处理文本
if (msg.contains("content") && msg["content"].is_string()) {
auto text_tokens = common_tokenize(ctx_, msg["content"], true);
tokens.insert(tokens.end(), text_tokens.begin(), text_tokens.end());
}
// 处理多模态内容
if (msg.contains("content") && msg["content"].is_array()) {
for (const auto& part : msg["content"]) {
if (part["type"] == "text") {
auto text_tokens = common_tokenize(ctx_, part["text"], true);
tokens.insert(tokens.end(), text_tokens.begin(), text_tokens.end());
} else if (part["type"] == "image_url") {
// 处理图像
std::string image_data = base64_decode(part["image_url"]["url"]);
auto image_tokens = process_image(image_data);
tokens.insert(tokens.end(), image_tokens.begin(), image_tokens.end());
}
}
}
}
return tokens;
}
4. 使用场景分类分析
4.1 学习和教育场景
入门级 (1-2周)
- simple/: 理解LLM推理的基本流程
- simple-chat/: 学习交互式应用开发
- embedding/: 掌握文本向量化技术
学习路径建议:
Day 1-3: simple/ - 理解基础API和流程
Day 4-7: simple-chat/ - 学习交互逻辑
Day 8-10: embedding/ - 掌握向量化
Day 11-14: 综合项目 - 构建简单应用
进阶级 (2-3周)
- batched/: 批处理优化技术
- parallel/: 并发处理实现
- save-load-state/: 状态管理机制
高级 (3-4周)
- speculative/: 投机解码技术
- lookahead/: 前瞻解码算法
- eval-callback/: 深入理解模型计算
4.2 生产部署场景
4.2.1. API服务开发
// 基于server示例的生产架构
class production_llm_service {
private:
std::unique_ptr<llama_http_server> server_;
std::unique_ptr<common_model_cache> model_cache_;
std::unique_ptr<common_load_balancer> load_balancer_;
public:
bool deploy(const service_config& config) {
// 1. 初始化模型缓存
model_cache_ = std::make_unique<common_model_cache>();
// 2. 配置负载均衡
load_balancer_ = std::make_unique<common_load_balancer>();
// 3. 启动HTTP服务
server_ = std::make_unique<llama_http_server>();
return server_->initialize(config.llama_params);
}
};
4.2.2. 批处理系统
// 高性能批处理管道
class batch_processing_pipeline {
private:
struct batch_job {
std::vector<llama_token> tokens;
std::string job_id;
std::function<void(const std::string&)> callback;
};
std::queue<batch_job> job_queue_;
std::mutex queue_mutex_;
std::thread processing_thread_;
public:
void submit_job(const std::string& text,
std::function<void(const std::string&)> callback) {
batch_job job;
job.tokens = common_tokenize(ctx_, text, true);
job.job_id = generate_job_id();
job.callback = callback;
{
std::lock_guard<std::mutex> lock(queue_mutex_);
job_queue_.push(job);
}
}
private:
void process_batch_jobs() {
while (running_) {
std::vector<batch_job> current_batch;
// 收集批处理任务
{
std::lock_guard<std::mutex> lock(queue_mutex_);
while (current_batch.size() < max_batch_size && !job_queue_.empty()) {
current_batch.push_back(job_queue_.front());
job_queue_.pop();
}
}
if (!current_batch.empty()) {
// 执行批处理
process_batch(current_batch);
} else {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
}
};
4.3 研究实验场景
4.3.1. 技术验证平台
// 实验框架基类
class experiment_framework {
protected:
std::map<std::string, double> metrics_;
std::vector<std::string> experiment_log_;
public:
virtual void setup_experiment() = 0;
virtual void run_experiment() = 0;
virtual void collect_metrics() = 0;
void execute_experiment() {
auto start_time = std::chrono::high_resolution_clock::now();
setup_experiment();
run_experiment();
collect_metrics();
auto end_time = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
metrics_["execution_time_ms"] = duration.count();
// 生成实验报告
generate_experiment_report();
}
protected:
void log_event(const std::string& event) {
experiment_log_.push_back(event);
}
void record_metric(const std::string& name, double value) {
metrics_[name] = value;
}
private:
void generate_experiment_report() {
nlohmann::json report;
report["timestamp"] = std::time(nullptr);
report["metrics"] = metrics_;
report["events"] = experiment_log_;
std::string report_path = "experiment_report_" +
std::to_string(std::time(nullptr)) + ".json";
std::ofstream file(report_path);
file << report.dump(2);
}
};
4.3.2. 投机解码实验
// 投机解码性能实验
class speculative_experiment : public experiment_framework {
private:
std::vector<std::string> test_prompts_;
std::vector<int> draft_ratios_ = {2, 4, 8, 16};
std::vector<float> acceptance_thresholds_ = {0.3f, 0.5f, 0.7f, 0.9f};
public:
void setup_experiment() override {
// 加载测试数据集
load_test_dataset();
log_event("Loaded " + std::to_string(test_prompts_.size()) + " test prompts");
}
void run_experiment() override {
for (int draft_ratio : draft_ratios_) {
for (float threshold : acceptance_thresholds_) {
run_single_experiment(draft_ratio, threshold);
}
}
}
void collect_metrics() override {
// 计算平均加速比
double total_speedup = 0.0;
int experiment_count = 0;
for (const auto& [name, value] : metrics_) {
if (name.find("speedup_") == 0) {
total_speedup += value;
experiment_count++;
}
}
record_metric("average_speedup", total_speedup / experiment_count);
}
private:
void run_single_experiment(int draft_ratio, float threshold) {
std::string experiment_name = "draft_" + std::to_string(draft_ratio) +
"_threshold_" + std::to_string(threshold);
log_event("Starting experiment: " + experiment_name);
double total_time_baseline = 0.0;
double total_time_speculative = 0.0;
for (const std::string& prompt : test_prompts_) {
// 基线测试
auto start = std::chrono::high_resolution_clock::now();
std::string result_baseline = generate_baseline(prompt, 100);
auto end = std::chrono::high_resolution_clock::now();
total_time_baseline += std::chrono::duration<double>(end - start).count();
// 投机解码测试
start = std::chrono::high_resolution_clock::now();
std::string result_speculative = generate_speculative(prompt, 100, draft_ratio, threshold);
end = std::chrono::high_resolution_clock::now();
total_time_speculative += std::chrono::duration<double>(end - start).count();
// 验证结果一致性
if (result_baseline != result_speculative) {
log_event("Warning: Results differ for prompt: " + prompt);
}
}
double speedup = total_time_baseline / total_time_speculative;
record_metric("speedup_" + experiment_name, speedup);
record_metric("time_baseline_" + experiment_name, total_time_baseline);
record_metric("time_speculative_" + experiment_name, total_time_speculative);
log_event("Experiment " + experiment_name + " completed. Speedup: " +
std::to_string(speedup) + "x");
}
};
5. 教学价值评估
5.1 代码质量分析
一致性评分 ⭐⭐⭐⭐⭐
- 编码规范: 所有示例遵循统一的编码标准
- 命名规范: 函数和变量命名清晰一致
- 注释质量: 关键代码段都有详细注释
- 错误处理: 统一的错误处理模式
完整性评分 ⭐⭐⭐⭐
- 功能完整: 每个示例都是可运行的完整程序
- 依赖管理: 明确的依赖关系和构建配置
- 文档齐全: 每个示例都有详细的README
- 测试覆盖: 包含多种测试用例
可维护性评分 ⭐⭐⭐⭐
- 模块化设计: 清晰的功能分离
- 接口清晰: 简洁的公共API
- 扩展容易: 易于添加新功能
- 调试友好: 包含调试和监控代码
5.2 学习路径设计
初学者路径 (评分: ⭐⭐⭐⭐⭐)
1. simple/ → 2. simple-chat/ → 3. embedding/ → 4. save-load-state/
优势:
- 循序渐进的复杂度
- 每个示例都有明确的学习目标
- 丰富的注释和文档支持
进阶开发者路径 (评分: ⭐⭐⭐⭐)
1. batched/ → 2. parallel/ → 3. speculative/ → 4. retrieval/
优势:
- 涵盖生产环境的关键技术
- 包含性能优化最佳实践
- 展示高级架构设计
研究者路径 (评分: ⭐⭐⭐⭐)
1. eval-callback/ → 2. lookahead/ → 3. diffusion/ → 4. training/
优势:
- 展示前沿研究成果
- 提供实验框架模板
- 支持自定义算法扩展
5.3 实用性评估
生产就绪度 ⭐⭐⭐⭐
- server/: 可直接用于生产API服务
- batched/: 适合大规模批处理任务
- embedding/: 完整的向量化解决方案
扩展性 ⭐⭐⭐⭐⭐
- 插件化架构: 易于添加新功能
- 配置驱动: 通过参数控制行为
- 模块化设计: 支持独立使用和组合
性能优化 ⭐⭐⭐⭐⭐
- 多种优化技术: 批处理、投机解码、并行处理
- 性能监控: 内置的性能分析和统计
- 内存管理: 高效的内存使用和复用
6. 最佳实践总结
6.1 代码组织模式
6.1.1. 标准模板结构
#include "common.h"
#include "llama.h"
int main(int argc, char ** argv) {
// 1. 参数解析
common_params params;
if (!common_params_parse(argc, argv, params, EXAMPLE_TYPE)) {
return 1;
}
// 2. 系统初始化
common_init();
llama_backend_init();
llama_numa_init(params.numa);
// 3. 模型加载
llama_model * model = llama_load_model_from_file(
params.model.c_str(), common_model_params_to_llama(params));
// 4. 上下文创建
llama_context * ctx = llama_new_context_with_model(
model, common_context_params_to_llama(params));
// 5. 核心逻辑
try {
run_inference_loop(params, model, ctx);
} catch (const std::exception & e) {
fprintf(stderr, "Error: %s\n", e.what());
return 1;
}
// 6. 资源清理
llama_free(ctx);
llama_free(model);
llama_backend_free();
return 0;
}
6.1.2. 错误处理模式
// RAII资源管理
struct llama_context_guard {
llama_context * ctx;
llama_context_guard(llama_context * c) : ctx(c) {}
~llama_context_guard() {
if (ctx) llama_free(ctx);
}
llama_context* release() {
llama_context * tmp = ctx;
ctx = nullptr;
return tmp;
}
};
// 异常安全使用
void safe_inference(const common_params & params) {
llama_model * model = nullptr;
llama_context * ctx = nullptr;
try {
model = llama_load_model_from_file(...);
ctx = llama_new_context_with_model(model, ...);
// 核心推理逻辑
perform_inference(ctx);
} catch (const std::exception & e) {
// 记录错误并清理资源
fprintf(stderr, "Inference failed: %s\n", e.what());
}
// 确保资源释放
if (ctx) llama_free(ctx);
if (model) llama_free(model);
}
6.2 性能优化技巧
6.2.1. 内存优化
// 内存池复用
class token_buffer_pool {
private:
std::vector<std::vector<llama_token>> pool_;
std::mutex pool_mutex_;
public:
std::vector<llama_token>* acquire(size_t size) {
std::lock_guard<std::mutex> lock(pool_mutex_);
for (auto& buffer : pool_) {
if (buffer.capacity() >= size) {
buffer.clear();
buffer.reserve(size);
return &buffer;
}
}
// 创建新缓冲区
pool_.emplace_back();
pool_.back().reserve(size);
return &pool_.back();
}
void release(std::vector<llama_token>* buffer) {
// 缓冲区自动回收到池中,无需显式释放
}
};
6.2.2. 计算优化
// 批处理优化
void optimized_batch_decode(llama_context * ctx,
const std::vector<std::vector<llama_token>>& sequences) {
// 1. 预分配批处理对象
int total_tokens = 0;
for (const auto& seq : sequences) {
total_tokens += seq.size();
}
llama_batch batch = llama_batch_init(total_tokens, 0, sequences.size());
// 2. 高效填充批处理
int token_idx = 0;
for (int seq_id = 0; seq_id < sequences.size(); ++seq_id) {
for (size_t pos = 0; pos < sequences[seq_id].size(); ++pos) {
batch.token[token_idx] = sequences[seq_id][pos];
batch.pos[token_idx] = pos;
batch.n_seq_id[token_idx] = 1;
batch.seq_id[token_idx][0] = seq_id;
batch.logits[token_idx] = (pos == sequences[seq_id].size() - 1);
token_idx++;
}
}
batch.n_tokens = token_idx;
// 3. 批量解码
llama_decode(ctx, batch);
// 4. 清理
llama_batch_free(batch);
}
6.3 调试和监控技巧
6.3.1. 性能监控
// 详细性能分析器
class performance_profiler {
private:
struct profile_entry {
std::string name;
std::chrono::high_resolution_clock::time_point start_time;
double total_time = 0.0;
int call_count = 0;
};
std::unordered_map<std::string, profile_entry> profiles_;
std::mutex profiles_mutex_;
public:
class profile_guard {
performance_profiler& profiler;
profile_entry& entry;
std::chrono::high_resolution_clock::time_point start;
public:
profile_guard(performance_profiler& p, profile_entry& e)
: profiler(p), entry(e), start(std::chrono::high_resolution_clock::now()) {}
~profile_guard() {
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration<double>(end - start).count();
entry.total_time += duration;
entry.call_count++;
}
};
profile_guard profile_scope(const std::string& name) {
std::lock_guard<std::mutex> lock(profiles_mutex_);
return profile_guard(*this, profiles_[name]);
}
void print_report() {
printf("Performance Report:\n");
printf("%-30s %12s %12s %12s\n", "Function", "Total(s)", "Calls", "Avg(ms)");
printf("%-30s %12s %12s %12s\n", "--------", "--------", "-----", "-------");
for (const auto& [name, entry] : profiles_) {
printf("%-30s %12.3f %12d %12.3f\n",
name.c_str(),
entry.total_time,
entry.call_count,
(entry.total_time * 1000) / entry.call_count);
}
}
};
// 使用示例
void perform_inference() {
static performance_profiler profiler;
{
auto guard = profiler.profile_scope("tokenization");
// 分词代码
}
{
auto guard = profiler.profile_scope("model_decode");
// 模型解码代码
}
{
auto guard = profiler.profile_scope("sampling");
// 采样代码
}
}
7. 总结
7.1 核心价值
Llama.cpp 的 examples 目录展现了卓越的工程设计:
1. 教学价值极高 ⭐⭐⭐⭐⭐
- 循序渐进的学习路径
- 丰富的注释和文档
- 从入门到专业的完整覆盖
2. 实用性强 ⭐⭐⭐⭐⭐
- 生产就绪的代码质量
- 直接可用的功能模板
- 最佳实践的集中体现
3. 技术深度 ⭐⭐⭐⭐
- 覆盖基础到前沿技术
- 多种优化技术演示
- 深入的底层实现展示
4. 扩展性优秀 ⭐⭐⭐⭐⭐
- 模块化的架构设计
- 插件化的功能扩展
- 灵活的配置系统
7.2 对项目的贡献
这些示例程序不仅为用户提供了学习资源,更为整个 llama.cpp 生态系统做出了重要贡献:
- 降低学习门槛: 让新用户快速上手
- 提供最佳实践: 展示标准的开发模式
- 促进技术创新: 为新技术提供验证平台
- 构建社区生态: 培养开发者社区
7.3 推荐使用策略
对于初学者:
1. 从 simple/ 开始,理解基本概念
2. 逐步学习 simple-chat/ 和 embedding/
3. 尝试修改和扩展示例代码
对于开发者:
1. 参考 server/ 构建生产服务
2. 使用 batched/ 优化批量处理
3. 研究 speculative/ 了解前沿技术
对于研究者:
1. 基于 eval-callback/ 构建实验框架
2. 扩展 training/ 实现新算法
3. 利用 diffusion/ 探索多模态应用
这些示例程序是 llama.cpp 项目的重要财富,为LLM推理技术的发展和应用推广提供了坚实的基础。无论是学习、研究还是生产开发,都能在这里找到有价值的参考和灵感。

740

被折叠的 条评论
为什么被折叠?



