<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>首页 on Jasper</title><link>https://jasper-aa64.github.io/zh/</link><description>Recent content in 首页 on Jasper</description><generator>Hugo</generator><language>zh-CN</language><lastBuildDate>Wed, 04 Nov 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://jasper-aa64.github.io/zh/index.xml" rel="self" type="application/rss+xml"/><item><title>IV：量化 C++ 开发、infra 题目和知识点</title><link>https://jasper-aa64.github.io/zh/interview/quant-infra-cpp/</link><pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/interview/quant-infra-cpp/</guid><description>&lt;p&gt;这组题来自一位第一次面量化方向的同学，岗位是 C++ 开发、交易系统开发和 infra，面的是几家传统量化私募和一家币圈量化。它的特点很集中：&lt;strong&gt;几乎不问项目，问答从 C++ 一路问到 CPU 和内核，手撕题没有一道是常见的算法题，全是并发和内存管理。&lt;/strong&gt; 所以准备的重点不是刷题，而是把“这行代码在机器上发生了什么”讲清楚。&lt;/p&gt;</description></item><item><title>IELTS：代码结构</title><link>https://jasper-aa64.github.io/zh/projects/ielts-studio-structure/</link><pubDate>Fri, 30 Oct 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/projects/ielts-studio-structure/</guid><description>&lt;p&gt;IELTS 是我做的雅思口语和写作练习工具，最近加了模拟面试。代码在 &lt;a href="https://github.com/Jasper-aa64/AI-realtime_speech-for-IELTS-Job_Interview"&gt;GitHub&lt;/a&gt;，最新的是 &lt;code&gt;desktop-realtime-voice&lt;/code&gt; 分支。这篇以现在的代码为基线，按三个问题讲：谁在什么时候干活，数据长什么样、放在哪，为什么这样切。第 5 节专门列现在的问题，踩坑的经过放在&lt;a href="../ielts-studio-retrospective/"&gt;复盘&lt;/a&gt;里。&lt;/p&gt;</description></item><item><title>A1：复杂度、二分、双指针、前缀和</title><link>https://jasper-aa64.github.io/zh/algo/a1-complexity-binary-search-two-pointers-prefix-sums/</link><pubDate>Thu, 29 Oct 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/algo/a1-complexity-binary-search-two-pointers-prefix-sums/</guid><description>&lt;p&gt;这一轮的四样东西都在数组上做查询，目标都是把暴力的 $O(n^2)$ 降下来。降得下来，靠的是两种性质：一种是单调性（二分、双指针），一种是能相减的累计量（前缀和、差分）。复杂度放在最前面，因为它决定一道题该往哪个方向想。&lt;/p&gt;</description></item><item><title>IV：A 股业务知识</title><link>https://jasper-aa64.github.io/zh/interview/a-share-business/</link><pubDate>Tue, 03 Nov 2026 00:00:00 +0800</pubDate><guid>https://jasper-aa64.github.io/zh/interview/a-share-business/</guid><description>&lt;p&gt;QD 岗位问业务，要的不是交易所协议的每个字段，而是两件事：你真的处理过这些数据，以及你知道哪些地方会让研究侧和实盘算出来不一样。下面按这个深度整理，先是 Level-2 行情本身，再是用它算的几个因子。&lt;/p&gt;</description></item><item><title>IELTS：复盘</title><link>https://jasper-aa64.github.io/zh/projects/ielts-studio-retrospective/</link><pubDate>Fri, 30 Oct 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/projects/ielts-studio-retrospective/</guid><description>&lt;p&gt;这篇是 &lt;a href="../ielts-studio-structure/"&gt;IELTS&lt;/a&gt; 的复盘。现在的代码是什么样、有哪些问题，结构篇第 5 节已经列全了，这里讲这些问题是怎么来的，以及下次会怎么做。先说结论：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;复用要复用线程模型，不能只复用类。&lt;/strong&gt; IELTS 命令行版复用了 &lt;code&gt;RealtimeClient&lt;/code&gt; 和音频类，但没复用麦克风、播放两个线程和状态机，结果考官的语音发出去了，却没人播。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;新写的东西先打通一条最简单的端到端。&lt;/strong&gt; 10 月初新写的 &lt;code&gt;native_voice&lt;/code&gt; 把最底下的设备层写得很干净，但网络、识别、合成一样都没有，接不进产品。10 月底换了做法：先把旧原型的连接层修好，再在上面写一层只管&amp;quot;说一段、听一段&amp;quot;的语音会话，第二天就接进了桌面壳。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;省事的设计都有代价，要写下来。&lt;/strong&gt; 半双工防住了回声，代价是不能打断；拿对话模型当朗读器，省掉了一个语音合成服务，代价是没法校验它有没有照读。新引擎改用了火山的指定文本接口，后一条才算解决。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;接口文档里的参数，要实测过才算数。&lt;/strong&gt; 判停窗口要另开一个开关才生效，必传的字段旧代码一直没发，这两件都是对着文档逐项核、再实测才发现的。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;网站这边三条&lt;/strong&gt;：慢在调用通道，不在逻辑；修 bug 先拿证据；&amp;ldquo;什么时候算失败&amp;quot;只能由一个地方判。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="1-七个月换了四次样子"&gt;&lt;span class="h-num"&gt;1.&lt;/span&gt; 七个月，换了四次样子&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;时间&lt;/th&gt;
 &lt;th&gt;形态&lt;/th&gt;
 &lt;th&gt;做了什么&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;3 月&lt;/td&gt;
 &lt;td&gt;C++/Qt 桌面原型&lt;/td&gt;
 &lt;td&gt;求职面试模拟：读简历 PDF，大模型出题，用火山引擎实时语音对话服务一问一答；最后用 cppcheck 和 ASan 查了一遍&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;5 月上旬&lt;/td&gt;
 &lt;td&gt;C++ 命令行&lt;/td&gt;
 &lt;td&gt;改成 IELTS 口语的三个部分，评分可以接 Codex、OpenAI 接口或 Claude&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;5 月中&lt;/td&gt;
 &lt;td&gt;单文件 Python 网站&lt;/td&gt;
 &lt;td&gt;网页录音、报告、钱包；做了评分校准和一次无人值守的长跑巡检&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;5 月中到月底&lt;/td&gt;
 &lt;td&gt;Django&lt;/td&gt;
 &lt;td&gt;迁到 Django，旧服务退役；接 HTTP 流式追问、WASM 音频预处理、实时识别&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;6 到 9 月&lt;/td&gt;
 &lt;td&gt;补功能和收尾&lt;/td&gt;
 &lt;td&gt;语料库、Takeaway、拼写间隔复习、公网提速、新手引导、P3 题库进度、部署文档&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;10 月&lt;/td&gt;
 &lt;td&gt;Qt 桌面壳&lt;/td&gt;
 &lt;td&gt;用 QtWebEngine 套网页；加了面试模式、AI 助手；新写 &lt;code&gt;native_voice&lt;/code&gt; 设备层；对旧 C++ 语音做了一次审计&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;10 月底到 11 月初&lt;/td&gt;
 &lt;td&gt;实时语音进桌面壳&lt;/td&gt;
 &lt;td&gt;连接层改成一个 I/O 线程独占；新写与内容无关的 &lt;code&gt;VoiceSession&lt;/code&gt;；面试追问压力改成实时语音，其余练习用它出字幕&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;提交数按月是：3 月 13 个，5 月 406 个，6 月 143 个，7 到 9 月一共 25 个，10 月 156 个，11 月到 1 日为止 1 个。从 5 月起，大部分代码是我写规格、Codex 或 Claude Code 实现、我验收。&lt;/p&gt;</description></item><item><title>IV：Python 与 C++ 算得不一样的地方</title><link>https://jasper-aa64.github.io/zh/interview/python-cpp-consistency/</link><pubDate>Tue, 03 Nov 2026 00:00:00 +0800</pubDate><guid>https://jasper-aa64.github.io/zh/interview/python-cpp-consistency/</guid><description>&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;研究和实盘要逐值对得上&lt;/strong&gt;：研究员给的是 pandas 写的因子，实盘用 C++ 重写，同一份行情喂进去，两边每个值都要一样（或在约定的容差内）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;对不上多半不是因子逻辑写错了&lt;/strong&gt;：更常见的是两边的默认行为不同。比如 pandas 默认跳过 NaN，C++ 的算术不会；pandas 的 &lt;code&gt;std&lt;/code&gt; 默认除以 n − 1，NumPy 默认除以 n。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;每一条都回答两个问题&lt;/strong&gt;：两边默认各怎么做，C++ 怎么写才能和研究侧对上。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="1-数值本身浮点加法不满足结合律"&gt;&lt;span class="h-num"&gt;1.&lt;/span&gt; 数值本身：浮点加法不满足结合律&lt;/h2&gt;
&lt;h3 id="11-求和顺序"&gt;&lt;span class="h-num"&gt;1.1&lt;/span&gt; 求和顺序&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;为什么加的顺序会改结果&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>IV：QD CV 技能行</title><link>https://jasper-aa64.github.io/zh/interview/cv-skills-review/</link><pubDate>Wed, 04 Nov 2026 00:00:00 +0800</pubDate><guid>https://jasper-aa64.github.io/zh/interview/cv-skills-review/</guid><description>&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;简历上写了“熟悉”，面试官就会顺着往下问&lt;/strong&gt;：这一页按简历“专业技能”的顺序，每一行挑出最可能被问到的几题，不求全。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;标 ★ 的先过&lt;/strong&gt;：量化私募的 C++ 岗最常问语言、容器、智能指针和并发基础；体系结构和低延迟优化是简历的重点，被问到要能落到数字和自己做过的事上。没标 ★ 的扫一眼，能说出一两句就行。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;复习的方法是先盖住“答到哪一层”自己答&lt;/strong&gt;：答不上，或者只能说出结论、说不出原因，就点开“本站”那一列的页面回看。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;复习顺序是知识点 → 实习 → 项目 1&lt;/strong&gt;：这一页是第一步；实习各条和项目 1 的追问另外过。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="cpp"&gt;&lt;span class="h-num"&gt;1.&lt;/span&gt; C++：STL、智能指针、模板、RAII、移动语义、CRTP、RVO&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;这一行几乎一定会问&lt;/strong&gt;：C++ 岗的一面通常从这里开始，问的是“底层怎么实现”，不是“怎么用”。&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;题目&lt;/th&gt;
 &lt;th&gt;答到哪一层&lt;/th&gt;
 &lt;th&gt;本站&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;★ &lt;code&gt;vector&lt;/code&gt; 扩容&lt;/td&gt;
 &lt;td&gt;容量满了就申请一块更大的内存（libstdc++ 是 2 倍，MSVC 是 1.5 倍），把元素搬过去，释放旧的，所以 &lt;code&gt;push_back&lt;/code&gt; 均摊 O(1)。搬完以后旧的迭代器、指针、引用全部失效。元素的移动构造是 &lt;code&gt;noexcept&lt;/code&gt; 才用移动，否则用拷贝，为的是扩容中途抛异常时原数组还完好。追问“延迟尖峰怎么避免”：均摊 O(1) 是平均，扩容那一次是 O(n)，大块还要缺页、&lt;code&gt;munmap&lt;/code&gt;，可达几 ms；办法是启动时 &lt;code&gt;reserve&lt;/code&gt; 到上限并把页先碰一遍，每轮 &lt;code&gt;clear()&lt;/code&gt; 复用，上限确定就用定长环形缓冲区，必须能长就换 &lt;code&gt;deque&lt;/code&gt; 这种不搬旧元素的结构&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/ref/stl-containers/#vector-growth"&gt;STL 容器&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;★ &lt;code&gt;map&lt;/code&gt; 和 &lt;code&gt;unordered_map&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;map&lt;/code&gt; 是红黑树，有序，查找 O(log n)；&lt;code&gt;unordered_map&lt;/code&gt; 是哈希表，桶数组加链表节点，平均 O(1)，最坏 O(n)。元素数超过“桶数 × 负载因子（默认 1）”就 rehash，所有元素重新分桶，迭代器失效，但指向元素的指针和引用不失效。热路径上按代码查找，能用数组下标就别用哈希表。追问“为什么慢”：两者都是节点式，每一步要读的地址都得等上一个节点读回来（pointer chasing），&lt;code&gt;map&lt;/code&gt; 查一次是 20 多步依赖的读，&lt;code&gt;unordered_map&lt;/code&gt; 命中也要 3 步；替代是排好序的 &lt;code&gt;vector&lt;/code&gt;、开放寻址哈希表、编号直接索引数组&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/ref/stl-containers/#pointer-chasing"&gt;STL 容器&lt;/a&gt;、&lt;a href="https://jasper-aa64.github.io/zh/ref/skiplist-rbtree-bplustree/"&gt;三种有序结构&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;★ &lt;code&gt;shared_ptr&lt;/code&gt; 怎么实现&lt;/td&gt;
 &lt;td&gt;对象指针加一个控制块，控制块里有强引用计数、弱引用计数和删除器。计数用原子操作增减。&lt;code&gt;make_shared&lt;/code&gt; 把对象和控制块一次分配。两个对象互相持有 &lt;code&gt;shared_ptr&lt;/code&gt; 会循环引用、永远不释放，其中一边改成 &lt;code&gt;weak_ptr&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/ref/smart-pointers/"&gt;智能指针&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;★ &lt;code&gt;shared_ptr&lt;/code&gt; 多线程下安全吗&lt;/td&gt;
 &lt;td&gt;分三样说。计数安全：各线程各拿一份复制、销毁都没问题，因为计数是原子加减。同一个 &lt;code&gt;shared_ptr&lt;/code&gt; 变量一边赋值一边读是数据竞争，C++20 用 &lt;code&gt;std::atomic&amp;lt;std::shared_ptr&amp;lt;T&amp;gt;&amp;gt;&lt;/code&gt;，之前用 &lt;code&gt;std::atomic_load&lt;/code&gt; / &lt;code&gt;std::atomic_store&lt;/code&gt; 或者加锁。指向的对象不受保护，改它要自己加锁。追问：&lt;code&gt;use_count()&lt;/code&gt; 只能当参考；析构在最后放手的线程里执行；&lt;code&gt;weak_ptr&lt;/code&gt; 要 &lt;code&gt;lock()&lt;/code&gt;，不要先 &lt;code&gt;expired()&lt;/code&gt; 再用&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/ref/smart-pointers/#shared-ptr-threads"&gt;智能指针 3.3&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;★ &lt;code&gt;unique_ptr&lt;/code&gt; 的开销&lt;/td&gt;
 &lt;td&gt;独占所有权，只能移动不能拷贝。用默认删除器时和裸指针一样大，解引用也一样快，所以没有理由用裸指针管理所有权&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/ref/smart-pointers/"&gt;智能指针&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;★ 移动语义和 &lt;code&gt;std::move&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;右值引用绑定“马上要死的对象”，移动构造把它的资源（比如堆上的缓冲区）直接拿过来、把源置空，省掉一次深拷贝。&lt;code&gt;std::move&lt;/code&gt; 本身什么都不移动，只是 &lt;code&gt;static_cast&lt;/code&gt; 成右值引用，让重载选中移动版本。完美转发是万能引用加 &lt;code&gt;std::forward&lt;/code&gt;，保持实参原来的左值或右值身份&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/ref/move-semantics/"&gt;移动语义&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;★ RAII&lt;/td&gt;
 &lt;td&gt;资源在构造函数里获取、在析构函数里释放。抛异常时栈展开会调用已构造对象的析构函数，所以资源不会泄漏，&lt;code&gt;lock_guard&lt;/code&gt; 就是这样保证锁一定被释放。析构函数不要抛异常&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/ref/special-members-raii/"&gt;构造、拷贝、析构和 RAII&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;★ 虚函数怎么实现&lt;/td&gt;
 &lt;td&gt;每个有虚函数的对象开头有一个 vptr，指向它所属类的 vtable；同一个虚函数在父类和每个子类的表里都在同一格，所以调用时编译器只要“读 vptr → 读第几格 → 跳过去”。贵的地方主要不是这次间接跳转，而是编译器不知道调的是谁，没法内联。基类的析构函数要写成 virtual&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/ref/virtual-functions/"&gt;虚函数&lt;/a&gt;、&lt;a href="https://jasper-aa64.github.io/zh/ref/object-layout/"&gt;对象布局&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;★ CRTP 为什么能替代虚函数（简历上写了）&lt;/td&gt;
 &lt;td&gt;子类把自己当模板参数传给基类，基类里 &lt;code&gt;static_cast&amp;lt;Derived*&amp;gt;(this)&lt;/code&gt; 去调，编译期就知道调的是谁，能内联，对象里也没有 vptr。代价是 &lt;code&gt;Base&amp;lt;A&amp;gt;&lt;/code&gt; 和 &lt;code&gt;Base&amp;lt;B&amp;gt;&lt;/code&gt; 是两个不相关的类型，放不进同一个容器&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/posts/crtp-static-polymorphism/"&gt;CRTP 与静态多态&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;RVO 和 NRVO&lt;/td&gt;
 &lt;td&gt;C++17 起，返回一个纯右值（&lt;code&gt;return T(...)&lt;/code&gt;）保证直接构造在调用方的位置上，不拷贝也不移动。返回具名局部变量（NRVO）编译器通常会做，但不保证。&lt;code&gt;return std::move(x);&lt;/code&gt; 反而会挡住 NRVO&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/posts/rvo-pure-functions/"&gt;返回值优化&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;模板&lt;/td&gt;
 &lt;td&gt;每种模板实参实例化一份代码，代价是编译时间和代码体积；特化和偏特化给特定类型换实现；约束模板参数从 SFINAE 到 C++20 的 concepts&lt;/td&gt;
 &lt;td&gt;待补&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="cpu"&gt;&lt;span class="h-num"&gt;2.&lt;/span&gt; CPU 体系结构：缓存、MESI、store buffer、ROB、LFB、分支预测、TLB、NUMA&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;这一行写得最长，被问到就要能落到数字&lt;/strong&gt;：先在&lt;a href="https://jasper-aa64.github.io/zh/ref/cpu-memory/"&gt;硬件地图&lt;/a&gt;上说清它在哪一层，再给量级，再连到自己做过的优化。&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;题目&lt;/th&gt;
 &lt;th&gt;答到哪一层&lt;/th&gt;
 &lt;th&gt;本站&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;★ 各级缓存的延迟&lt;/td&gt;
 &lt;td&gt;L1D 约 1 ns（4–5 周期），L2 约 3–5 ns，L3 约 10–30 ns，内存约 70–120 ns；L1、L2 每核私有，L3 全核共享。缓存行 64 B，一次至少搬一整行&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/ref/cpu-memory/"&gt;CPU 与内存&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;★ 伪共享（简历：环形缓冲区计数按缓存行隔离）&lt;/td&gt;
 &lt;td&gt;两个线程写的变量不同，但在同一条缓存行上。每次写都要先拿到这条行的独占权，行就在两个核之间来回搬，一次跨核约 20–100 ns。用 &lt;code&gt;alignas(64)&lt;/code&gt; 把它们隔到不同的行。实习里环形缓冲区的读写计数就是这样隔开的，写入延迟中位数降了约 40%&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/posts/memory-ordering-false-sharing-dependency-chains/"&gt;#2 内存序与缓存行&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;★ MESI&lt;/td&gt;
 &lt;td&gt;每条行在每个核里有四种状态：M（改过、只有我有）、E（没改、只有我有）、S（多个核都有）、I（无效）。写之前必须拿到独占：别的核的副本先作废，回了 ACK 才能写。读一条在别人那里是 M 的行，由那个核直接转发过来&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/posts/low-latency-mesi-cache-coherence/"&gt;MESI&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;store buffer 和 StoreLoad 重排&lt;/td&gt;
 &lt;td&gt;store 先进 store buffer，流水线不等独占权就往下走，等的是队首那条 store。所以“先写 A、再读 B”可能让别的核看到读在写前面，这是 x86 唯一允许的重排。&lt;code&gt;seq_cst&lt;/code&gt; 的 store 在 x86 上编成 &lt;code&gt;xchg&lt;/code&gt;，就是为了堵住它&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/posts/memory-ordering-false-sharing-dependency-chains/"&gt;#2&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;乱序执行和 ROB&lt;/td&gt;
 &lt;td&gt;指令按依赖关系乱序执行，按程序顺序退休；ROB 有几百项。一条读内存的 load 卡在队首时，后面的指令把 ROB 填满就只能停下&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/ref/cpu-memory/"&gt;CPU 与内存&lt;/a&gt;、&lt;a href="https://jasper-aa64.github.io/zh/posts/low-latency-cache-case-study/"&gt;缓存案例&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;LFB 和内存级并行&lt;/td&gt;
 &lt;td&gt;每个核约 10–16 个 LFB，同时在路上的 L1 缺失最多这么多。单核带宽 ≈ 在途数 × 64 B ÷ 延迟，所以单核读内存到不了整机带宽&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/posts/low-latency-memory-latency-bandwidth/"&gt;内存延迟与带宽&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;★ 分支预测&lt;/td&gt;
 &lt;td&gt;一条分支要十几个周期后才知道往哪走，CPU 只能先猜。猜对几乎不花钱，猜错要把之后的活全部扔掉，约 15–20 个周期。数据有规律就猜得准，随机就只能猜对一半，经典例子是数组排序后同一个循环快好几倍&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/posts/branch-prediction-branch-optimization/"&gt;#7 分支预测&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;TLB 和大页&lt;/td&gt;
 &lt;td&gt;TLB 缓存虚拟地址到物理地址的翻译，每个核一份，L1 dTLB 64–96 项，二级 1500–3000 项，每项管一页。4 KiB 的页一共只盖几 MiB；2 MiB 的大页一项盖 512 倍。没命中要走页表，多 15–30 ns&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/posts/low-latency-context-switch-page-table-tlb/"&gt;上下文切换、页表与 TLB&lt;/a&gt;、&lt;a href="https://jasper-aa64.github.io/zh/posts/low-latency-memory-latency-bandwidth/"&gt;内存延迟与带宽&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;NUMA&lt;/td&gt;
 &lt;td&gt;多插槽的机器，每个插槽有自己的内存，读另一个插槽的内存更慢、带宽更低。物理页在第一次写的时候落在写它的那个节点上（first touch），所以内存要在绑好核的线程里初始化，或者用 &lt;code&gt;numactl --membind&lt;/code&gt; 指定&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/posts/cpu-affinity-core-isolation-numa/"&gt;#1 CPU 隔离与 NUMA&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="optimization"&gt;&lt;span class="h-num"&gt;3.&lt;/span&gt; 低延迟优化方法&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;每一条都能挂到实习里做过的事&lt;/strong&gt;：答完原理，接一句“我在哪里用过、效果多少”。&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;题目&lt;/th&gt;
 &lt;th&gt;答到哪一层&lt;/th&gt;
 &lt;th&gt;本站&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;★ 热路径为什么不能 &lt;code&gt;malloc&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;malloc&lt;/code&gt; 的耗时不确定：可能要抢锁、找合适的空闲块，还可能调 &lt;code&gt;brk&lt;/code&gt; / &lt;code&gt;mmap&lt;/code&gt; 进内核，第一次碰到新页还会缺页。内存池启动时一次分好固定大小的块，用 free list 管理，分配和释放都是 O(1)；arena 只往后顺序分配，到时候整体释放。实习里回测按交易日复用同一块内存，耗时降了约 44%&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/posts/hot-path-memory-allocators/"&gt;#3 内存池与分配器&lt;/a&gt;、&lt;a href="https://jasper-aa64.github.io/zh/ref/new-malloc-page-fault/"&gt;new 一次走到哪&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;★ 依赖链和多累加器（简历：拆分串行累加的依赖链）&lt;/td&gt;
 &lt;td&gt;只用一个累加器时，每次加法都要等上一次的结果，被加法的延迟（约 4 周期）卡住，CPU 的两个加法单元大半时间空着。拆成 4–8 个累加器各加各的，最后再合起来，吞吐就上去了。代价是浮点加法的顺序变了，末位会变，和研究侧对拍时要按容差比（&lt;a href="https://jasper-aa64.github.io/zh/interview/python-cpp-consistency/"&gt;Python 与 C++&lt;/a&gt; 1.1）&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/posts/loop-optimization/#fp-sum"&gt;#9 3.3–3.4&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;★ 无分支写法（简历上写了）&lt;/td&gt;
 &lt;td&gt;把依赖数据、猜不准的 &lt;code&gt;if&lt;/code&gt; 改成 &lt;code&gt;cmov&lt;/code&gt;、掩码或查表，不用猜就不会猜错。分支本来就猜得准时反而可能更慢：分支猜对了可以提前往下跑，&lt;code&gt;cmov&lt;/code&gt; 必须等条件算出来&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/posts/branch-prediction-branch-optimization/"&gt;#7&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;★ 数据布局&lt;/td&gt;
 &lt;td&gt;按访问模式摆数据：热字段放在一起、冷字段挪出去，一次处理只碰少数几条缓存行；按字段批量扫描时用 SoA（每个字段一个数组）比 AoS 更省带宽，也更好向量化&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/posts/alignment-layout-cache-dram-geometry/"&gt;#6 内存对齐与布局&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;预取和预热&lt;/td&gt;
 &lt;td&gt;软件预取（&lt;code&gt;__builtin_prefetch&lt;/code&gt;）提前几百 ns 把下一批数据要过来，提前太多会把还要用的行挤掉。预热是启动时把内存页都碰一遍、把热路径空跑几遍，让缺页、缓存和分支预测器都先热起来；实习里启动时预触内存页，消掉了首轮的缺页&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/posts/prefetch-warmup-double-buffer/"&gt;#14 预取、预热与双缓冲&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;SIMD&lt;/td&gt;
 &lt;td&gt;一条指令同时算 4 个（AVX2）或 8 个（AVX-512）double。编译器自动向量化要求循环之间没有依赖、指针不会互相别名、内存连续；浮点求和要放开结合律才会被向量化，这又会改末位&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/posts/simd-vectorization-intrinsics/"&gt;#15 SIMD&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;强度削弱和定点数&lt;/td&gt;
 &lt;td&gt;把除法换成乘以倒数或移位，把乘法换成加法；价格用放大后的整数存，既快，又不会有浮点取整的问题&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/posts/strength-reduction-arithmetic/"&gt;#12 强度削弱&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="linux-tuning"&gt;&lt;span class="h-num"&gt;4.&lt;/span&gt; Linux 低延迟调优：绑核、隔离、中断亲和、实时调度、大页&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;QD 岗问得少，能说清每个手段解决什么就够&lt;/strong&gt;：重点是“为什么要这样做”，命令记个名字就行。&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;题目&lt;/th&gt;
 &lt;th&gt;答到哪一层&lt;/th&gt;
 &lt;th&gt;本站&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;绑核和核心隔离&lt;/td&gt;
 &lt;td&gt;绑核（&lt;code&gt;pthread_setaffinity_np&lt;/code&gt;、&lt;code&gt;taskset&lt;/code&gt;）让线程不被挪到别的核，否则换核后 L1、L2 和 TLB 都是冷的。&lt;code&gt;isolcpus&lt;/code&gt; 让调度器不往这些核上放别的任务，&lt;code&gt;nohz_full&lt;/code&gt; 再去掉时钟中断&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/posts/cpu-affinity-core-isolation-numa/"&gt;#1&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;中断亲和&lt;/td&gt;
 &lt;td&gt;网卡中断默认可能落在热线程的核上，打断它；改 &lt;code&gt;/proc/irq/N/smp_affinity&lt;/code&gt; 把中断挪到别的核&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/posts/cpu-affinity-core-isolation-numa/"&gt;#1&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;实时调度和优先级反转&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;SCHED_FIFO&lt;/code&gt; 的线程不会被普通线程抢占。优先级反转：低优先级线程拿着锁，被中优先级线程抢走 CPU，高优先级线程等锁一直等下去；办法是优先级继承锁&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/posts/scheduling-priority-inversion-kernel-tuning/"&gt;#19 调度与优先级反转&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;大页&lt;/td&gt;
 &lt;td&gt;用 hugetlbfs 预留大页，或者开透明大页（THP），减少 TLB 缺失。THP 的 &lt;code&gt;khugepaged&lt;/code&gt; 会在后台合并页、改页表，可能带来抖动，低延迟场景常关掉&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/posts/scheduling-priority-inversion-kernel-tuning/"&gt;#19&lt;/a&gt;、&lt;a href="https://jasper-aa64.github.io/zh/posts/low-latency-memory-latency-bandwidth/"&gt;内存延迟与带宽&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="concurrency"&gt;&lt;span class="h-num"&gt;5.&lt;/span&gt; 多线程与并发&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;基础题一定会问，无锁那几个是加分项&lt;/strong&gt;：互斥锁、条件变量、原子变量和内存序要答到“为什么这样写才对”。&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;题目&lt;/th&gt;
 &lt;th&gt;答到哪一层&lt;/th&gt;
 &lt;th&gt;本站&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;★ 内存序：relaxed、acquire、release、seq_cst&lt;/td&gt;
 &lt;td&gt;relaxed 只保证这一个变量的读写是原子的，不管前后顺序。release 写配上 acquire 读：读到这个值的线程，能看到写方在 release 之前写的所有东西，这就是“发布数据”的标准写法。seq_cst 再加一条所有线程看到同一个全局顺序。x86 上 acquire 和 release 都是普通的 &lt;code&gt;mov&lt;/code&gt;，只有 seq_cst 的 store 要 &lt;code&gt;xchg&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/posts/memory-ordering-false-sharing-dependency-chains/"&gt;#2&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;★ 条件变量为什么要配锁和 while&lt;/td&gt;
 &lt;td&gt;等待方在锁里检查条件，&lt;code&gt;wait&lt;/code&gt; 原子地“放锁并睡下”，否则检查完、睡下之前的通知会丢。醒来要在 &lt;code&gt;while&lt;/code&gt; 里重新检查条件，因为可能是虚假唤醒，也可能被别的线程抢先拿走了数据&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/ref/thread-sync/"&gt;线程同步&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;★ 死锁和怎么避免&lt;/td&gt;
 &lt;td&gt;四个条件：互斥、持有并等待、不可抢占、循环等待。实际最常用的办法是破坏循环等待：所有线程按同一顺序加锁，或者用 &lt;code&gt;std::scoped_lock&lt;/code&gt; 一次锁住多把&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/ref/thread-sync/"&gt;线程同步&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;★ 线程池&lt;/td&gt;
 &lt;td&gt;一个任务队列加几个工作线程，队列用互斥锁加条件变量保护；提交任务时用 &lt;code&gt;packaged_task&lt;/code&gt; 包一层，调用方拿 &lt;code&gt;future&lt;/code&gt; 等结果&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/ref/thread-pool-future/"&gt;线程池和 future&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;lock-free 和 wait-free&lt;/td&gt;
 &lt;td&gt;lock-free 保证总有某个线程在前进，但某个线程可能一直 CAS 失败重试；wait-free 保证每个线程的步数都有上界。SPSC 环满了就返回失败的接口是 wait-free，满了就空转等的是阻塞式&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/posts/wait-free-sticky-counter/"&gt;#17 wait-free&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;SPSC 无锁队列&lt;/td&gt;
 &lt;td&gt;生产者只写 tail、消费者只写 head，各自用 release 发布、用 acquire 读对方，不需要 CAS。容量取 2 的幂，下标用 &lt;code&gt;&amp;amp; (N-1)&lt;/code&gt; 代替取模；head 和 tail 放在不同的缓存行，再各自缓存一份对方的下标，少读对方的行&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/posts/lock-free-queue-logger-micro-batching/"&gt;#4 无锁队列&lt;/a&gt;、&lt;a href="https://jasper-aa64.github.io/zh/posts/thread-safe-queue/"&gt;#18 线程安全队列&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;自旋锁：TTAS、票据锁、MCS&lt;/td&gt;
 &lt;td&gt;TTAS 先用普通读等锁看起来空闲了再 CAS，等待时不反复抢独占权；票据锁按取号顺序拿锁，公平；MCS 每个等待者在自己的节点上自旋，锁释放时只惊动下一个&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/posts/spinlock-implementation/"&gt;#16 自旋锁&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="system"&gt;&lt;span class="h-num"&gt;6.&lt;/span&gt; Linux 系统编程：进程与 IPC、epoll、mmap、虚拟内存与缺页&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;进程线程和缺页最常被问&lt;/strong&gt;：网络那半句（TCP/IP、HTTP、WebSocket）简历上写的是“了解”，过一遍下面链的几页 ref 就够。&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;题目&lt;/th&gt;
 &lt;th&gt;答到哪一层&lt;/th&gt;
 &lt;th&gt;本站&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;★ 进程和线程的区别&lt;/td&gt;
 &lt;td&gt;进程是资源的容器：地址空间、fd 表、信号处理函数；线程是在容器里跑的执行流：寄存器、栈、TLS。线程切换和进程切换的直接开销差不多，都在 1 到几 µs；差别在切换以后，同一进程的线程还能接着用 TLB 和缓存&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/ref/process-thread-fork/"&gt;进程和线程&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;★ &lt;code&gt;fork&lt;/code&gt; 和写时复制&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;fork&lt;/code&gt; 只复制页表，父子进程共用物理页，页标成只读；谁先写，谁触发缺页，内核这时才复制那一页。多线程程序 &lt;code&gt;fork&lt;/code&gt; 后子进程里只剩调 &lt;code&gt;fork&lt;/code&gt; 的那个线程&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/ref/process-thread-fork/"&gt;进程和线程&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;★ 虚拟内存和缺页（简历：启动时预触内存页）&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;new&lt;/code&gt; → &lt;code&gt;malloc&lt;/code&gt; → &lt;code&gt;brk&lt;/code&gt; / &lt;code&gt;mmap&lt;/code&gt; 拿到的只是一段虚拟地址，第一次访问才缺页、分配物理页，每次几百 ns 到几 µs。所以实习里启动时先把内存全碰一遍，热路径上就不再缺页&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/ref/new-malloc-page-fault/"&gt;new 一次走到哪&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;IPC 有哪几种&lt;/td&gt;
 &lt;td&gt;管道、消息队列、共享内存、信号、socket。共享内存最快，数据不用经过内核拷贝，但同步要自己做&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/ref/ipc/"&gt;进程间通信&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;mmap&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;把文件或匿名内存映射进地址空间；读文件时少一次从内核缓冲区到用户缓冲区的拷贝，按页懒加载&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/ref/new-malloc-page-fault/"&gt;new 一次走到哪&lt;/a&gt;、&lt;a href="https://jasper-aa64.github.io/zh/ref/file-io/"&gt;文件 IO&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;epoll&lt;/td&gt;
 &lt;td&gt;用红黑树存要监听的 fd，就绪的 fd 由内核回调挂到就绪链表上，&lt;code&gt;epoll_wait&lt;/code&gt; 只取这个链表，不像 &lt;code&gt;select&lt;/code&gt; 那样每次扫一遍所有 fd。水平触发只要还有数据就一直通知，边缘触发只在状态变化时通知一次，要一次读到 &lt;code&gt;EAGAIN&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/ref/epoll-internals/"&gt;epoll&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;网络（简历写的是“了解”）&lt;/td&gt;
 &lt;td&gt;三次握手、四次挥手、TIME_WAIT；TCP 怎么做到可靠；一次 HTTP 请求的全过程&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/ref/tcp-states/"&gt;TCP 状态&lt;/a&gt;、&lt;a href="https://jasper-aa64.github.io/zh/ref/tcp-reliability/"&gt;TCP 可靠传输&lt;/a&gt;、&lt;a href="https://jasper-aa64.github.io/zh/ref/http-basics/"&gt;HTTP&lt;/a&gt;、&lt;a href="https://jasper-aa64.github.io/zh/ref/socket-programming/"&gt;socket 编程&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="tools"&gt;&lt;span class="h-num"&gt;7.&lt;/span&gt; 工具：perf、火焰图、TSC 测量、分位数、ASan/UBSan&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;这一行和实习、项目 1 都连着&lt;/strong&gt;：答工具怎么用时，顺手讲一次自己用它找到了什么。&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;题目&lt;/th&gt;
 &lt;th&gt;答到哪一层&lt;/th&gt;
 &lt;th&gt;本站&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;★ perf 怎么用&lt;/td&gt;
 &lt;td&gt;先 &lt;code&gt;perf stat&lt;/code&gt; 看整体：IPC、缓存缺失、分支预测失败；再 &lt;code&gt;perf record -g&lt;/code&gt; 加 &lt;code&gt;perf report&lt;/code&gt; 找热点函数；最后 &lt;code&gt;perf annotate&lt;/code&gt; 看热点落在哪几条指令上。火焰图横向宽度是采样占比，不是时间先后。实习里用它找到日志后台线程空转，调整轮询间隔后 CPU 占用降了 31%&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/posts/low-latency-perf-profiling/"&gt;perf 专题&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;★ 怎么测一段代码的延迟&lt;/td&gt;
 &lt;td&gt;用 &lt;code&gt;rdtsc&lt;/code&gt; / &lt;code&gt;rdtscp&lt;/code&gt; 读 TSC，现代 x86 的 TSC 恒定频率、各核同步；前后要加栅栏，防止被测的指令被乱序挪到计时外面；换算成 ns 要用校准过的频率；空测一次计时本身的开销再减掉&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/posts/tsc-latency-measurement/"&gt;#20 用 TSC 测延迟&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;★ 为什么报分位数不报平均&lt;/td&gt;
 &lt;td&gt;延迟分布是长尾的，平均值会被少数尖刺拉高，也可能把它们藏起来；报 P50、P99、P99.9，尖刺在哪个分位上一目了然。实习里单包处理延迟就是按中位数和 P99 报的&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/posts/trading-system-latency-metrics/"&gt;#21 交易系统性能衡量&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;ASan 和 UBSan&lt;/td&gt;
 &lt;td&gt;ASan 查越界、释放后使用、重复释放，程序会慢约 2 倍；UBSan 查有符号溢出、空指针、未对齐访问这类未定义行为。只在测试构建里开&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/interview/quant-infra-cpp/"&gt;IV：量化 C++ 题目&lt;/a&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;编译选项会不会改结果&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;-O2&lt;/code&gt; 换 &lt;code&gt;-O3&lt;/code&gt; 一般不改浮点结果；&lt;code&gt;-march=native&lt;/code&gt; 打开 FMA 会改末位；&lt;code&gt;-ffast-math&lt;/code&gt; 会删掉 NaN 判断、重排求和，因子代码里不能开&lt;/td&gt;
 &lt;td&gt;&lt;a href="https://jasper-aa64.github.io/zh/interview/python-cpp-consistency/"&gt;Python 与 C++&lt;/a&gt; 第 5 节&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;</description></item><item><title>C0：赛场上的数学</title><link>https://jasper-aa64.github.io/zh/algo/c0-contest-math-overview/</link><pubDate>Fri, 30 Oct 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/algo/c0-contest-math-overview/</guid><description>&lt;p&gt;比赛后半段的数学题，难点很少在定理本身：定理大多能背下来，卡住的地方是看不出这道题该用哪一个，或者看出来了却推不到能写代码的那一步。所以这篇不按定理排，按赛场上的动作排：先讲一道数学题拿到手怎么走（第 1 节），再分数论、组合计数、线性代数三块，每样东西都讲它解决什么、题面上什么信号该想到它、式子怎么推出来（第 2 到 4 节），最后从题面开始完整写三道题（第 5 节）。&lt;/p&gt;</description></item><item><title>CPU 与内存：地图、数字、怎么估算</title><link>https://jasper-aa64.github.io/zh/ref/cpu-memory/</link><pubDate>Thu, 08 Oct 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/ref/cpu-memory/</guid><description>&lt;p&gt;这页是随时翻的总图，不是一篇文章。三部分按“在哪 → 多大多快 → 怎么合起来算”排：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://jasper-aa64.github.io/zh/ref/cpu-memory/#map"&gt;存储层次&lt;/a&gt;：硬件地图，一次读写经过哪些框，每个框在哪篇文章里讲过。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jasper-aa64.github.io/zh/ref/cpu-memory/#numbers"&gt;性能参数&lt;/a&gt;：每个框的延迟、带宽、容量，同时能挂几个请求（MLP），以及 CPU 自己想吃多快（带宽需求）。都是帮助理解的量级，不对应哪台机器。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jasper-aa64.github.io/zh/ref/cpu-memory/#estimate"&gt;估算模型&lt;/a&gt;：用一个式子把第 2 部分的数串起来，判断一段代码慢在“搬得多”还是“搬得慢”。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;后面讲到新的硬件层，都补在这一页。往上一层，一条行情经过哪些线程，见&lt;a href="https://jasper-aa64.github.io/zh/ref/trading-architecture/"&gt;交易系统架构&lt;/a&gt;。&lt;/p&gt;</description></item><item><title>C++ 与 Linux：总图</title><link>https://jasper-aa64.github.io/zh/ref/cpp-linux/</link><pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/ref/cpp-linux/</guid><description>&lt;p&gt;这页放 C++ 程序在 Linux 上最常翻的总图，和&lt;a href="https://jasper-aa64.github.io/zh/ref/cpu-memory/"&gt;硬件地图&lt;/a&gt;是一对：硬件地图讲一次访存经过哪些硬件，这页讲在那之前，操作系统替程序做了什么。三张图：&lt;a href="https://jasper-aa64.github.io/zh/ref/cpp-linux/#map"&gt;进程地图&lt;/a&gt;讲一个进程的内存长什么样，&lt;a href="https://jasper-aa64.github.io/zh/ref/cpp-linux/#io"&gt;IO 地图&lt;/a&gt;讲数据怎么进出这个进程，&lt;a href="https://jasper-aa64.github.io/zh/ref/cpp-linux/#build"&gt;从源码到进程&lt;/a&gt;讲一个可执行文件是怎么编出来、又怎么变成进程的。细分的知识点（epoll、fork、动态链接……）写成单页，和这页一起放在 &lt;a href="https://jasper-aa64.github.io/zh/interview/#cpplinux"&gt;IV 栏的 C++ 与 Linux 这一组&lt;/a&gt;，这页只放总图和最常翻的表。&lt;/p&gt;
&lt;h2 id="map"&gt;&lt;span class="h-num"&gt;1.&lt;/span&gt; 进程地图：一个进程的虚拟地址空间&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://jasper-aa64.github.io/maps/process-map.zh.html" target="_blank" rel="noopener"&gt;&lt;img src="https://jasper-aa64.github.io/images/ref/process-map.zh.svg" alt="进程地图：左边是 x86-64 上一个进程的虚拟地址空间，从高到低依次是内核空间、非规范地址空洞、主线程的栈、vDSO、mmap 区（共享库、大块 malloc、其他线程的栈和 TLS、文件映射和共享内存）、没映射的空地、堆、.bss、.data、.rodata、.text 和最低 64 KiB 的不映射区；右边是 C++ 变量各放在哪、线程共享什么、页表和缺页怎么把虚拟地址落到物理内存、在自己机器上怎么看、出了错落在哪一段。" loading="lazy" decoding="async"&gt;&lt;/a&gt;&lt;/p&gt;</description></item><item><title>交易系统架构：地图、时间、怎么切</title><link>https://jasper-aa64.github.io/zh/ref/trading-architecture/</link><pubDate>Tue, 20 Oct 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/ref/trading-architecture/</guid><description>&lt;p&gt;这页是随时翻的架构总图，和&lt;a href="https://jasper-aa64.github.io/zh/ref/cpu-memory/"&gt;硬件地图&lt;/a&gt;是一对：那页讲一次访存经过哪些硬件，这页讲一条行情经过哪些线程。三部分按“在哪 → 多快 → 怎么切”排：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://jasper-aa64.github.io/zh/ref/trading-architecture/#map"&gt;架构地图&lt;/a&gt;：一条行情变成一笔成交，经过哪些框，每个框在哪篇文章里讲过。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jasper-aa64.github.io/zh/ref/trading-architecture/#numbers"&gt;时间&lt;/a&gt;：每一段大概多久，用来判断该抠哪一段。都是量级，不对应哪套系统。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jasper-aa64.github.io/zh/ref/trading-architecture/#rules"&gt;怎么切&lt;/a&gt;：工业级系统怎么分线程、在哪里放队列、哪里必须汇合。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;后面讲到新的组件，都补在这一页。&lt;/p&gt;
&lt;h2 id="map"&gt;&lt;span class="h-num"&gt;1.&lt;/span&gt; 架构地图：一条行情经过哪些框&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://jasper-aa64.github.io/maps/arch-map.zh.html" target="_blank" rel="noopener"&gt;&lt;img src="https://jasper-aa64.github.io/images/ref/arch-map.zh.svg" alt="交易系统架构地图：交易所的行情经网卡进行情接入线程，先写一份进广播环给落盘和监控，再按股票分到各分片线程算盘口和因子，汇合后进决策线程的策略、风控、订单管理和下单网关，经券商柜台到交易所；回报沿红线回到订单管理和账户状态，再回到策略。最下面一排是旁路：离线回放、落盘、监控、日志、交易员指令和客户端推送。" loading="lazy" decoding="async"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;绿框是热线程，各绑一个隔离核；蓝框是线程之间交接数据的结构；黄框是汇合点；最下面一排虚线框是旁路。黑线是行情到报单，红线是回报回来，灰色虚线是旁路。点图打开可交互的版本：点一个框看它做什么、为什么这样设计；选一条路径，一步一步看怎么走。&lt;/p&gt;</description></item><item><title>L2：线性基</title><link>https://jasper-aa64.github.io/zh/algo/l2-xor-basis/</link><pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/algo/l2-xor-basis/</guid><description>&lt;p&gt;给 $n$ 个数，从中任选一些异或起来，最大能得到多少？$n = 50$ 时子集有 $2^{50}$ 个，枚举不动。可是这些子集的异或值里有大量重复：$a$、$b$、$a \oplus b$ 三个数，任选子集能得到的值和只用 $a$、$b$ 时一模一样，第三个数是多余的。把多余的数一个个去掉，剩下的数最多 60 个（每个数 60 位），能异或出的值却一个不少。剩下的这组数就是&lt;strong&gt;线性基&lt;/strong&gt;。要说清楚“多余”是什么意思、为什么最多剩 60 个，需要把异或放进线性代数里看。&lt;/p&gt;</description></item><item><title>perf</title><link/><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid/><description>&lt;p&gt;速查列表里的一条入口，不是单独的页面：卡片链到 &lt;code&gt;target&lt;/code&gt; 指的那篇文章，标题、简介和日期都从那篇读，文章本身是唯一的一份。&lt;/p&gt;</description></item><item><title>性能瓶颈：信号、原因、修法</title><link>https://jasper-aa64.github.io/zh/ref/bottlenecks/</link><pubDate>Thu, 22 Oct 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/ref/bottlenecks/</guid><description>&lt;!-- 卡片的源文件在 content/bottlenecks/：中文 &lt;id&gt;.zh.md，英文 &lt;id&gt;.md，文件名就是 id，也是下面的锚点。第 1、2 节由短代码 bottleneck-lookup、bottleneck-cards 从这些文件自动汇总；文章末尾用短代码 bottleneck 按 id 嵌入同一份卡片。加一种瓶颈只需新增这两个文件。 --&gt;
&lt;p&gt;用 &lt;a href="https://jasper-aa64.github.io/zh/posts/low-latency-perf-profiling/"&gt;perf&lt;/a&gt; 找到热点以后，下一步是认出它是哪种瓶颈、照什么办法改。这一页把常见的瓶颈各写成一张卡片：信号是在 perf 里或源码里看到什么，原因是为什么慢，修法是怎么改，验证是怎么确认改对了，原理链到站内讲这个机制的文章。&lt;/p&gt;</description></item><item><title>左值、右值和移动语义：std::move、完美转发</title><link>https://jasper-aa64.github.io/zh/ref/move-semantics/</link><pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/ref/move-semantics/</guid><description>移动就是把源对象的指针拿过来、把源对象留成空的，只拷几个字节。std::move 只是一次类型转换，真正挑中移动构造的是重载决议；有名字的右值引用本身是左值，所以模板里要用 std::forward 把参数按原来的类别转出去。</description></item><item><title>构造、拷贝、析构和 RAII：对象的一生和异常安全</title><link>https://jasper-aa64.github.io/zh/ref/special-members-raii/</link><pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/ref/special-members-raii/</guid><description>类自己管资源，就要自己写析构、拷贝和移动；能交给 std::string、vector、unique_ptr 管的，一个都不用写。RAII 让资源跟着对象走：不管是正常 return 还是异常，离开作用域都会调析构函数，这是 C++ 里做清理的标准办法。</description></item><item><title>虚函数：vptr、vtable 和动态多态</title><link>https://jasper-aa64.github.io/zh/ref/virtual-functions/</link><pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/ref/virtual-functions/</guid><description>虚调用 = 读对象开头的 vptr → 读这个类 vtable 里那一格 → 按读到的地址跳。最贵的通常不是这一跳，而是编译器没法内联；类型在编译期就能定时，可以改用静态多态。要复用一个类的实现，放成成员，不要继承它。</description></item><item><title>对象布局：对齐、填充、vptr 和 vtable</title><link>https://jasper-aa64.github.io/zh/ref/object-layout/</link><pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/ref/object-layout/</guid><description>对象的大小 = 成员 + 为对齐补的填充，按从大到小排成员填充最少；空类做基类可以不占空间。有虚函数时对象只多一个 8 字节的 vptr，函数再多也只是表变长；vtable 每个类一张，放在重定位后变只读的 .data.rel.ro。多继承时第二个基类子对象有自己的 vptr，转换指针要加偏移，经它调用的虚函数要先过一个调整 this 的 thunk。</description></item><item><title>const、constexpr、consteval、constinit：哪个保证编译期算</title><link>https://jasper-aa64.github.io/zh/ref/const-constexpr/</link><pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/ref/const-constexpr/</guid><description>const 管的是能不能改，不管什么时候算；要编译期算出来，变量用 constexpr，函数用 consteval；静态变量怕初始化顺序问题，用 constinit。</description></item><item><title>lambda 和 std::function：闭包对象、捕获和类型擦除</title><link>https://jasper-aa64.github.io/zh/ref/lambda-function/</link><pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/ref/lambda-function/</guid><description>lambda 就是一个带 operator() 的类，按模板传进去和手写函数一样快；std::function 换来的是“任何可调用的东西都能放进同一个类型”，付出的是可能的堆分配和一次不能内联的间接调用。捕获引用时，被捕获的变量得比 lambda 活得久。</description></item><item><title>四种 cast：static_cast、dynamic_cast、const_cast、reinterpret_cast</title><link>https://jasper-aa64.github.io/zh/ref/cpp-casts/</link><pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/ref/cpp-casts/</guid><description>static_cast 是编译期的、会调整指针但不检查；dynamic_cast 是运行时的、会检查但要查表；const_cast 只动 const；reinterpret_cast 只换解释方式，拿来读写别的类型的对象多半是未定义行为，看字节用 std::bit_cast。C 风格转换会替你挑一种，挑的那种不一定是你以为的。</description></item><item><title>new 一次走到哪：operator new、malloc、brk/mmap 和缺页</title><link>https://jasper-aa64.github.io/zh/ref/new-malloc-page-fault/</link><pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/ref/new-malloc-page-fault/</guid><description>new 拿到的是地址，不是内存。malloc 大多数时候在自己手里的空闲块里就找到了，进内核的 brk、mmap 也只是登记一段地址；物理页要等第一次写才由缺页分配，每页一次，每次约 0.5–2 µs。所以第一遍写一块新内存比第二遍慢，delete 之后 RSS 不降也不等于泄漏。</description></item><item><title>函数调用栈帧：返回地址、寄存器传参和栈溢出</title><link>https://jasper-aa64.github.io/zh/ref/stack-frame/</link><pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/ref/stack-frame/</guid><description>栈帧就是一次调用在栈上占的那一块：call 把返回地址压进去，函数开头保存旧的 rbp、把 rsp 往下挪出局部变量的位置，ret 再把返回地址弹回 rip。参数前 6 个走寄存器，大对象由调用者在自己的帧里留好位置、把地址悄悄传进去，RVO 就是被调用者直接在那里构造。栈上的三种常见错误都能在这张图上找到位置：越过栈底的保护页、指向已经被下一帧覆盖的格子、写过头盖掉返回地址。</description></item><item><title>智能指针：unique_ptr、shared_ptr 和 weak_ptr</title><link>https://jasper-aa64.github.io/zh/ref/smart-pointers/</link><pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/ref/smart-pointers/</guid><description>默认用 unique_ptr：所有权只有一份，大小和裸指针一样。对象的寿命真要由几方共同决定时才用 shared_ptr，复制要付一次原子加，线程安全的只有计数；指回去的边、观察者、缓存用 weak_ptr，用之前 lock() 一下。</description></item><item><title>线程同步：互斥锁、条件变量、信号量、读写锁和死锁</title><link>https://jasper-aa64.github.io/zh/ref/thread-sync/</link><pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/ref/thread-sync/</guid><description>共享数据要么同一时刻只让一个线程碰（互斥锁），要么让等的线程睡到条件成立（条件变量、信号量）；条件检查和睡下必须在同一把锁里完成，否则唤醒会丢。死锁的根源是环形等待，固定顺序或一次拿齐就拆掉了环。</description></item><item><title>用户态和内核态：什么时候进内核，什么时候回来</title><link>https://jasper-aa64.github.io/zh/ref/user-kernel-mode/</link><pubDate>Wed, 04 Nov 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/ref/user-kernel-mode/</guid><description>进内核不是换线程，是同一个线程换到 Ring 0 跑内核代码。三种入口里，系统调用和异常由自己的代码引起，改代码能避开；中断要靠配置挪走。一次空的系统调用约 50–150 ns，拖成长尾的是在内核里睡下，或者回来前被调度走。</description></item><item><title>线程池和 future：std::thread、promise、packaged_task、async</title><link>https://jasper-aa64.github.io/zh/ref/thread-pool-future/</link><pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/ref/thread-pool-future/</guid><description>future 拿结果靠的是一块共享状态：promise 写、future 读，各一次；packaged_task 和 async 都是在它外面再包一层。线程池就是一个加锁的任务队列加几个一直在取任务的线程，难点在停止：标志要在锁里改，队列要先做完。</description></item><item><title>进程和线程：fork、写时复制、共享与独占、进程状态</title><link>https://jasper-aa64.github.io/zh/ref/process-thread-fork/</link><pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/ref/process-thread-fork/</guid><description>进程和线程在内核里是同一种东西，差别是共享多少：线程共用一份地址空间和一张 fd 表，只独占栈、寄存器和 TLS。fork 只复制页表，物理页等到有人写才复制，所以 fork 加 exec 很便宜；fork 之后两边的 fd 指着同一个打开的文件。进程退出后留下一条记录等父进程 wait，没人收就是僵尸；父进程先走，子进程交给 init 或最近的 subreaper 收。</description></item><item><title>进程间通信：管道、共享内存、信号和 socket</title><link>https://jasper-aa64.github.io/zh/ref/ipc/</link><pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/ref/ipc/</guid><description>进程间通信的方式看起来很多，按数据走不走内核只分两类。管道和 socket 让内核转交：写的一方拷进内核缓冲区，读的一方拷出来，内核顺便管了排队、满了阻塞、空了等待、对端关闭。共享内存让两个页表指向同一批物理页：建好以后读写就是普通访存，0 次拷贝、0 次系统调用，但没人告诉对方“数据到了”，也没人拦着两边同时写。信号和 eventfd 只传通知，不传数据。</description></item><item><title>文件 IO：fd、打开的文件和 inode</title><link>https://jasper-aa64.github.io/zh/ref/file-io/</link><pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/ref/file-io/</guid><description>fd → 打开的文件（偏移、O_APPEND、O_NONBLOCK）→ inode（大小、权限、链接数、数据块）。dup 和 fork 复制的是指向第二层的指针，所以共用偏移；重定向就是 open 加 dup2。数据到磁盘前要过两层缓冲：FILE* 的用户态缓冲靠 fflush 推进内核，页缓存靠 fsync 推上磁盘。</description></item><item><title>epoll：红黑树、就绪链表和回调</title><link>https://jasper-aa64.github.io/zh/ref/epoll-internals/</link><pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/ref/epoll-internals/</guid><description>epoll 在内核里有三样东西：红黑树记着关注哪些 fd，只给 epoll_ctl 用；就绪链表记着哪些 fd 现在有事，回调往里挂，epoll_wait 从里取；等待队列记着谁睡在 epoll_wait 里。事件来的时候不查树，靠的是登记时挂在 socket 上的那个指针。所以连接再多，每一圈事件循环的开销只看真正有事件的那几个；但每一次 epoll_wait、每一次 recv 仍然是一次系统调用。</description></item><item><title>TCP：三次握手、四次挥手和 TIME_WAIT</title><link>https://jasper-aa64.github.io/zh/ref/tcp-states/</link><pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/ref/tcp-states/</guid><description>握手是两个方向各对一次序列号，中间两步合成了 SYN+ACK；第三个报文让发起方能拒绝迟到的旧 SYN。accept 不参与握手，只从全连接队列里取走一条建好的连接。挥手时 ACK 由内核当场回，FIN 要等程序 close，所以通常是四个报文。CLOSE_WAIT 没有超时，成堆出现就是程序漏了 close；TIME_WAIT 是先关的一方替连接收尾，在 Linux 上固定 60 s，太多时靠复用连接解决。</description></item><item><title>TCP 怎么做到可靠：确认重传、流量控制、拥塞控制</title><link>https://jasper-aa64.github.io/zh/ref/tcp-reliability/</link><pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/ref/tcp-reliability/</guid><description>可靠靠编号加确认：没确认的就重传，连着 3 个重复确认不等超时就重传。能发多少看两个窗口：接收方的 rwnd 防止把对方的缓冲区塞爆，发送方自己的 cwnd 防止把网络塞爆，发送窗口取两者的较小值。TCP 是字节流，没有消息边界，消息要自己用长度前缀或分隔符切出来。</description></item><item><title>socket 编程：服务端和客户端各调哪几个函数</title><link>https://jasper-aa64.github.io/zh/ref/socket-programming/</link><pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/ref/socket-programming/</guid><description>服务端的 socket 先 bind 地址、再 listen 变成监听 socket，握手由内核完成，accept 只是从队列里取走一条建好的连接，换来一个新 fd；客户端 connect 时内核替它挑本地端口。TCP 只保证字节按序到达，不保证一次 read 拿到一条完整消息，所以读写都要循环、消息边界要协议自己定。并发从每连接一个进程或线程，走到一个线程用 epoll 等很多个非阻塞 socket，回调里绝不能阻塞。</description></item><item><title>HTTP 和 HTTPS：一次请求从输入网址到拿到页面</title><link>https://jasper-aa64.github.io/zh/ref/http-basics/</link><pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/ref/http-basics/</guid><description>一个新的 HTTPS 请求，要等 DNS、TCP 握手、TLS 握手各花掉时间，才轮到真正的请求，复用连接就是为了省掉前面几段。HTTP 消息在 TCP 字节流上靠空行和 Content-Length（或分块）划出边界；HTTP/2 用流解决了应用层的排队，却躲不开 TCP 的按序交付，HTTP/3 才把流做进 QUIC。TLS 用非对称密码学商量出密钥、证明服务器身份，数据本身用对称加密，因为快几个数量级。</description></item><item><title>STL 容器的底层：vector、deque、list、map、unordered_map</title><link>https://jasper-aa64.github.io/zh/ref/stl-containers/</link><pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/ref/stl-containers/</guid><description>容器的复杂度和迭代器失效规则都不用背，从它在内存里的样子就能推出来：连续内存的 vector 搬家时一切都失效，节点式的 list、map 只有被删的那个失效，unordered_map rehash 时迭代器失效、引用不失效。</description></item><item><title>LRU 缓存：哈希表加双向链表</title><link>https://jasper-aa64.github.io/zh/ref/lru-cache/</link><pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/ref/lru-cache/</guid><description>LRU 要两样东西：按 key 一步找到，按使用先后排好队。哈希表给前一样，双向链表给后一样，哈希表的值存链表节点的位置把两者接上。命中时用 splice 把节点挪到头部，节点不动、位置不变，哈希表也不用改。</description></item><item><title>跳表、红黑树、B+ 树：三种有序结构各用在哪</title><link>https://jasper-aa64.github.io/zh/ref/skiplist-rbtree-bplustree/</link><pubDate>Mon, 02 Nov 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/ref/skiplist-rbtree-bplustree/</guid><description>三者都是 O(log n) 的有序结构，差别在每一步的代价落在哪：红黑树和跳表在内存里，一步是一次指针跳转，比的是实现难度和改动范围；B+ 树在磁盘上，一步是一次读页，所以要把树做得又宽又矮。</description></item><item><title>低时延交易 —— 上下文切换、页表与 TLB</title><link>https://jasper-aa64.github.io/zh/posts/low-latency-context-switch-page-table-tlb/</link><pubDate>Wed, 04 Nov 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/posts/low-latency-context-switch-page-table-tlb/</guid><description>线程比进程轻，轻在切换之后 TLB 和缓存还能接着用，不在切换本身。TLB 没有 MESI：同一个进程里任何一个线程改了页表，此刻还装着这份页表的核都要被打断一次，绑核和 isolcpus 挡不住，所以热路径进程跑起来以后地址空间只读不改。</description></item><item><title>交易系统笔记 #21：交易系统性能衡量</title><link>https://jasper-aa64.github.io/zh/posts/trading-system-latency-metrics/</link><pubDate>Tue, 03 Nov 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/posts/trading-system-latency-metrics/</guid><description>TTT 和 RTT 各量哪一段，为什么要报 p50、p99、p999 而不是平均值，在线程边界打点怎么看出时间花在处理还是排队，以及量级表怎么拿来做延迟预算。</description></item><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>交易系统笔记 #19：调度策略、优先级反转与内核调优</title><link>https://jasper-aa64.github.io/zh/posts/scheduling-priority-inversion-kernel-tuning/</link><pubDate>Thu, 29 Oct 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/posts/scheduling-priority-inversion-kernel-tuning/</guid><description>谁先跑、跑多久：调度类和时间片，实时优先级的三个坑，优先级反转的两种修法，PREEMPT_RT 为什么 HFT 常常不用，以及一台低延迟机器从 BIOS 到 sysctl 要动哪些开关。</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><item><title>交易系统笔记 #1：CPU 隔离与 NUMA</title><link>https://jasper-aa64.github.io/zh/posts/cpu-affinity-core-isolation-numa/</link><pubDate>Wed, 09 Sep 2026 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/posts/cpu-affinity-core-isolation-numa/</guid><description>低延迟系统怕的是抖动，不是平均值。目标是让热线程成为它那个物理核上唯一在跑的东西：绑核消掉迁移，隔离参数把调度、时钟中断、RCU 和设备中断赶到打杂核，关超线程、禁深睡、锁频率去掉核自身的波动，最后让热线程只碰本节点的内存。</description></item><item><title>知悉</title><link>https://jasper-aa64.github.io/zh/about/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://jasper-aa64.github.io/zh/about/</guid><description>&lt;h2 id="关于本站"&gt;关于本站&lt;/h2&gt;
&lt;p&gt;低延迟 C++｜算法｜AI Native&lt;/p&gt;
&lt;h3 id="算法博客"&gt;算法博客&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;比赛里反复出现的公式和结论&lt;/li&gt;
&lt;li&gt;值得记住的简短证明&lt;/li&gt;
&lt;li&gt;可以放心复用的模板&lt;/li&gt;
&lt;li&gt;赛时能省时间的问题转化&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>