现代 C++ 实战(15):现代设计模式
GoF 23 种设计模式在教科书上往往是一页页继承树。现代 C++ 里很多模式可以大幅瘦身:std::function 替代策略接口、make_unique 管工厂、variant + visit 做访问者——少写类,多写意图。 这一篇对应 demo:ref/cpp_demo/basics/design_patterns/(Strategy、Observer、Builder、Singleton、Command、Visitor)。 这是「现代 C++ 实战」系列的第 15 篇。建议先读 第 14 篇:线程池与背压控制。 一、模式还要学吗? 传统痛点 现代 C++ 的解 每种策略一个子类 std::function + lambda Observer 抽象接口 + 虚函数 回调列表;对象观察者用 weak_ptr 复杂构造参数爆炸 Builder 链式 + 移动语义 new/delete 散落 make_unique 工厂 + RAII 为每种类型写 Visitor 子类 std::variant + std::visit(C++17) ...
大模型数学速成(11):KV Cache——推理加速的关键
没有 KV Cache,大模型逐 token 生成会反复重算整段历史的 K、V——复杂度爆炸、延迟不可接受。第 10 篇 讲了用 GQA 压缩 Cache 体积;本篇收官:Prefill / Decode 两阶段、Decode 单步在算什么、显存怎么估,以及多模态里 Cache 怎么共享。 这是「大模型数学速成」系列的第 11 篇(收官)。建议先读 第 10 篇:GQA。 一、O(N²) 重复计算问题生成第 $t$ 个 token 时,Attention 需要当前 Q 与位置 $1 \ldots t$ 的全部 K 做点积(第 05 篇)。 若不缓存 K/V,每步 Decode 都要对整段输入重新做 Q/K/V 投影和 Attention: 步数 每步参与 token 数 naive 总计算量量级 生成 $S$ 个 token $1, 2, \ldots, S$ $O(S^2)$ 重复投影 + Attention 长对话下,算力浪费在「重算旧 token 的 K/V」 上。KV Cache 的核心思想:算过的...
现代 C++ 实战(14):线程池与背压控制
第 13 篇讲了 jthread、semaphore、latch/barrier——都是同步工具。生产环境里更常见的问题是:任务来得比处理快,线程该复用还是现开?队列该无限长还是限流?队列满了怎么办? 这一篇用 demo ref/cpp_demo/concurrency/thread_pool/ 讲线程池架构、有界队列、背压策略,以及 future/promise 取异步结果;附带 HTTP REST Server/Client 实战。 这是「现代 C++ 实战」系列的第 14 篇。建议先读 第 13 篇:C++20 同步原语。 一、为什么需要线程池?每来一个任务就 std::thread(...).detach(): 问题 后果 创建开销 线程栈分配、内核调度——毫秒级,短任务不划算 线程数爆炸 1 万并发 → 1 万线程,内存与上下文切换拖垮系统 难以复用 无法统一限流、监控、优雅关闭 线程池:固定 N 个工作线程 + 任务队列,提交方只 submit,worker 循环取任务执行——摊薄创建成本、控制并发上限。 ...
大模型数学速成(10):GQA——分组查询注意力
第 09 篇的 MHA 里,Q、K、V 各有 $h$ 个头——推理时要为每个 token 缓存全部 K/V 头,显存随上下文长度线性暴涨。GQA(Grouped Query Attention,分组查询注意力) 让多路 Q 共享更少的 K/V 头,在效果接近 MHA 的前提下大幅压缩 KV Cache。 这一篇讲清:KV Cache 为何吃显存、GQA 怎么分组共享、以及 MHA / GQA / MQA 对照。 这是「大模型数学速成」系列的第 10 篇。建议先读 第 09 篇:多头注意力。下一篇 KV Cache(系列收官)。 一、KV Cache 显存从哪来?Decode 阶段每生成一个新 token,都要和历史上所有 token 做 Attention(第 05 篇)。历史 token 的 K、V 在 Prefill 时已算过——存下来复用,不必每步重算,这就是 KV Cache。 每层、每个 token 需缓存: $$\text{K 缓存} + \text{V 缓存} \approx 2 \times h_\text{kv} ...
现代 C++ 实战(13):C++20 同步原语
第 12 篇用 thread、mutex、condition_variable 搭好了并发地基。C++20 补上最后几块拼图:jthread 自动 join 与协作式取消、semaphore 限流、latch / barrier 多线程阶段同步——写法更短、语义更清晰。 这一篇对应 demo:ref/cpp_demo/concurrency/sync_primitives/。 这是「现代 C++ 实战」系列的第 13 篇。建议先读 第 12 篇:多线程基础。 一、C++20 补上了什么? 原语 解决的问题 std::jthread 忘记 join() 导致 terminate;需要优雅停止后台线程 std::counting_semaphore 限制同时访问资源的线程数(连接池、限流) std::latch 一次性「等所有人到齐」 std::barrier 可重复的多阶段同步点 std::stop_token 协作式取消(配合 jthread) 编译要求:GCC 10+、Clang 10+、MSVC 2019+,-std=c++20...
大模型数学速成(09):多头注意力——多个专家各看各的
第 05 篇我们用的是「一整块」768 维 Q/K/V 做一次 Attention。实际模型里常见 12、16、32 个头(head)——同一层里多组更小的 Attention 并行运行,再拼回去。为什么要拆头?维度怎么变? 这一篇讲清 分头、逐头 Attention、concat 与输出投影 $W_o$ 的全流程。 这是「大模型数学速成」系列的第 9 篇。建议先读 第 05 篇:Attention 与 Softmax、第 07 篇:RoPE。下一篇 GQA。 一、单头 vs 多头:陪审团类比单头 Attention:一个「专家」用 768 维 Q/K/V 同时看所有语义侧面。 多头 Attention(MHA):$h$ 个「陪审员」,每人只拿 $d_\text{head}$ 维(如 64 维),独立做一遍 Attention,最后把 $h$ 份意见 拼接 成完整向量。 12单头:1 人 768 维全包多头:12 人各 64 维,各看各的角度,再合并报告 直觉:不同 head 可学到不同模式——有的关注语法距离,有的关注指代,有的...
现代 C++ 实战(12):多线程基础
第一季讲完语言特性,第二季进入并发与工程。第一课是 C++11 起就有的四件套:std::thread、mutex、condition_variable、atomic——创建并行任务、保护共享数据、线程间等待通知、无锁计数。 这一篇对应 demo:ref/cpp_demo/concurrency/thread_demos/(含 thread 基础、mutex、条件变量、atomic、线程安全队列)。 这是「现代 C++ 实战」系列的第 12 篇,第二季开篇。建议先读 第 11 篇:Concepts。 一、为什么需要多线程? 利用多核:CPU 密集型任务拆到多个核并行 重叠 I/O 与计算:一个线程等磁盘/网络,另一个继续算 响应性:UI 线程不阻塞于后台任务 C++ 标准库从 C++11 起提供跨平台线程抽象,不必直接写 pthread(Linux)或 Win32 API。 二、std::thread:创建、join、detach1234567891011121314151617#include <thread>#include <...
大模型数学速成(08):ViT 层 vs LLM 层——概念对照总表
前 7 篇我们分别讲了张量约定、矩阵乘、Q/K/V、Norm 与残差、Attention、FFN、RoPE—— pieces 齐了,但读 ViT 或 LLM 代码时仍容易混:patch 和 token 是一回事吗?为什么 ViT 双向、LLM 因果?RMS Norm 和 LayerNorm 谁在哪? 这一篇不引入新公式,用 一张骨架图 + 对照大表 把视觉 Transformer 与语言模型在同一套数学语言下对齐,并给出后续阅读路径。 这是「大模型数学速成」系列的第 8 篇。建议已读 第 00–07 篇。下一篇 多头注意力。 一、共同的 Transformer 骨架无论 ViT 还是 LLM,一层 block 的核心结构相同(Pre-LN 写法): 12345678输入 X [d, S] S = 序列长度(patch 数或 token 数) │ ├─ Norm ──► Self-Attention ──► + 残差 │ ├─ Norm ──► FFN ──► + 残差 │ ▼输出 X' [d, S...
现代 C++ 实战(11):Concepts 与模板进阶
模板很强大,但约束不足时编译错误像天书——SFINAE、enable_if 能解决问题,却难写难读。C++20 Concepts 把「类型必须满足什么」写进签名,错误从 100 行模板展开变成 3 行人话。 这一篇从 SFINAE 回顾到 Concepts 语法,并配合 type_traits 与 demo:ref/cpp_demo/basics/type_traits_demo/。 这是「现代 C++ 实战」系列的第 11 篇。建议先读 第 10 篇:Ranges。 一、模板的问题:约束写在哪?12345template<typename T>T add(T a, T b) { return a + b; }add(1, 2); // OKadd("hello", "world"); // 可能编译过,但语义不对(指针相加) 我们希望:在模板实例化前就拒绝不合法的类型,并给出清晰错误——而不是在深层实例化里爆炸。 历史上三条路线: 时代 手段 C++11 SFINA...
大模型数学速成(07):RoPE——用旋转编码位置
Self-Attention 本身不包含位置信息——打乱 token 顺序,只要 Q/K/V 一起打乱,注意力分数不变。但语言有语序:「狗咬人」≠「人咬狗」。位置编码告诉模型每个 token 在序列中的位置;现代 LLM 广泛使用的 RoPE(Rotary Position Embedding,旋转位置编码) 通过「旋转」Q/K 向量来注入相对位置。 这一篇搞懂:为什么需要 RoPE、旋转在算什么、为何只改 Q/K、以及绝对位置与相对位置的关系。 这是「大模型数学速成」系列的第 7 篇。建议先读 第 06 篇:FFN。下一篇 ViT 层 vs LLM 层对照总表。 一、顺序问题:Attention 是「置换等变」的第 05 篇 的注意力只看点积相似度。若把 token 列整体重排(Q、K、V 同步重排),每个 token 关注谁不变——模型不知道谁在前谁在后。 早期做法: 方法 思路 代表 绝对位置嵌入 给每个位置 $i$ 学一个向量 $p_i$,$x + p_i$ 原始 Transformer RoPE 按位置旋转...










