<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Cpp on Jasper</title><link>https://jasper-aa64.github.io/zh/tags/cpp/</link><description>Recent content in Cpp on Jasper</description><generator>Hugo</generator><language>zh-CN</language><lastBuildDate>Mon, 02 Nov 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://jasper-aa64.github.io/zh/tags/cpp/index.xml" rel="self" type="application/rss+xml"/><item><title>交易系统笔记 #20：用 TSC 测延迟</title><link>https://jasper-aa64.github.io/zh/posts/tsc-latency-measurement/</link><pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/posts/tsc-latency-measurement/</guid><description>TSC 读到的是什么、什么时候读到、在哪个核上读到，热路径上打点该怎么记，以及 tick 怎么换算成 ns：频率从哪来、怎么自己校准。</description></item><item><title>交易系统笔记 #18：线程安全队列</title><link>https://jasper-aa64.github.io/zh/posts/thread-safe-queue/</link><pubDate>Tue, 27 Oct 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/posts/thread-safe-queue/</guid><description>从一把锁到多生产者：阻塞队列的条件变量和丢失唤醒，rte_ring 的两对下标和按顺序发布，Disruptor 的可用标记，以及队列满了该阻塞、丢弃还是覆盖。</description></item><item><title>交易系统笔记 #17：wait-free</title><link>https://jasper-aa64.github.io/zh/posts/wait-free-sticky-counter/</link><pubDate>Mon, 26 Oct 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/posts/wait-free-sticky-counter/</guid><description>wait-free 不是更快的 lock-free：它把 CAS 失败从“再来一次”变成“我排在你后面”，每个线程的步数因此有了上界，代价是更多原子操作和更难写对的代码。</description></item><item><title>低时延交易 —— perf：怎么用、看哪些指标</title><link>https://jasper-aa64.github.io/zh/posts/low-latency-perf-profiling/</link><pubDate>Thu, 22 Oct 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/posts/low-latency-perf-profiling/</guid><description>先用 stat 看总账，再用 record 和 report 找到占 98% 样本的函数，最后用 annotate 看到样本全落在加法后面一条指令上：那是一条一环扣一环的加法链，拆成四路就快了 3.3 倍。拿不到硬件计数器时，cpu-clock 采样照样能用；报告里只剩地址，先查符号表和程序所在的路径。</description></item><item><title>交易系统笔记 #16：自旋锁</title><link>https://jasper-aa64.github.io/zh/posts/spinlock-implementation/</link><pubDate>Wed, 21 Oct 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/posts/spinlock-implementation/</guid><description>自旋锁快不快，看等锁的核在干什么：每次都读改写，就把锁所在的行抢来抢去；先读后抢、循环里加 PAUSE、抢输了退一步，等的时候才几乎不打扰持锁的核。</description></item><item><title>低时延交易 —— 缓存案例：一次行情重算 64 个期权</title><link>https://jasper-aa64.github.io/zh/posts/low-latency-cache-case-study/</link><pubDate>Tue, 20 Oct 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/posts/low-latency-cache-case-study/</guid><description>一条行情要碰 140 条缓存行，时间大多花在等它们。四步改的都不是计算，是数据什么时候到、到哪一层、在谁手里：让缺失同时在路上，提前要下一批，交出读一次就没用的行，跨核交接时少读一次，把要给对面读的行先放进 L3。</description></item><item><title>交易系统笔记 #12：强度削弱与算术优化</title><link>https://jasper-aa64.github.io/zh/posts/strength-reduction-arithmetic/</link><pubDate>Thu, 15 Oct 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/posts/strength-reduction-arithmetic/</guid><description>编译器只做它能证明结果不变的替换，常数乘除它做得比人好；要人动手的，是它缺信息或者换了结果会变的地方：运行时的除数、符号、浮点。价格用整数存，精确也快。</description></item><item><title>交易系统笔记 #13：类型双关、函数调用与编译提示</title><link>https://jasper-aa64.github.io/zh/posts/type-punning-calls-compiler-hints/</link><pubDate>Thu, 15 Oct 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/posts/type-punning-calls-compiler-hints/</guid><description>编译器能优化到什么程度，取决于它看得见什么、敢假设什么：类型不同就当成不重叠，看不见函数体就照调用约定传参，提示宏是你直接替它做的决定，有的决定并不对。</description></item><item><title>交易系统笔记 #14：预取、预热与双缓冲</title><link>https://jasper-aa64.github.io/zh/posts/prefetch-warmup-double-buffer/</link><pubDate>Thu, 15 Oct 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/posts/prefetch-warmup-double-buffer/</guid><description>让数据先到缓存里有三种办法，各有前提：预取要你比硬件先知道地址，预热要在读数据的那个核上做，双缓冲要保证写的一方不会回到读的一方还没读完的那一份。</description></item><item><title>交易系统笔记 #15：SIMD 向量化与 intrinsics</title><link>https://jasper-aa64.github.io/zh/posts/simd-vectorization-intrinsics/</link><pubDate>Thu, 15 Oct 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/posts/simd-vectorization-intrinsics/</guid><description>一条指令算一排数，前提是编译器知道能用多宽的寄存器、确定没有重叠、允许重排；这三条做不到时，用 intrinsics 自己写，代价是对齐、指令集检测和结果的最后几位。</description></item><item><title>交易系统笔记 #10：指针访存优化</title><link>https://jasper-aa64.github.io/zh/posts/pointer-memory-access/</link><pubDate>Mon, 12 Oct 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/posts/pointer-memory-access/</guid><description>a[i] = *b 这种循环，编译器每轮都要重读 *b，因为写 a[i] 可能改掉它。指针优化做的事，就是让编译器敢少读，让链表装进更近的缓存。</description></item><item><title>交易系统笔记 #11：返回值优化与纯函数</title><link>https://jasper-aa64.github.io/zh/posts/rvo-pure-functions/</link><pubDate>Mon, 12 Oct 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/posts/rvo-pure-functions/</guid><description>按值返回结构体通常是零成本的，不用改成传出参数；要留意的是几种一定会移动或拷贝的写法。纯函数属性是向编译器做承诺：说对了少调几次，说错了是未定义行为。</description></item><item><title>交易系统笔记 #8：CRTP 与静态多态</title><link>https://jasper-aa64.github.io/zh/posts/crtp-static-polymorphism/</link><pubDate>Mon, 12 Oct 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/posts/crtp-static-polymorphism/</guid><description>基类写流程、子类填细节，虚函数在运行时选，CRTP 在编译期选。省下的不只是一次间接跳转：调用能整个内联，对象里也没有 vptr。代价是 Base&lt;A&gt; 和 Base&lt;B&gt; 毫无关系，放不进同一个容器。</description></item><item><title>交易系统笔记 #9：循环优化</title><link>https://jasper-aa64.github.io/zh/posts/loop-optimization/</link><pubDate>Mon, 12 Oct 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/posts/loop-optimization/</guid><description>点积循环 -O3 报告“已向量化”，四个加法却还是一个等一个：浮点加法不满足结合律，编译器不敢换顺序。循环优化真正要你管的，是这类挡住编译器的地方，和它不会替你做的改动。</description></item><item><title>低时延交易 —— 内存延迟与带宽</title><link>https://jasper-aa64.github.io/zh/posts/low-latency-memory-latency-bandwidth/</link><pubDate>Fri, 09 Oct 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/posts/low-latency-memory-latency-bandwidth/</guid><description>一次内存访问约 100 ns、运回 64 B，一次一个只有 0.64 GB/s，可一个核顺序读能拿到 15–60 GB/s，差出来的倍数就是同时在路上的请求数。下一次读的地址要不要等这一次读回来，决定了代码受延迟还是受带宽限制。</description></item><item><title>交易系统笔记 #7:分支预测与分支优化</title><link>https://jasper-aa64.github.io/zh/posts/branch-prediction-branch-optimization/</link><pubDate>Fri, 09 Oct 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/posts/branch-prediction-branch-optimization/</guid><description>前端每个周期都要取指令，可一条分支要十几个周期后才算出往哪走，所以只能先猜。猜对几乎不花钱，猜错要把之后的活全部扔掉，约 15–20 个周期。同一条 if，数据有规律就猜得准，随机就只能猜对一半；而编译器可能早已把它变成了 cmov。</description></item><item><title>低时延交易 —— MESI 缓存一致性</title><link>https://jasper-aa64.github.io/zh/posts/low-latency-mesi-cache-coherence/</link><pubDate>Sun, 04 Oct 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/posts/low-latency-mesi-cache-coherence/</guid><description>MESI 只执行一条规矩：一条缓存行，要么只有一个核能写，要么有任意多个核只能读，两者不会同时存在。四个状态就是两个问题的答案——别人有没有这一行？我改过没有？协议里的每一笔代价，不是在层与层之间搬数据，就是在等另一个核确认。伪共享、SPSC 队列的缓存游标、读者越多生产者越慢，都是同一台状态机。一致性仍然不保证多字读取不撕裂，也不管不同地址的写谁先谁后，那是内存屏障的事。</description></item><item><title>交易系统笔记 #5：共享内存里的 SPMC 广播环</title><link>https://jasper-aa64.github.io/zh/posts/spmc-shared-memory-broadcast-ring/</link><pubDate>Sun, 04 Oct 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/posts/spmc-shared-memory-broadcast-ring/</guid><description>行情只有一个生产者，却有好几个消费者，每个都要看到每一条，而生产者是热路径，不能等任何人。答案是广播环：每个槽位自带序号戳，读者各持私有游标，生产者写满一圈就覆盖最老的槽位，从不读读者的状态。代价转给了读者：套圈是无声的；读完再复查一次戳，只有在写者先作废戳的前提下才抓得到写了一半的槽位；追上来的读者会抢走生产者要写的那一行，全速写时每条都变贵。放进共享内存再加三条：元素里不能有任何指针，第一圈每 32 条碰一次缺页，读者重启不存游标就全丢。</description></item><item><title>交易系统笔记 #6：内存对齐、布局与缓存和 DRAM 的几何</title><link>https://jasper-aa64.github.io/zh/posts/alignment-layout-cache-dram-geometry/</link><pubDate>Sun, 04 Oct 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/posts/alignment-layout-cache-dram-geometry/</guid><description>访问从来不按字节付钱，按缓存行付；地址决定了碰哪几条行、这些行争哪个缓存组、住在哪个 DRAM 通道和 bank。第一部分讲对齐：为什么按 8 对齐的 double 不会跨行、填充为什么是为了数组、alignas 写在类型上和成员上的区别、堆上怎么拿 64 和 2 MiB 对齐。第二部分讲布局：一次访问碰几条行，AoS 还是 SoA，去规范化砍掉的是依赖链。第三部分进到硬件：L1 用地址的 bit 6–11 选组，相差 4 KiB 的数据不管缓存多空都在抢 8 个格子（M1 上实测）；DRAM 一个 bank 只有一个行缓冲，命中 14 ns、冲突 41 ns；按列遍历慢是五层叠加；刷新带来软件消不掉的 P99；热行步长和通道数互质，通道才均衡。</description></item><item><title>交易系统笔记 #4：无锁队列与 Micro-Batching</title><link>https://jasper-aa64.github.io/zh/posts/lock-free-queue-logger-micro-batching/</link><pubDate>Sun, 20 Sep 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/posts/lock-free-queue-logger-micro-batching/</guid><description>无锁 SPSC 队列成立，是因为每个共享变量只有一个写者：每一边只写自己的下标，只读对方的。剩下的工作是别让这个性质被悄悄破坏：元素个数用两个下标相减而不是共享计数器，对方的下标缓存一份、只在看起来满了或空了时才刷新，日志按文本段推而不是按字符推。SPSC 只在“失败就返回”的接口下才是 wait-free，有界队列满了总得在丢弃和阻塞之间选一个。</description></item><item><title>交易系统笔记 #3：内存池与分配器</title><link>https://jasper-aa64.github.io/zh/posts/hot-path-memory-allocators/</link><pubDate>Thu, 17 Sep 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/posts/hot-path-memory-allocators/</guid><description>这篇里的每一种技术，都是在不同的层面上做同一件事：把一个不确定的、牵涉内核的操作从热路径上挪走，强制它在开头一次性发生完，不让它伏击任何一个请求。对象池用永远 O(1) 的空闲链表，换掉 malloc 那种耗时可变的搜索。mlockall 加上仔细的 mallopt 调优，阻止内核悄悄把页收回——预先缺页则补上 mlock 单独留下的那个口子。手写的 arena 分配器让 STL 本身配合一个由 bump 指针内存支撑的自定义分配器。std：：pmr 用标准库的方式解决同一个「让 STL 配合」的问题——拿编译期的速度换运行时的灵活。</description></item><item><title>交易系统笔记 #2：内存序与缓存行</title><link>https://jasper-aa64.github.io/zh/posts/memory-ordering-false-sharing-dependency-chains/</link><pubDate>Tue, 15 Sep 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/posts/memory-ordering-false-sharing-dependency-chains/</guid><description>同一套缓存硬件带出两个不相干的问题。正确性：写先进 store buffer，别的核要过一段时间才看得见，靠 release/acquire 配对补上顺序。性能：就算每个值都读对了，两个核抢同一条缓存行（伪共享）照样慢，靠缓存行布局解决。</description></item></channel></rss>