时间层级
基本换算
| 单位 | 中文 | 科学记数法 | 换算成秒 | 1秒等于多少该单位 | 形象比喻(大概) |
|---|---|---|---|---|---|
| 1 s | 1秒 | 10⁰ s | 1 s | — | 人类眨眼一次 ≈ 0.1–0.4秒 |
| 1 ms | 1毫秒 | 10⁻³ s | 0.001 s | 1,000 ms | 1秒能发生1000次毫秒级事件 |
| 1 μs | 1微秒 | 10⁻⁶ s | 0.000001 s | 1,000,000 μs | 1毫秒 = 1000微秒 |
| 1 ns | 1纳秒 | 10⁻⁹ s | 0.000000001 s | 1,000,000,000 ns | 1微秒 = 1000纳秒 |
- 1秒 = 10³ ms = 10⁶ μs = 10⁹ ns
- 1ms = 10³ μs = 10⁶ ns
- 1μs = 10³ ns
量级具体含义
| 时间量级 | 典型含义 / 现象 | 是否能接受 | Perf/eBPF常看到的典型开销 |
|---|---|---|---|
| 几十~几百ms | 人类反应时间、很多云端推理延迟 | 基本不可接受 | — |
| 10–50 ms | 常见摄像头/激光雷达帧率(10–30Hz) | 勉强 | — |
| 1–5 ms | 主流量产系统控制周期、很多规划/控制循环周期 | 可接受但偏松(目标尽量压到<3ms) | — |
| 100–800 μs | 高端激光雷达一帧处理目标时间、很多端到端模型单帧推理目标 | 比较理想的目标区间 | — |
| 10–100 μs | 高性能内核热点函数、一次eBPF程序完整执行、一次高优先级中断处理 | 非常好的内核/中间件延迟 | 一次比较重的eBPF prog |
| 1–10 μs | 一次比较干净的内存拷贝(几KB)、一次context switch(优化后) | 优秀内核路径延迟 | perf record热点函数 |
| 几百ns–2μs | cache miss + TLB miss、跨NUMA访问、一次锁竞争严重时 | 可以接受,但累积多了很致命 | L3 miss、跨socket访问 |
| 几十~200 ns | L1/L2 cache hit、简单算术、一次原子操作、现代CPU一条load | 这是我们的理想基本操作延迟 | perf stat中最常见的指令级延迟 |
| <10 ns | 寄存器操作、L1 hit + 流水线完美命中 | 理论最优指令延迟 | — |
最常见的“时间杀手”层级速查表
- >1ms → 基本都是算法/框架/大锁/调度问题
- 100μs–1ms → 经常是多线程竞争、内存分配、序列化/反序列化、数据拷贝、通信
- 10–100μs → 内核态热点、软中断、eBPF开销、GPU拷贝、一次大的memcpy
- 1–10μs → cache miss、锁、上下文切换、page fault
- <1μs → 基本进入CPU微架构优化领域(分支预测、cacheline false sharing、指令乱序、SIMD等)
常见操作的典型耗时估算表
主流 x86-64 CPU(Intel Arrow Lake / Lunar Lake / AMD Zen 5 / Zen 6 架构级别)上,C++ 常见操作的典型耗时估算表。
这些数字是热路径(hot path)、L1 cache hit 假设下的最佳情况,实际生产环境会因为 cache miss、分支预测失败、TLB miss、超线程竞争、内存带宽饱和等因素显著变差(通常 5–100×)。
所有时间都换算成 纳秒 (ns),假设 CPU 主频 ≈ 5.0–5.5 GHz(常见 turbo 频率)。
基于主流桌面/服务器 CPU 的实测汇总(uops.info、Agner、CppCon 低延迟 talk、HFT 社区数据),不同 CPU 代际会有 20–50% 浮动,但相对顺序和数量级几乎不变。
| 操作类型 | 典型 cycles | 典型时间 (ns) @ ~5GHz | 备注 / 常见场景 / 优化建议 |
|---|---|---|---|
| 简单整数加法/减法/位运算 (reg-reg) | 0.25–1 | 0.05–0.2 ns | 几乎免费,4-wide 发射,融合 add-and-branch 等 |
| 整数乘法 (64-bit) | 3–5 | 0.6–1 ns | Zen 5 / Arrow Lake 已很强,imul r64,r64 |
| 整数除法 (64-bit) | 12–25 | 2.4–5 ns | 非常贵,避免在热路径;用乘法近似或查表替代 |
| 浮点加法/乘法 (scalar double) | 3–5 | 0.6–1 ns | 现代 CPU 几乎和整数乘一样快 |
| 浮点 FMA (fused multiply-add) | 4–6 | 0.8–1.2 ns | 最划算的浮点操作,鼓励用 *=+ 而不是分开写 |
| SIMD 加法/乘法 (256/512-bit AVX2/AVX-512) | 0.5–1 / lane | 0.1–0.2 ns per lane | 吞吐量极高,但 latency 仍 ≈ 4 cycles |
| L1 cache hit 读 (load) | 4–5 | 0.8–1 ns | 最常见内存访问,load-use 依赖链起点 |
| L1 cache hit 写 (store) | ≈1–2 (隐式) | <0.5 ns | store buffer 隐藏大部分延迟 |
| L2 cache hit 读 | 12–20 | 2.4–4 ns | 常见二级缓存命中,Intel 通常比 AMD 略快 |
| L3 cache hit 读 (同 die) | 35–60 | 7–12 ns | 跨 core 但同 CCD/CCX,AMD Zen 5 优化明显 |
| L3 miss → 远端 NUMA / DRAM 随机读 | 200–400+ | 40–80+ ns | 性能杀手,随机访问 4KB page 常在此区间 |
| 普通函数调用 (hot, 非虚) | 2–8 | 0.4–1.6 ns | 包括 prologue/epilogue,现代 CPU call/ret 预测极好 |
| 虚函数调用 (devirtualized / profiled) | 3–10 | 0.6–2 ns | 预测命中时接近普通调用 |
| 虚函数调用 (cold / miss) | 20–50+ | 4–10+ ns | vtable 走 + 预测惩罚,最坏情况 |
| std::mutex lock/unlock (uncontended) | 20–60 | 4–12 ns | 单线程或低竞争时还行 |
| std::mutex lock (轻度竞争) | 50–300 | 10–60 ns | 开始痛了 |
| std::mutex lock (高竞争) | 数百–数千 | 几十 ns – 几 μs | 灾难,火焰图上锁等待很粗 |
| std::atomic fetch_add (relaxed) | 1–3 | 0.2–0.6 ns | 无竞争时极快 |
| std::atomic fetch_add (seq_cst) | 8–20 | 1.6–4 ns | 全内存屏障,贵很多,但最安全 |
| CAS (compare_exchange_strong, 无冲突) | 10–25 | 2–5 ns | lock cmpxchg 指令本身 + 失败重试开销 |
| CAS 失败重试一次 (轻冲突) | +10–30 | +2–6 ns | 指数退避或 backoff 很重要 |
| context switch (用户态 → 内核 → 用户态) | 几千–上万 | 1–5 μs | 系统调用、调度器切换 |
| memcpy 64 字节 (cacheline 大小) | 10–30 | 2–6 ns | 非对齐或跨 page 更慢 |
| memcpy 4KB (一页) | 数百–千 | 几十–几百 ns | 带宽瓶颈明显 |
| std::vector push_back (预分配好容量) | 5–20 | 1–4 ns | 热路径可接受 |
| std::vector push_back (扩容) | 几百–几千 | 几十 ns – 几 μs | 重新分配 + 拷贝旧元素,灾难性抖动 |
| std::unordered_map 查找 (良好 hash) | 20–80 | 4–16 ns | 缓存友好时;差 hash 轻松上百 ns |
| std::map / std::set 查找 (平衡树) | 80–200+ | 16–40+ ns | 指针追逐 + 分支,热路径慎用 |
“痛点层级”速查
- < 2 ns → 基本免费(寄存器操作、简单算术、L1 hit)
- 2–10 ns → 可接受但要小心累积(L2 hit、原子操作、函数调用、CAS)
- 10–50 ns → 开始疼了(L3 hit、轻竞争 mutex、好的 unordered_map)
- 50–200 ns → 明显瓶颈(L3 miss、同 die 跨 core、差的 map)
-
>200 ns → 通常是灾难(DRAM 随机访问、锁等待、context switch、vector 扩容)
- “热路径上任何超过 10 ns 的单次操作都要怀疑”
- “超过 50 ns 的内存访问基本是 L3 或更远,优先优化数据局部性”
- “锁竞争一旦出现,几十 ns 轻松变几百 ns → 优先无锁 / 读写分离 / per-thread 数据”
- “memcpy / 序列化 / protobuf 在高频路径上杀伤力极大 → 尽量零拷贝、flat buffer、原地操作”
- “perf + vtune + uops.info + Agner Fog 指令表 是日常标配”