<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Blog on Yu's Space</title><link>https://code-agree.github.io/blog/</link><description>Recent content in Blog on Yu's Space</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Sun, 01 Jan 2023 00:00:00 +0000</lastBuildDate><atom:link href="https://code-agree.github.io/blog/index.xml" rel="self" type="application/rss+xml"/><item><title>低延迟 Socket 优化:从成本模型到平台层</title><link>https://code-agree.github.io/blog/2026-08-27-low_latency_socket/</link><pubDate>Thu, 27 Aug 2026 00:10:00 +0800</pubDate><guid>https://code-agree.github.io/blog/2026-08-27-low_latency_socket/</guid><description>一份自顶向下的分析框架。目标不是罗列参数,而是建立&amp;quot;为什么&amp;quot;的因果链—— 每一项优化都应该能追溯到它消除了哪一类物理成本。
0. 方法论:为什么不能从参数列表开始 #&amp;ldquo;Socket 有哪些优化&amp;quot;是一个被问烂了的问题,而绝大多数答案是失败的,因为它们是平铺的清单: TCP_NODELAY、SO_REUSEPORT、零拷贝、DPDK……
清单式回答有三个致命缺陷:
无法判断适用性。同一个参数在吞吐场景和延迟场景下的取值是相反的,脱离目标谈优化没有意义。 无法排序。不知道哪一项值 10μs、哪一项值 100ns,就会把精力花在错误的地方。 无法发现清单外的问题。真实系统的瓶颈常常不在清单上——它在 BIOS 里、在 NUMA 拓扑里、在 cache 替换策略里。 本文采用的路径是:
解剖数据路径 → 建立成本模型 → 确立度量纪律 → 逐层消除成本 → 验证 (第 1 节) (第 2 节) (第 3 节) (第 4-7 节) (第 8 节) 核心论点:Socket 优化的本质是在从网线到应用逻辑的路径上,系统性地消除排队点、 切换点、拷贝点和失效点。所有具体参数都是这四个原理在不同层次上的投影。
1. 解剖:一个包到底经历了什么 #1.1 接收路径(RX) #① NIC 收包,DMA 写入 RX ring 预挂的 buffer ② NIC 触发 MSI-X 中断(可能被 interrupt coalescing 延迟) ③ 硬中断处理:屏蔽该队列中断,raise NET_RX softirq ④ softirq 上下文 napi_poll:取 descriptor,构造 skb,GRO 聚合 ⑤ netif_receive_skb → ip_rcv → tcp_v4_rcv 查 socket hash → 加 socket 锁 → 序号检查 → 入 receive_queue ⑥ sk_data_ready → wake_up → try_to_wake_up 目标线程若在别的核 → 发 IPI → 对端调度器抢占 ⑦ 用户态 epoll_wait 返回 → recv() → copy_to_user 第一个反直觉的结论:第 ⑤ 步——真正的 TCP 协议处理——在 fast path 上只有几百纳秒。 而整条路径的 wire-to-app 延迟通常是 5–15μs。</description></item><item><title>rdtsc 计时:为什么 TSC 恒速、为什么 rdtscp、为什么标定而非假设</title><link>https://code-agree.github.io/blog/2026-08-26-rdtsc/</link><pubDate>Wed, 26 Aug 2026 08:00:00 +0800</pubDate><guid>https://code-agree.github.io/blog/2026-08-26-rdtsc/</guid><description>HFT 系统里几乎所有 µs 级延迟数字都出自同一个源头:rdtscp 读数 × 一个启动时标定的换算系数。这套做法为什么合理?本文不罗列&amp;quot;最佳实践&amp;quot;,而是把三个设计决策(依赖 TSC 不变性、选 rdtscp 而非 rdtsc、实测标定而非假设频率)各自的备选方案摆出来逐一淘汰——每个选择都有它的道理,换一个前提,结论就会不同。
0. 问题定义:测 µs 级延迟,需要一个什么样的钟? #从需求出发,一个用于延迟测量的时钟必须同时满足四条:
读取开销远小于被测量程——测 µs 级区间,读钟本身必须是 ns 级; 恒速——两次读数之差必须正比于真实流逝时间,比例系数恒定; 单调——不回退; 跨核可比——打点发生在不同线程/进程/核上,读数必须在同一把尺上。 候选其实有两个。clock_gettime(CLOCK_MONOTONIC) 走 vDSO,不进内核,~20ns,四条都满足;TSC(Time Stamp Counter)一条指令直读,~10ns。但这不是二选一——vDSO 的 clock_gettime 底层就是&amp;quot;读 TSC + 用内核维护的系数换算&amp;quot;,它是 TSC 的包装,不是替代品。选择裸读 rdtscp,买到的是三样东西:开销再减一半(打点在最热的路径上,10ns 也值得省);可以只存原始 tick、离线再换算——热路径连乘除都免了;换算系数自己掌控(§3 会讲为什么这很重要)。代价是换算的正确性要自己负责——这正是本文其余部分的主题。而这一切成立的前提是 TSC 满足第 2、4 条,这并非天经地义——第一节先把它论证清楚。
1. 为什么 TSC 恒速(invariant)——一个计数器不能同时忠于&amp;quot;周期&amp;quot;和&amp;quot;时间&amp;quot; #TSC 诞生时(Pentium 时代)真的是周期计数器:每个核心时钟周期 +1。那时 CPU 频率固定,周期数 × 周期长度 = 时间,二者等价,没有矛盾。
矛盾由两个硬件演化引入:
变频(SpeedStep/Turbo):核心频率随负载在 1.2GHz~4GHz 之间滑动 → 同样的周期数对应不同的时间; 休眠(C-states):核心时钟直接停掉 → 周期计数停止,时间却在流逝。 于是出现一个不可回避的分岔:当频率可变时,&amp;ldquo;数周期&amp;quot;和&amp;quot;数时间&amp;quot;成为两个互斥的语义,一个计数器只能忠于其中一个。 Intel 的选择是时间:</description></item><item><title>虚拟内存与 Page Table:从一次访存看懂 MMU、TLB、Page Fault 的完整机制</title><link>https://code-agree.github.io/blog/2026-08-25-page_table/</link><pubDate>Tue, 25 Aug 2026 09:00:00 +0800</pubDate><guid>https://code-agree.github.io/blog/2026-08-25-page_table/</guid><description>进程隔离、共享内存、huge page、lazy allocation、段错误——这些看似独立的现象,底层是同一个机制的不同侧面:page table(页表)。本文从零把它讲透,并在最后把上述现象逐一&amp;quot;接回&amp;quot;这张表。
0. 起点:程序里的地址全是虚拟的 #你代码里的每个指针、每个 &amp;amp;变量,都是 virtual address(虚拟地址)——不是内存条上的真实位置。CPU 拿到它不能直接访问 RAM,必须先翻译成 physical address(物理地址)。
为什么要虚拟化?直接用物理地址的世界没法过:所有程序挤在同一片真实内存里互相可踩(无隔离)、程序必须预知自己被装载到哪(无法重定位)。虚拟化后,每个进程都以为自己独占一条平坦的地址空间,真实内存的分配、位置、给不给,由 OS 在翻译层暗中操作。
1. Page 与 page table:按页翻译的字典 #翻译不能按字节做(字典会比内存还大),所以按 page(页) 做:
虚拟地址空间切成 4KB 一页 → page 物理内存切成 4KB 一块 → page frame(页帧) page table = &amp;ldquo;virtual page number → physical frame number&amp;rdquo; 的对照字典,每个进程一本 字典的每个词条 = PTE(page table entry) virtual address (64bit) = [ virtual page number ][ offset(12bit) ] │查 page table │原样保留 ▼ ▼ physical address = [ physical frame number ][ offset ] 每一次访存(每条 load/store)都要经过这次翻译,执行者是 CPU 里的硬件单元 MMU(Memory Management Unit)——&amp;ldquo;隔离由硬件强制&amp;quot;就是这个字面意思。</description></item><item><title>SIMD Parser 原理精读:把逐字节状态机编译成位运算数据流</title><link>https://code-agree.github.io/blog/2026-08-24-simd_parser/</link><pubDate>Mon, 24 Aug 2026 19:30:00 +0800</pubDate><guid>https://code-agree.github.io/blog/2026-08-24-simd_parser/</guid><description>上一篇《SIMD 深入解析:从硬件原理到加密货币 HFT 中的应用》(下称&amp;quot;硬件篇&amp;quot;)讲了 SIMD 的硬件机理与加密 HFT 链路全景,其中 §10.3 说&amp;quot;simdjson 比逐字符解析快约 10 倍&amp;quot;,但没有回答为什么能快——毕竟解析看起来是最&amp;quot;串行&amp;quot;的活:每个字节的含义取决于它前面的所有字节。这一篇专门拆这个问题:SIMD parser 如何把一个逐字节状态机改写成位运算数据流。其中最漂亮的一击,是用一条乘法指令跑完 64 步状态转移。
1. 先理解敌人:parser 是 CPU 最不擅长的负载 #一个标量 JSON parser 的本质是逐字节状态机:
for (each byte c) { switch (state) { case IN_STRING: if (c == &amp;#39;&amp;#34;&amp;#39;) state = OUT; else if (c == &amp;#39;\\&amp;#39;) state = ESC; break; case OUT: if (c == &amp;#39;{&amp;#39;) push(OBJ); else if (c == &amp;#39;&amp;#34;&amp;#39;) state = IN_STRING; break; ... } } 这段代码同时踩中现代 CPU 的两个死穴:</description></item><item><title>SIMD 深入解析:从硬件原理到加密货币 HFT 中的应用</title><link>https://code-agree.github.io/blog/2026-08-24-simd/</link><pubDate>Mon, 24 Aug 2026 17:10:28 +0800</pubDate><guid>https://code-agree.github.io/blog/2026-08-24-simd/</guid><description>前半部分把 SIMD 的硬件机理从零讲透:寄存器、执行单元、数据布局、内存边界、自动向量化、频率代价;后半部分逐段拆解加密货币 HFT 的 tick-to-trade 链路,回答&amp;quot;SIMD 到底在哪里赚钱&amp;quot;。
0. 你天天都在用 SIMD #先建立一个认知:哪怕你从没写过一行向量代码,你的程序也早就在享受 SIMD 了——memcpy/memcmp/strlen 的 glibc 实现内部全是向量指令;absl::flat_hash_map 查找快的秘密是一条 SIMD 指令同时比对 16 个候选槽位;simdjson 解析 JSON 快十倍,名字就写在脸上;你写个普通的数组循环开 -O3,编译器会自动把它改写成 SIMD 版本。
但&amp;quot;被动享受&amp;quot;和&amp;quot;主动驾驭&amp;quot;之间差一层原理。这篇文章先把原理讲清,再回到加密货币 HFT 的具体链路上看它值多少钱。
1. 本质:一条指令,多份数据 #假设要把两个数组对应位置相加:
float a[8] = {1, 2, 3, 4, 5, 6, 7, 8}; float b[8] = {10, 20, 30, 40, 50, 60, 70, 80}; float c[8]; for (int i = 0; i &amp;lt; 8; i++) c[i] = a[i] + b[i]; 普通写法下 CPU 做 8 轮,每轮加 1 个数。SIMD(Single Instruction, Multiple Data)的做法:把 a 的 8 个数一口气装进一个&amp;quot;加宽版寄存器&amp;quot;,b 的 8 个数装进另一个,用一条加法指令让 8 对数同时相加:</description></item><item><title>MengRao/SPSC_Queue 源码精读：把跨核延迟做到 50ns 的四种 SPSC 队列设计</title><link>https://code-agree.github.io/blog/2026-08-07-spsc_queue_mengrao/</link><pubDate>Fri, 07 Aug 2026 09:00:00 +0800</pubDate><guid>https://code-agree.github.io/blog/2026-08-07-spsc_queue_mengrao/</guid><description>MengRao/SPSC_Queue 是国内低延迟圈知名开发者饶萌（tcpshm、fmtlog 的作者）开源的单生产者单消费者无锁队列，README 宣称 10–200B 消息的跨核通信延迟在 50–100ns。整个仓库只有 4 个头文件、每个不到 150 行，却把 SPSC 队列设计空间里几乎所有关键取舍都覆盖了：定长 vs 变长消息、IPC crash-safe vs 极致延迟。本文逐个拆解这四个实现，重点回答一个问题：同样是无锁环形队列，凭什么它能比教科书写法快一倍？拆完源码后，文章会再退一步补齐全景：支撑这一切的寻址/缓存一致性/内存序三层地基，以及走出 SPSC 之后 MPSC/MPMC 的核心机制与选型。
一、仓库总览：一个 2×2 设计矩阵 #四个头文件不是四个孤立实现，而是两个维度的组合：
定长消息（模板参数 T） 变长消息（带 MsgHeader） 原子发布，crash-safe，可用于共享内存 IPC SPSCQueue.h SPSCVarQueue.h（用于作者的 tcpshm 框架） 极致延迟优化，仅限线程间 SPSCQueueOPT.h SPSCVarQueueOPT.h 两行的本质区别只有一条：消费者靠什么发现新消息。
基础版：消费者轮询生产者发布的共享索引 write_idx——发现消息要读 两条缓存行（索引一条、数据一条）； OPT 版：把&amp;quot;有没有消息&amp;quot;这个标志内嵌进数据所在的缓存行——发现消息只要读 一条缓存行。 跨核通信的延迟基本上就是缓存行在两个核之间传输的次数 × 单次传输耗时（几十 ns）。OPT 版把次数从 2 降到 1，延迟近乎减半——这是整个仓库最核心的亮点。代价是发布操作从&amp;quot;单条原子 store&amp;quot;变成多步写入，进程中途崩溃会把队列留在不一致状态，所以 OPT 版不能用于共享内存 IPC。
在深入源码之前，先交代两件事：这类队列在设计空间里的位置（它是&amp;quot;队列&amp;quot;，不是&amp;quot;广播&amp;quot;），以及四个实现共享的三条&amp;quot;设计基因&amp;quot;。
延伸阅读：并发原语的硬件成本（MESI、原子指令、六种内存序）可以先看这篇打底：并发原语深度剖析：从 Mutex 到 Atomic 到 Lock-Free 数据结构
二、队列还是广播：一个先于所有代码的设计决策 #深入源码前值得先停一步。SPSC_Queue 的一切设计都建立在一个前提上：读者的消费进度（read_idx）放在共享结构里，写者看得见。这个决策先于所有实现细节，直接划定了这类队列的能力边界——它的对立面是把读进度私有化的&amp;quot;广播&amp;quot;模型：</description></item><item><title>Optiver 的 Agentic SDLC：围绕 AI Agent 重新设计开发流程</title><link>https://code-agree.github.io/blog/2026-07-27-optiver-agentic-sdlc/</link><pubDate>Mon, 27 Jul 2026 11:56:41 +0800</pubDate><guid>https://code-agree.github.io/blog/2026-07-27-optiver-agentic-sdlc/</guid><description>本文是对 Optiver 技术博客 Engineering the Agentic SDLC（2026-06-23）的阅读整理与延伸思考。Optiver 是全球顶级做市商，这篇文章记录了他们把 AI Agent 纳入交易系统开发流程的实验，压力测试场景是 HFT 工程里最&amp;quot;脏&amp;quot;的活之一：接入新交易所。
核心命题 #不要把 Agent 塞进为人类设计的开发流程（SDLC）里去加速旧流程，而是从零开始围绕 Agent 重新设计开发生命周期。新前提是：Agent 承担大部分工作，人类负责引导和把关。由此几乎一切都要变——规格说明、工具链、代码评审、可观测性、甚至代码组织方式。
Optiver 的判断很清醒：具体工具未必长久，但底层约束比工具本身更耐久。
他们把这套体系拆成三层：
SDLC 工作流：开发、评审、测试、发布、运维。 Agent 原语：上下文（context）、执行环境（harness）。 共享底座：度量、执行、编排、治理。 压力测试：接入新交易所 #测试场景选得很有代表性——交易所连接。每个交易所都需要一个会话管理组件（登录、心跳、与内部系统通信），工作重复但细节因场所而异，小的协议差异出错代价很高，而且每接一个新交易所都要重来一遍。
结果：上线时间缩短 75%，工程人力投入减少 85%，约 90% 的产出达到&amp;quot;可直接送审&amp;quot;质量。
早期尝试很粗糙：通用编码 Agent 只能做到一半——不理解组件、无法可靠驱动测试工具、看不到系统内部关键部分、无法判断自己的代码错了，于是用各种&amp;quot;变通方案&amp;quot;填坑，这些代码根本过不了评审。他们的关键发现是：大多数失败源于外围工作流的缺口，而不是模型本身。
四条核心经验 #1. 黄金路径是工程出来的 #想要端到端可用的 Agent 流水线，必须通过工程手段压低方差，不能靠运气。每一步要么是显而易见的下一步，要么被明确文档化为下一步——不能指望 Agent 自己悟出来。他们为此做了三件事：重写组件、去掉会变成运行时错误的静默默认值；收紧规格、让操作顺序无歧义；构建编排框架，让 Agent 分阶段推进、阶段之间设验证门、每阶段只注入当前相关的上下文。
2. 上下文决定结果 #AGENTS.md 不是上下文的全部。Agent 需要三类上下文，缺任何一类都会产生不同的失败模式：
系统上下文：组件做什么、约束是什么。 领域上下文：外部环境实际如何表现——从真实流量中捕获。 方法上下文：这类问题怎么解、怎么测、什么算好。 领域上下文最有意思：交易所文档只告诉你有哪些报文，不告诉你发出去之后会发生什么。让 Agent 能探测线上系统、捕获真实流量的工具，把 Agent 锚定在现实上，而不是锚定在推断上。做过交易所对接的人对这句话应该都有生理反应——文档和真实行为之间的 gap 才是工作量的大头。
3. 反压（Backpressure）降低方差 #Agent 会在没成功的时候告诉你它成功了。解法是把&amp;quot;是否成功&amp;quot;的判定权从它手里拿走：用真实捕获流量构建应用测试；支持在非生产环境跑端到端场景、拿到干净的通过/失败信号；编排框架用代码验证每阶段交付物。
最终形态：Agent 在创造性部分（协议理解、实现、边界情况）自由发挥，在确定性部分（编译过没有、测试过没有、场景跑通没有）被严格约束。缺少反压，错误、bug 和坏设计会随着 Agent 独立工作不断复利放大。</description></item><item><title>从 send() 到网线：Linux 网络发包全路径与缓冲区概念辨析</title><link>https://code-agree.github.io/blog/2026-06-26-tcp_send_path/</link><pubDate>Fri, 26 Jun 2026 00:00:00 +0800</pubDate><guid>https://code-agree.github.io/blog/2026-06-26-tcp_send_path/</guid><description>本文是《从网线到策略引擎：Linux 网络收包全路径深度解析》的镜像篇。收包讲的是「数据如何从网卡到达用户态」，本文反过来，讲一个本地用户态的数据如何通过 TCP 发送到对端，并顺带把几个最容易混淆的概念——网卡、socket、socket 缓冲区、网卡缓冲区、内核缓冲区——一次性辨析清楚。
〇、先把概念辨析清楚（这是理解全流程的地基） #很多人讲发包讲不清，根子在于把不属于同一层的东西当成一回事。这几个词其实分属三个层次。
1. socket —— 是「对象」，不是「缓冲区」 #socket 是内核里的一个数据结构，代表「一条通信端点」。你 socket() 拿到的 fd，背后就是它。它记着源/目的 IP、端口、协议、连接状态、收发队列指针、TCP 状态机变量等。
socket 是「这条连接的档案 + 控制中心」，本身不存数据，但挂着两个缓冲区。
2. socket 缓冲区 —— per-socket 的软件收发队列 #每个 socket 自带两个队列：
发送缓冲区（send buffer，SO_SNDBUF）：send() 写进来的数据先攒在这。对 TCP，它还兼任重传底本——没收到 ACK 的数据必须留在这里。 接收缓冲区（recv buffer，SO_RCVBUF）：收到的数据攒在这等 recv()。 它在内核内存里，由协议栈管理。
3. 内核缓冲区 —— 一个泛称 #这个词最容易含糊，它不是某个具体东西：
狭义场景下，「内核缓冲区」常就指上面的 socket 缓冲区——相对「用户缓冲区」而言，数据从用户态拷进内核态的那块。 广义上，还包括数据在协议栈里流转时的载体——sk_buff（skb）：内核描述一个网络包的核心结构，封装 TCP/IP/以太网头就是往它上面填。 记法：用户缓冲区 ↔ 内核缓冲区，是「用户态 vs 内核态」这条线；socket 缓冲区是内核缓冲区里最靠近应用的那一段。
4. 网卡（NIC）—— 硬件 #真正把数据变成电信号/光信号送上线的 PCIe 硬件设备。CPU 通过「描述符 + 寄存器（门铃）+ DMA + 中断」这套接口跟它打交道。</description></item><item><title>HFT 系统中的延迟测量与绝对时间戳：一份工程实践指南</title><link>https://code-agree.github.io/blog/2026-04-27-timestamp/</link><pubDate>Mon, 27 Apr 2026 00:42:44 +0800</pubDate><guid>https://code-agree.github.io/blog/2026-04-27-timestamp/</guid><description>面向 Linux/x86_64 平台,覆盖进程内耗时测量、端到端链路延迟、日志时间戳以及监管合规场景。
0. 为什么 HFT 对&amp;quot;时间&amp;quot;如此苛刻 #在高频交易系统里,&amp;ldquo;时间&amp;quot;其实包含三个不同的工程问题,它们的解法几乎没有交集,初学者最容易混为一谈:
相对延迟(Relative Latency):我执行一段代码/一次订单处理花了多少纳秒?内部哪段代码是瓶颈?这关心单机内的时间差,要求极低的测量开销和极高的精度。 本机间隔与超时(Local Intervals):每 5 分钟跑一次校对、心跳 30 秒一次、订单 100 ms 内未回报视为超时——这关心单机内的时间流逝,要求时钟单调、不被 NTP 跳变干扰。 绝对时间戳与跨机器比较(Absolute Timestamp):某事件发生在 UTC 的哪一刻?接收时刻和交易所发出时刻差多少?这关心与外界对齐的墙上时间,要求与交易所、监管的时间基准一致,MiFID II RTS 25 规定业务时钟偏离 UTC 不得超过 100 μs。 第一类追求极致的低开销(单次测量 &amp;lt;10 ns),第二类追求逻辑正确性(不能被时钟跳变坑),第三类追求极致的对齐精度(与 UTC 偏差 &amp;lt;1 μs)。本文按这三条主线展开。
1. Linux 上的时钟源总览 #把所有候选工具按&amp;quot;离硬件远近&amp;quot;排一次,对应它们的精度、开销和 HFT 可用性:
工具 精度 单次开销 HFT 适用 说明 rdtsc / rdtscp CPU 周期(~0.3 ns) 15~40 cycles(~5~15 ns) ✅ 首选 进程内热路径唯一能做到个位数纳秒开销的手段 clock_gettime(CLOCK_MONOTONIC) (vDSO) 纳秒 ~20~30 ns ✅ 常用 底层也是 TSC + 换算,省去自己标定 clock_gettime(CLOCK_MONOTONIC_RAW) 纳秒 ~20~30 ns ⚠️ 特殊 不受 NTP 频率调整,更&amp;quot;原生&amp;rdquo; clock_gettime(CLOCK_REALTIME) 纳秒 ~20~30 ns ❌ 测延迟 / ✅ 打墙钟 会被 NTP 跳变,不能用于计算耗时 clock_gettime(CLOCK_TAI) 纳秒 ~20~30 ns ✅ 日志 不含闰秒,但仍会跟随系统墙钟 step gettimeofday() 微秒 ~20 ns ❌ 精度不够,POSIX 已不推荐 HPET 10~100 ns 几百 ns ~ 1 μs ❌ MMIO 访问,比 TSC 慢数十倍 NIC 硬件时间戳 数十 ns(含 PHY) 内核旁路返回 ✅ 必备 唯一能测 wire-to-wire 的手段 time() / clock() 秒 / 进程 CPU 时间 — ❌ time() 太粗,clock() 不是墙钟 表中的 clock_gettime 开销默认假设 x86_64 新内核走 vDSO。较老内核上 CLOCK_MONOTONIC_RAW/CLOCK_TAI 可能退化为 syscall,开销会高出数倍。</description></item><item><title>kernel socket vs DPDK：一条 WebSocket 帧的两段旅程与纳秒级实测</title><link>https://code-agree.github.io/blog/2026-04-25-kernel_socket_vs_dpdk/</link><pubDate>Sat, 25 Apr 2026 00:00:00 +0800</pubDate><guid>https://code-agree.github.io/blog/2026-04-25-kernel_socket_vs_dpdk/</guid><description>— 一条 180 字节的 TLS 帧，从网卡到用户回调，在两条路径上分别要走多久？走哪些步骤？为什么差 3-10 倍？
0. TL;DR #本文做三件事：
走一遍 kernel socket 收包路径——9 步，每步做什么，花多少纳秒 走一遍 DPDK + F-Stack + BIO_s_mem 路径——5 步，每步的用户态替代品 **用真实项目数据（FlashShark-ws，AWS EC2 VM，60 s，n=26 835）**对比两条路径的差距，包括一组真 A/B 对照（M0 kernel 9.6 µs vs M7 DPDK 2.8 µs 的 TLS 解密段） 最硬的两个数：
一条 180 B 的 Binance bookTicker 帧在 DPDK 路径上 e2e p50 = 5.3 µs、min = 1.1 µs 同样的 TLS 解密工作，kernel socket + OpenSSL BIO_s_socket 路径 p50 ≈ 9.</description></item><item><title>共享内存 IPC 深度实测：从 iceoryx 到自研 SPSC，HFT 场景下的终极选型</title><link>https://code-agree.github.io/blog/2026-04-17-iceoryx_ipc_benchmark/</link><pubDate>Fri, 17 Apr 2026 12:00:00 +0800</pubDate><guid>https://code-agree.github.io/blog/2026-04-17-iceoryx_ipc_benchmark/</guid><description>本文从实测出发，完整覆盖三个层次的共享内存 IPC 方案：iceoryx（工业级零拷贝框架）、Aeron（全栈消息系统）、自研 SPSC Ring Buffer（极致低延迟）。通过同平台 benchmark 对比、源码级热路径分析、跨进程 atomic 原理拆解，回答一个核心问题：HFT 的进程间通信到底该怎么选？
一、测试环境 # 项目 详情 机器 MacBook Pro (Mac16,8) CPU Apple M4 Pro, 14 核 (10 Performance @ 4.51GHz + 4 Efficiency @ 2.74GHz) 内存 24 GB 统一内存 OS macOS 26.3.1 (Darwin 25.3.0, arm64) 内核 xnu-12377.91.3 RELEASE_ARM64_T6041 编译器 Apple Clang 15.0.0 (clang-1500.3.9.4) C++ 标准 C++17 构建类型 Release (-O3 -DNDEBUG) Sanitizers 全部关闭 (ASAN/TSAN OFF) iceoryx 版本 v2.95.8 (commit 15dc8ed05) 二、iceoryx 性能实测 #2.</description></item><item><title>同一专线、同一秒、延迟不一样——从一次排查看网卡中断与核心隔离的本质</title><link>https://code-agree.github.io/blog/2026-04-09-buffer/</link><pubDate>Thu, 09 Apr 2026 00:03:41 +0800</pubDate><guid>https://code-agree.github.io/blog/2026-04-09-buffer/</guid><description>本文从一个真实的加密货币交易系统延迟异常出发，逐层拆解网卡中断机制、多队列架构、PPS 瓶颈、Buffer Bloat、网卡隔离与 CPU 核组规划，帮助读者建立从物理网卡到 CPU 处理的完整认知。
一、问题现象 #在一台 AWS c8g.metal-48xl（Graviton4，192 核，裸金属）上，同一条专线接入了多个交易所的行情。某天观察到以下现象：
时间 BN 延迟 GATE 延迟 间隔 17:33:36 248ms 20ms 同秒，相隔 7ms 17:35:46 73-82ms（连续 8 包） 20ms 同秒，相隔 170ms 17:41:17 86ms 20ms 同秒，相隔 142ms 关键矛盾：如果是专线本身的问题（光纤故障、中继设备拥塞），同一时刻所有流量都应该受影响。但 GATE 始终稳定在 20ms，只有 BN 在波动。
结论：问题出在 TY 端到 BN 的独有路径上。 但这次排查引出了一系列关于网卡架构和 CPU 资源分配的深层问题，值得系统梳理。
二、中断到底是什么：一次中断，两个角色 #很多人把&amp;quot;网卡中断&amp;quot;和&amp;quot;CPU 中断&amp;quot;当作两种不同的中断，但实际上它们描述的是同一次中断的发起方和执行方。
打个比方：有人按了你家门铃（网卡发中断），你听到铃声后起身去开门、收快递、拆包裹（CPU 处理中断）。门铃和你的行动不是两件独立的事，而是同一件事的两端。
网卡中断（发起方）：网卡通过 DMA 将数据写入 Ring Buffer 后，通过 PCIe 总线向 CPU 的中断控制器发送一个消息写入（MSI-X，Message Signaled Interrupt）——本质是一次 PCIe 内存写事务，而非传统 INTx 那样的专用电气信号线——含义是&amp;quot;Queue 5 有包到了，请处理&amp;quot;。这个动作本身几乎没有开销，网卡发完就结束了。</description></item><item><title>低延迟系统的 CPU 核心规划：从 isolcpus 到 Busy Poll 的内核级实战</title><link>https://code-agree.github.io/blog/2026-03-30-cpu_bindcore/</link><pubDate>Mon, 30 Mar 2026 00:00:00 +0000</pubDate><guid>https://code-agree.github.io/blog/2026-03-30-cpu_bindcore/</guid><description>上一篇文章《从网线到策略引擎：Linux 网络收包全路径深度解析》解决的是&amp;quot;数据包怎么走&amp;quot;的问题——从物理层到用户态的七个阶段。本文解决的是&amp;quot;CPU 核心怎么分&amp;quot;的问题——在一个多核系统上，哪些核跑内核家务活、哪些核处理网卡中断、哪些核跑行情线程，以及为什么这样分配。
一、问题的起点：能不能把所有核都 isolate 掉？ #在低延迟系统中，isolcpus 是最常见的核心隔离手段。它的作用是将指定的 CPU 从内核调度器的默认调度域中移除，使得普通进程不会被自动调度到这些核上，从而为延迟敏感的用户线程提供&amp;quot;干净&amp;quot;的执行环境。
一个自然的想法是：既然隔离能减少干扰，那把所有核都隔离了，再手动把用户线程绑上去，岂不是最干净？
这样做不合理，甚至可能导致系统无法正常运行。
1.1 内核的&amp;quot;家务活&amp;quot;必须有人干 #Linux 系统的正常运行依赖大量内核线程和基础设施：
ksoftirqd/N — 每个 CPU 上的软中断处理线程 kworker/N:M — 工作队列线程（延迟工作、异步 I/O 等） rcu_preempt — RCU 回调处理 migration/N — 进程迁移 watchdog/N — CPU 死锁检测 systemd (PID 1) — 用户空间初始化 sshd / journald — 基础系统服务 当你用 isolcpus=0-191 把所有 192 个核都隔离后，这些内核线程和系统服务没有任何核可以正常调度。较新的内核在启动早期会检查是否至少存在一个 housekeeping CPU，如果全部隔离，系统可能直接启动失败或行为异常。
1.2 CPU 0 的特殊地位 #即使不全部隔离，把 CPU 0 隔离也需要格外小心。在 Linux 内核中，CPU 0 承担着特殊职责：
Boot CPU：系统启动阶段的大量初始化逻辑绑定在 CPU 0 上执行，部分内核子系统硬编码依赖它。start_kernel() → rest_init() → kernel_init() 这条初始化链路始终在 CPU 0 上运行。</description></item><item><title>从网线到策略引擎：Linux 网络收包全路径深度解析</title><link>https://code-agree.github.io/blog/2026-03-17-net_proc/</link><pubDate>Tue, 17 Mar 2026 23:10:26 +0800</pubDate><guid>https://code-agree.github.io/blog/2026-03-17-net_proc/</guid><description>本文以一个真实的加密货币交易系统为背景，系统性讲解 Linux 下一个网络数据包从物理网卡到达用户态应用的完整路径，并结合 AWS Graviton (ARM64) + ENA 网卡 + 192 核环境的实际 IRQ 数据进行分析。
一、全景概览 #一个数据包从交易所服务器发出，到你的策略引擎处理完毕，经历了以下七个阶段：
交易所服务器 │ ▼ ┌─────────────────────────────────────────────────────────┐ │ 阶段 1：物理层 &amp;amp; NIC 硬件 │ │ 网线 → PHY → MAC → On-Chip SRAM暂存 → RSS分流 │ │ → DMA 写入主存 Ring Buffer │ ├─────────────────────────────────────────────────────────┤ │ 阶段 2：硬中断 (Hard IRQ) │ │ NIC 触发中断 → CPU 响应 → 最小化处理后调度 softirq │ ├─────────────────────────────────────────────────────────┤ │ 阶段 3：软中断 &amp;amp; NAPI 轮询 │ │ ksoftirqd / NET_RX_SOFTIRQ → NAPI poll 批量收包 │ ├─────────────────────────────────────────────────────────┤ │ 阶段 4：内核协议栈 │ │ IP 层 → TCP/UDP 层 → 查找 socket → 放入 socket buffer │ ├─────────────────────────────────────────────────────────┤ │ 阶段 5：Socket Buffer │ │ sk_receive_queue 排队等待用户态读取 │ ├─────────────────────────────────────────────────────────┤ │ 阶段 6：用户态唤醒 &amp;amp; 数据拷贝 │ │ epoll_wait 返回 → recv/read → 数据从内核拷贝到用户态 │ ├─────────────────────────────────────────────────────────┤ │ 阶段 7：应用层处理 │ │ JSON 解析 → 策略计算 → 下单 / 信号转发 │ └─────────────────────────────────────────────────────────┘ 下面逐层展开。</description></item><item><title>Claude Code 项目配置完整手册</title><link>https://code-agree.github.io/blog/2026-03-11-agent-setup/</link><pubDate>Wed, 11 Mar 2026 10:51:47 +0800</pubDate><guid>https://code-agree.github.io/blog/2026-03-11-agent-setup/</guid><description>Claude Code 项目配置完整手册 # 从零开始配置 Claude Code 项目：目录结构、CLAUDE.md、Skills、Memory、Subagents 一站式指南。
目录 # 项目初始化 Checklist 目录结构模板 CLAUDE.md 配置 Settings 配置 Skills 配置 Memory 持久记忆系统 Subagents 配置 MCP Servers 配置 Hooks 生命周期钩子 示例 Subagents 团队协作最佳实践 1. 项目初始化 Checklist #按照以下顺序完成项目配置，每一步完成后打勾：
第一阶段：基础配置 # 创建项目目录结构（参照 第 2 节） 编写根目录 CLAUDE.md（项目级指令，团队共享） 编写个人 ~/.claude/CLAUDE.md（用户级偏好，不入版本控制） 创建 .claude/settings.json（hooks、权限、环境变量） 第二阶段：Skills 与 Memory # 创建项目级 Skills 目录 .claude/skills/ 编写所需 Skill 的 SKILL.md（参照 第 5 节） 确认 Auto Memory 已启用（运行 /memory 查看） 如需要，创建 .</description></item><item><title>HFT 热路径错误处理：异常机制、optional 与最佳实践</title><link>https://code-agree.github.io/blog/2026-03-04-try_catch/</link><pubDate>Wed, 04 Mar 2026 10:24:45 +0800</pubDate><guid>https://code-agree.github.io/blog/2026-03-04-try_catch/</guid><description>本文从 C++ 异常机制与 std::optional&amp;lt;T&amp;gt; 的实现原理出发，说明二者在延迟与可预测性上的差异，并给出在高频交易（HFT）热路径上的错误处理最佳实践。
1. 为什么热路径要单独考虑错误处理 #在 HFT 系统中，报价、下单、风控等逻辑往往在热路径上执行：每笔行情或每次 tick 都会触发，执行频率高、对延迟和抖动极其敏感。错误处理方式会直接影响：
延迟：异常抛出时的栈展开、析构会带来不可预测的延迟尖刺； 可预测性：热路径上应尽量无分支或分支可预测，避免因“可能失败”的 API 引入额外分支或控制流； 局部失败隔离：单个标的或档位失败不应拖垮整次处理，但实现方式不能以牺牲延迟为代价。 因此，热路径上的错误处理需要显式设计，而不是依赖“通用”的异常或包装类型。
2. C++ 异常机制（try-catch）原理 #2.1 基本流程 #当程序执行到 throw expr 时：
构造异常对象：用 expr 构造一个异常对象（可能拷贝或移动）； 栈展开（stack unwinding）：从当前 throw 点开始，沿调用栈向上逐层退出，每退出一层就析构该层中的局部对象（按构造的逆序）； 查找 catch：在调用栈上寻找与异常类型匹配的 catch 子句； 匹配成功：进入 catch 块执行，然后从 catch 之后继续执行（或返回到调用方）； 匹配失败：若一直到 main 仍未匹配，则调用 std::terminate()，进程终止（可能产生 coredump）。 因此：
有 catch：异常被接住 → 栈展开 + 执行 catch → 程序继续运行，但本帧已付出栈展开和析构的代价； 无 catch：未捕获异常 → std::terminate() → 进程退出，不再存在“本帧延迟”的问题。 热路径上若使用 try-catch，我们讨论的是“有 catch、程序继续”的情形：不抛时几乎不付出额外成本，一旦抛就会在本帧产生一次高延迟。
2.2 Zero-cost 异常模型：成本被挪去了哪里 #主流编译器（GCC、Clang、64 位 MSVC）采用表驱动的零成本异常：正常路径不插入任何登记指令（老的 SJLJ 模型每进一次 try 都要 setjmp 登记，人人路路付费）；异常处理所需的信息被编译成静态表（.</description></item><item><title>并发原语与内存序深度剖析：从 Mutex 到 Atomic 到 Lock-Free 数据结构</title><link>https://code-agree.github.io/blog/2026-03-03-mutext/</link><pubDate>Tue, 03 Mar 2026 23:39:27 +0800</pubDate><guid>https://code-agree.github.io/blog/2026-03-03-mutext/</guid><description>本文系统梳理 Linux/x86_64 环境下各种并发同步机制的实现原理、硬件行为和性能开销，并完整讲解 C++ 内存模型与六种内存序的语义、典型误用与调试方法，为 HFT 场景下的并发设计提供决策依据。本文是本站内存序主题的权威出处，其他文章涉及内存序时均链接至此。
一、硬件基础：理解开销的根源 #在讨论任何同步原语之前，必须先理解 CPU 缓存一致性协议，因为所有同步开销的本质都是 cache line 在核间的传输。
1.1 MESI 协议 #现代多核 CPU 通过 MESI 协议（Modified, Exclusive, Shared, Invalid）维护缓存一致性：
Core 0 Core 1 ┌──────────┐ ┌──────────┐ │ L1 Cache │ │ L1 Cache │ │ Line X: │ │ Line X: │ │ Modified │ │ Invalid │ └────┬─────┘ └────┬─────┘ │ │ └──────┬──────────────┘ │ ┌──────┴──────┐ │ L3 Cache │ (或 Directory) │ / Ring Bus│ └─────────────┘ 四种状态：</description></item><item><title>高频交易中的 WebSocket 架构设计：从阻塞 IO 到事件驱动</title><link>https://code-agree.github.io/blog/2026-03-03-websocket/</link><pubDate>Tue, 03 Mar 2026 23:38:37 +0800</pubDate><guid>https://code-agree.github.io/blog/2026-03-03-websocket/</guid><description>本文以一个真实的 OKX Fill Receiver 实现为切入点，深入探讨 HFT 场景下 WebSocket 客户端的架构设计，涵盖 Socket 编程基础、epoll 事件驱动模型、Connection/Socket/Thread 的关系，以及面向超低延迟的工程优化。
一、Socket 编程基础：从 fd 到 Connection #1.1 文件描述符 (File Descriptor) 是一切的起点 #在 Unix/Linux 中，一切皆文件。网络连接也不例外——一个 TCP 连接在内核中对应一个 struct socket，在用户态通过一个整数 文件描述符 (fd) 来引用。
一个 TCP 连接的建立过程：
客户端: 服务端: fd = socket(AF_INET, SOCK_STREAM, 0) listen_fd = socket(...) connect(fd, server_addr, ...) bind(listen_fd, addr, ...) │ listen(listen_fd, backlog) │── SYN ──────────────────► │ │◄─────────────── SYN+ACK ── │ │── ACK ──────────────────► conn_fd = accept(listen_fd, .</description></item><item><title>Intel Xeon 6982P 服务器硬件深度分析与 NUMA 调优指南</title><link>https://code-agree.github.io/blog/2026-03-01-numa/</link><pubDate>Sun, 01 Mar 2026 12:49:15 +0800</pubDate><guid>https://code-agree.github.io/blog/2026-03-01-numa/</guid><description>基于阿里云 ECS 实例实测数据，涵盖 CPU 拓扑、缓存层级、NUMA 架构原理及 HFT 场景下的性能调优策略。
一、硬件概览 #本文分析的目标机器为一台阿里云 ECS 实例，搭载 Intel Xeon 6982P-C 处理器（Granite Rapids 架构），以下所有数据均来自该机器的实际输出。
指标 值 架构 x86_64 处理器 Intel Xeon 6982P-C Socket 数 1 物理核心数 96 逻辑 CPU 数 192（超线程 ×2） 基础频率 / 最大睿频 3600 MHz / 3900 MHz NUMA 节点数 3 总内存 ~377 GB DDR5 二、CPU 基础指标详解 #2.1 架构与运行模式 #Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian Architecture: x86_64 — 64 位 x86 指令集架构，Intel 和 AMD 通用。 CPU op-mode(s): 32-bit, 64-bit — 处理器同时支持运行 32 位和 64 位程序。 Byte Order: Little Endian — 小端字节序，x86 平台的固定特征，低位字节存储在低地址。 2.</description></item><item><title>adapter-poller 线程段错误问题定位与分析报告</title><link>https://code-agree.github.io/blog/2026-01-23-corddump/</link><pubDate>Fri, 23 Jan 2026 17:56:12 +0800</pubDate><guid>https://code-agree.github.io/blog/2026-01-23-corddump/</guid><description>adapter-poller 线程段错误问题定位与分析报告 #执行摘要 #问题：adapter-poller 线程在 panda-strategy 进程中多次发生段错误（SIGSEGV）。
崩溃位置：std::_Hashtable&amp;lt;...pandas::Adapter::ContractSpec...&amp;gt;::find (hashtable.h:1665)
根本原因：访问 pandas::Adapter::ContractSpec 哈希表时，this 指针无效，对象可能已被销毁。
关键发现：
崩溃发生在 adapter-poller 线程处理订单更新时 调用链：Adapter.cpp:307 → Adapter::pollCommon → std::_Hashtable::find Binance TD 代码存在类型不匹配和空指针检查缺失问题 修复建议：
修复 Binance TD 代码中的类型不匹配（std::make_unique → std::make_shared） 添加空指针检查（5 处 m_order_ws-&amp;gt;send() 调用） 检查 Adapter 对象的生命周期管理 1. 问题现象 #1.1 系统日志显示的问题 #从系统日志 /var/log/messages 中观察到以下问题：
问题一：OOM (Out of Memory) #Jan 22 20:29:38 ... kernel: Out of memory: Killed process 2150786 (node) total-vm:45193936kB, anon-rss:28584348kB, file-rss:0kB, shmem-rss:0kB 被杀死进程：node 进程（PID: 2150786） 内存占用：约 28GB (anon-rss: 28584348kB) 结论：这是独立问题，与 adapter-poller 段错误无关 问题二：段错误 (Segmentation Fault) #Jan 23 02:46:49 .</description></item><item><title>段错误调试分析：从系统日志到代码修复</title><link>https://code-agree.github.io/blog/2026-01-19-core_ana/</link><pubDate>Mon, 19 Jan 2026 22:53:59 +0800</pubDate><guid>https://code-agree.github.io/blog/2026-01-19-core_ana/</guid><description>摘要 #本文详细记录了一次段错误（SEGV）的完整调试过程。通过系统日志分析、地址解析、汇编代码分析和代码审查，成功定位并修复了 PerformanceMonitor 中的数组越界问题。本文展示了如何在没有完整 coredump 文件的情况下，仅凭系统日志和调试工具进行问题定位。
一、问题现象 #1.1 崩溃信息 #从 coredumpctl 获取的崩溃信息：
PID: 178581 (panda_strategy-) Signal: 11 (SEGV) Timestamp: Mon 2026-01-19 21:25:12 CST Executable: /home/jason/panda/panda-strategy/Strategy/out/build/wsl-profile/panda_strategy-1.0.0 Message: Coredump entry has no core attached 关键问题：coredump 文件未保存，无法使用 GDB 直接分析。
1.2 崩溃时间点分析 #从日志文件 main.log 中可以看到：
[21:25:12.816398][178634][info][strategy] Funding rates updated, total entries: 93, updated entries: 5 [21:25:12.816474][178634][info][strategy] Updated tradability parameters: 274 margin entries [21:25:12.816493][178634][info][strategy] Reference updated for 3 symbols [21:25:12.816502][178634][info][strategy] Fee rate table updated, total entries: 36, updated entries: 36 [21:25:12.</description></item><item><title>段错误(SEGV)故障定位排查文档</title><link>https://code-agree.github.io/blog/2026-01-15-debug_procedure2/</link><pubDate>Thu, 15 Jan 2026 10:18:16 +0800</pubDate><guid>https://code-agree.github.io/blog/2026-01-15-debug_procedure2/</guid><description>📋 问题概述 #故障现象：panda_strategy-1.0.0 程序在运行过程中发生段错误(Segmentation Fault)，进程崩溃。
崩溃时间：2026-01-15 01:04:11 CST
崩溃进程：PID 2312019
崩溃线程：adapter-poller[2312082]
信号类型：SIGSEGV (Signal 11)
🔍 排查步骤 #1. 确认崩溃信息 #1.1 使用 coredumpctl 获取崩溃基本信息 #coredumpctl info 2312019 输出结果：
PID: 2312019 (panda_strategy-) UID: 1000 (jason) GID: 1000 (jason) Signal: 11 (SEGV) Timestamp: Thu 2026-01-15 01:04:11 CST (7h ago) Command Line: ./panda_strategy-1.0.0 Executable: /home/jason/panda/panda-strategy/Strategy/out/build/wsl-profile/panda_strategy-1.0.0 Storage: none Message: Process 2312019 (panda_strategy-) of user 1000 dumped core. 关键信息：
✅ 确认崩溃时间：2026-01-15 01:04:11 CST ⚠️ Storage: none - 核心转储文件未保存，但可以尝试其他方法分析 1.</description></item><item><title>Linux Coredump 调试完整指南</title><link>https://code-agree.github.io/blog/2026-01-15-debug_procedure/</link><pubDate>Thu, 15 Jan 2026 10:05:34 +0800</pubDate><guid>https://code-agree.github.io/blog/2026-01-15-debug_procedure/</guid><description>目录 # 概述 Coredump 生成机制 环境检查与配置 Coredump 文件定位 使用 GDB 分析 Coredump 实战案例分析 最佳实践与故障排查 概述 #当 Linux 进程因段错误（SIGSEGV）、总线错误（SIGBUS）等信号异常终止时，系统可以生成 coredump 文件，记录进程崩溃时的完整内存状态。通过分析 coredump，我们可以准确定位崩溃原因，包括：
崩溃时的函数调用栈（Stack Trace） 局部变量和全局变量的值 内存布局和寄存器状态 崩溃发生的精确代码位置 本文档提供一套完整的 coredump 调试流程，从环境配置到深度分析，帮助开发者快速定位和解决程序崩溃问题。
Coredump 生成机制 #2.1 系统级配置：core_pattern #Linux 内核通过 /proc/sys/kernel/core_pattern 控制 coredump 的生成方式：
# 查看当前配置 cat /proc/sys/kernel/core_pattern 常见配置模式：
传统模式：直接生成 core 文件
core 在当前工作目录生成名为 core 的文件。
Systemd-coredump 模式（现代 Linux 发行版默认）：
|/usr/lib/systemd/systemd-coredump %P %u %g %s %t %c %h %e 由 systemd-coredump 服务统一管理，提供压缩、存储和查询功能。
自定义路径模式：
/var/crash/core.%e.%p.%t 生成到指定目录，文件名包含可执行文件名、PID 和时间戳。</description></item><item><title>HFT 热路径中的分支：从 CPU 流水线到尾延迟治理的完整工程指南</title><link>https://code-agree.github.io/blog/2025-12-26-if_pre/</link><pubDate>Fri, 26 Dec 2025 12:32:42 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-12-26-if_pre/</guid><description>高频交易系统追求微秒甚至纳秒级的稳定性。&amp;ldquo;热路径少写 if&amp;ldquo;是常见的优化建议，但这个规则需要精确理解：if 的性能代价不来自语法层面，而来自 CPU 微架构层面的流水线行为。本文从 CPU 执行机理出发，构建完整的工程决策框架。
1. 前置知识：CPU 流水线执行模型 #1.1 流水线的本质：并行与预测 #现代 x86 CPU 采用超标量乱序执行架构，将指令执行分解为多个阶段并行推进：
前端（Front-end） 后端（Back-end） ┌─────────────────┐ ┌──────────────────┐ │ Fetch (I-cache) │──→ │ Schedule/Dispatch│ │ Decode (µop) │──→ │ Execute (ALU/LSU)│ │ Branch Predict │──→ │ Memory Access │ │ Rename (RAT) │──→ │ Retire (ROB) │ └─────────────────┘ └──────────────────┘ 关键机制：
深度流水线（典型 14-20 级）需要提前知道下一步执行什么 乱序执行（OoO）可以在 Reorder Buffer (ROB) 内并行处理 ~224 条 µops（Skylake） 推测执行（Speculative Execution）在分支结果未知时继续执行 1.2 为什么需要分支预测 #控制流指令（if/switch/call）会改变下一条指令地址。如果等待条件计算完成，流水线将停顿。现代 CPU 使用：</description></item><item><title>C++ Map 容器性能差异的底层实现分析：std::map vs std::unordered_map vs absl::flat_hash_map vs absl::node_hash_map</title><link>https://code-agree.github.io/blog/2025-12-26-various_map/</link><pubDate>Fri, 26 Dec 2025 01:32:39 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-12-26-various_map/</guid><description>C++ Map 容器性能差异的底层实现分析：std::map vs std::unordered_map vs absl::flat_hash_map vs absl::node_hash_map #摘要 #本文基于实际的性能基准测试结果，从底层数据结构和内存布局的角度深入分析 std::map、std::unordered_map、absl::flat_hash_map 和 absl::node_hash_map 四种容器在不同操作场景下的性能差异。测试结果表明，在查找密集型场景中，absl::flat_hash_map 相比传统 std::map 有近 3 倍的性能提升。
一、测试结果概览 #基于 50 个元素、100 万次查找操作的基准测试结果：
容器类型 查找耗时 相对性能 插入耗时 混合操作耗时 std::map 44.878 ms 1.00x (基准) 2.323 ms 29.211 ms std::unordered_map 25.400 ms 1.77x 1.706 ms 18.323 ms absl::node_hash_map 16.179 ms 2.77x 1.816 ms 13.714 ms absl::flat_hash_map 15.329 ms 2.93x 1.867 ms 13.138 ms 二、数据结构底层实现分析 #2.1 std::map：红黑树实现 #2.1.1 内存布局 #std::map 基于**红黑树（Red-Black Tree）**实现，是一种自平衡二叉搜索树。每个节点包含：</description></item><item><title>std::map vs absl::flat_hash_map 性能比较分析</title><link>https://code-agree.github.io/blog/2025-12-24-flat_hash_map/</link><pubDate>Wed, 24 Dec 2025 17:13:38 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-12-24-flat_hash_map/</guid><description>一、底层实现原理 #1.1 std::map 实现原理 #std::map 是基于**红黑树（Red-Black Tree）**实现的有序关联容器。
红黑树特性 # 自平衡二叉搜索树：保证最坏情况下 O(log n) 的查找、插入、删除时间复杂度 有序性：元素按照键值自动排序（基于 operator&amp;lt; 或自定义比较器） 内存布局：节点式存储，每个节点包含： 键值对（key-value pair） 左子节点指针 右子节点指针 父节点指针 颜色标记（红/黑） 查找过程 #查找键 K： 1. 从根节点开始 2. 比较 K 与当前节点键值 3. 如果 K &amp;lt; 当前键，进入左子树 4. 如果 K &amp;gt; 当前键，进入右子树 5. 如果 K == 当前键，返回节点 6. 重复步骤 2-5，直到找到或到达叶子节点 时间复杂度：O(log n)，其中 n 为树中节点数
平均比较次数：log₂(n) 对于 20 个元素：log₂(20) ≈ 4.3 次比较 对于 100 个元素：log₂(100) ≈ 6.6 次比较 插入过程 #插入键值对 (K, V)： 1.</description></item><item><title>Linux系统负载定位与分析实战指南</title><link>https://code-agree.github.io/blog/2025-12-04-cpu_debug/</link><pubDate>Thu, 04 Dec 2025 23:27:01 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-12-04-cpu_debug/</guid><description>前言 #在高性能计算场景（如HFT交易系统、实时数据处理等）中，系统负载的精确定位至关重要。本文通过一个真实的系统负载分析案例，系统性地介绍如何使用Linux性能分析工具链，从表面现象深入到根本原因。
本文涉及的工具链 # htop/top: 实时系统资源概览 vmstat: 系统级性能统计 pidstat: 进程级性能分析 其他辅助工具: /proc文件系统、perf、strace等 第一阶段：初步观察 - htop #工具介绍 #htop是top的增强版本，提供彩色、交互式的系统监控界面。相比top，它更直观地展示：
每个CPU核心的使用率 进程/线程列表 内存和Swap使用情况 Load Average（负载平均值） 关键指标解读 #CPU使用率分布 #CPU 0-15: |||||||||||||||||| 100.0% 解读要点：
每核心独立显示：现代多核系统必须分核心观察
颜色含义（通常）：
绿色：用户态进程（user space） 红色：内核态（kernel/system） 蓝色：低优先级进程（nice） 黄色：IRQ（硬件中断） 品红：Soft IRQ（软中断） 灰色：IO Wait 青色：Steal（虚拟化环境） 异常模式识别：
全核心100%：CPU密集型负载，可能是正常业务或失控进程 某些核心100%，其他空闲：不均衡的线程分配或CPU亲和性设置 高IO Wait（灰色）：存储瓶颈 高Soft IRQ（品红）：网络包处理压力大 Load Average深度解析 #Load average: 13.08, 13.23, 12.62 常见误区：Load Average ≠ CPU使用率
Load Average的真实含义：
处于以下状态的进程/线程数量的时间加权平均值：
R状态（Running/Runnable）：正在运行或等待CPU D状态（Uninterruptible Sleep）：不可中断睡眠（通常等待I/O） 三个数字的含义：
第一个：1分钟平均 第二个：5分钟平均 第三个：15分钟平均 解读规则：</description></item><item><title>SPSC DTO 构造语义优化复盘</title><link>https://code-agree.github.io/blog/2025-11-08-default/</link><pubDate>Sat, 08 Nov 2025 00:47:42 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-11-08-default/</guid><description>背景 #最近一次 panda-datatype 提交中，LockFreeRingBuffer 的读写索引放宽为 memory_order_relaxed，同时将行情/订单相关 DTO（Depth5、OrderUpdate、TradeUpdate 等）的默认构造方式改为“成员内联默认值 + = default”。这看似语法微调，实则是为单生产者单消费者（SPSC）数据通道消除隐藏成本。
旧实现的隐患 #旧版在 .cpp 里手写构造函数逐字段清零，逻辑直观，却带来三个问题：
失去平凡属性：自定义构造/析构一旦出现，类型就不再是 trivially constructible/copyable，无法放心地按位拷贝。无锁结构如 LockFreeRingBuffer&amp;lt;std::array&amp;lt;T&amp;gt;&amp;gt; 一旦直接覆盖槽位，轻则报错、重则 UB。 破坏无锁假设：SPSC 设计假定“写入即内存搬运”。如果数组槽位里的 T 有非平凡构造，buffer[index] = item; 就不得不调用它，整条“无锁 + 放宽内存序”路径失效。 维护负担重：新增字段必须同步修改 .cpp 构造，否则默认值遗漏；默认值散落在实现文件，代码审查也不直观。 新方案：成员默认值 + 默认化构造 #提交中 OrderUpdate 的具体修改如下（TradeUpdate 类似）：
struct OrderUpdate { Exchange m_exchange = Exchange::UNKNOWN; uint32_t m_accountId = 0; std::array&amp;lt;char, 32&amp;gt; m_clientOrderId = {}; OrderStatus m_status = OrderStatus::UNKNOWN; int32_t m_errorStatusCode = 0; std::array&amp;lt;char, 64&amp;gt; m_errorReason = {}; OrderUpdate() = default; }; Depth5 也简化为：</description></item><item><title>高性能网络I/O优化原理与实战：从网卡队列到CPU绑核的系统优化</title><link>https://code-agree.github.io/blog/2025-08-18-network_queue/</link><pubDate>Mon, 18 Aug 2025 16:07:40 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-08-18-network_queue/</guid><description>高性能网络I/O优化原理与实战：从网卡队列到CPU绑核的系统优化 #前言 #在高频交易、实时通信等对延迟极度敏感的应用场景中，理解和优化网络I/O处理路径至关重要。本文深入探讨从硬件层面的网卡队列机制到操作系统层面的CPU调度优化，为开发者提供系统性的网络性能优化理论基础和实践方法。
1. 网络数据包处理的完整流程 #1.1 数据包从网卡到应用程序的路径 #理解网络I/O优化的前提是掌握数据包处理的完整流程：
1. 网卡接收数据包 ↓ 2. DMA传输到内存 ↓ 3. 硬件中断触发 (IRQ) ↓ 4. 内核网络栈处理 ↓ 5. 数据放入Socket缓冲区 ↓ 6. 应用程序通过系统调用读取数据 在这个流程中，每一步都涉及CPU资源的分配和调度，任何一步的低效都可能成为整体性能的瓶颈。
1.2 传统单队列网卡的局限性 #早期网卡采用单队列设计，所有网络数据包的处理都集中在一个队列中：
所有数据包 → 单个接收队列 → 单个CPU核心处理 → 性能瓶颈 这种设计在多核系统中存在明显问题：
单核心瓶颈：所有网络中断都在单个CPU核心上处理 资源浪费：其他CPU核心无法参与网络处理 扩展性差：网络吞吐量受限于单核心性能 2. 中断机制与IRQ基础 #2.1 什么是IRQ (Interrupt Request) #**IRQ（中断请求）**是计算机系统中硬件设备通知CPU需要处理某个事件的机制。在网络处理中，IRQ是连接硬件和软件的关键桥梁。
中断的基本概念：
中断：硬件设备向CPU发送的信号，表示有事件需要处理 IRQ号：标识不同中断源的唯一编号 中断向量：指向中断服务程序的内存地址 中断优先级：决定多个中断同时发生时的处理顺序 2.2 中断处理的完整流程 #从网卡数据包到CPU处理的中断流程：
1. 网卡接收数据包 ↓ 2. 网卡通过PCIe总线向中断控制器发送IRQ信号 ↓ 3. 中断控制器（APIC/IO-APIC）选择目标CPU ↓ 4. CPU接收中断信号，保存当前上下文 ↓ 5. CPU跳转到中断服务程序（ISR - Interrupt Service Routine） ↓ 6.</description></item><item><title>深入理解Socket类型：从原理到HFT应用的技术分析</title><link>https://code-agree.github.io/blog/2025-08-08-raw_socket/</link><pubDate>Fri, 08 Aug 2025 01:10:05 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-08-08-raw_socket/</guid><description>引言 #在现代网络编程中，Socket作为应用程序与网络协议栈的接口，为开发者提供了不同层次的网络访问能力。从高级的流式传输到底层的数据包控制，不同类型的Socket满足着各种应用场景的需求。本文将深入分析TCP Socket、UDP Socket和Raw Socket三种主要类型，探讨它们的技术原理、性能差异，并在高频交易(HFT)场景下进行实战分析。
Socket类型概述 #Socket本质上是操作系统提供的网络编程接口，它抽象了底层的网络通信细节。根据工作的协议层级和提供的抽象程度，主要分为三种类型：
基本分类 # Socket类型 创建方式 工作层级 抽象程度 应用场景 TCP Socket socket(AF_INET, SOCK_STREAM, 0) 传输层 高 可靠连接通信 UDP Socket socket(AF_INET, SOCK_DGRAM, 0) 传输层 中 无连接快速通信 Raw Socket socket(AF_INET, SOCK_RAW, protocol) 网络层 低 自定义协议开发 网络协议栈与Socket的对应关系 #协议栈层次结构 #应用层 | HTTP, WebSocket, 自定义协议 传输层 | TCP, UDP 网络层 | IP, ICMP 数据链路层 | Ethernet 物理层 | 电信号传输 Socket在协议栈中的位置 #TCP Socket: 完全封装传输层TCP协议，提供可靠的字节流传输。内核自动处理连接管理、流量控制、拥塞控制和数据重传。
UDP Socket: 封装传输层UDP协议，提供简单的数据报传输。内核处理端口管理和基本的错误检测。
Raw Socket: 直接访问网络层，绕过传输层处理。允许应用程序完全控制IP数据包的构造和发送。
各Socket类型详细分析 #TCP Socket (SOCK_STREAM) #工作原理 #TCP Socket基于可靠传输协议，提供面向连接的字节流服务：</description></item><item><title>深度解析UDP高丢包问题：从现象到原理的完整剖析</title><link>https://code-agree.github.io/blog/2025-07-27-network_buffer/</link><pubDate>Sun, 27 Jul 2025 11:36:17 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-07-27-network_buffer/</guid><description>深度解析UDP高丢包问题：从现象到原理的完整剖析 #在高性能网络应用中，UDP丢包问题是一个常见但复杂的挑战。本文将通过一个真实的案例，从问题现象出发，深入分析丢包的根本原因，并详细解释内核缓冲区与应用层缓冲区的区别和优化策略。
问题现象：91%的惊人丢包率 #测试环境与配置 #我们使用iperf3进行UDP性能测试，配置如下：
测试协议：UDP 目标带宽：100 Mbps 包大小：64字节 测试时长：60秒 客户端：172.28.15.164 服务端：47.83.183.226 (内网地址：192.168.24.102) 测试结果分析 #客户端发送情况（正常）：
Transfer: 715 MBytes (60秒内) Bitrate: 100 Mbits/sec (稳定发送) Total Datagrams: 11,718,940 (0丢失) 服务端接收情况（异常）：
Transfer: 66.4 MBytes (仅接收到9.3%的数据) Bitrate: 9.20 Mbits/sec (带宽骤降90%) Lost/Total: 10,610,551/11,699,051 (91%丢包率) 丢包模式的三个阶段 #通过详细分析服务端日志，我们发现了明显的性能退化模式：
初始阶段（0-1秒）：91.2 Mbps，丢包率0% 过渡阶段（1-5秒）：带宽逐渐下降，丢包率从6.8%急剧增加到75% 稳定阶段（6-60秒）：稳定在3 Mbps，丢包率高达97% 这种模式表明系统在面对高速UDP流量时，从初始的正常处理快速退化为严重的性能瓶颈状态。
根因分析：UDP接收缓冲区不足 #发现关键线索 #通过检查系统配置，我们发现了问题的根源：
$ cat /proc/sys/net/core/rmem_default 212992 $ cat /proc/sys/net/core/rmem_max 212992 关键发现：UDP接收缓冲区仅有208KB，这在高速网络环境中明显不足。
缓冲区容量计算 #让我们计算一下这个缓冲区的实际容量：
目标带宽：100 Mbps = 12.5 MB/s 包大小：64字节（仅载荷） 每秒包数：100 Mbps ÷ (64×8 bits) ≈ 195,312包/秒 208KB缓冲区可存储包数：208,896 ÷ 64 ≈ 3,264包 缓冲区满载时间：3,264 ÷ 195,312 ≈ 0.</description></item><item><title>DPDK为什么必须使用Hugepage：从内存管理本质到架构必需</title><link>https://code-agree.github.io/blog/2025-07-21-hugepage_indpdk/</link><pubDate>Mon, 21 Jul 2025 03:54:37 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-07-21-hugepage_indpdk/</guid><description>引言 #在高性能网络编程领域，DPDK（Data Plane Development Kit）作为用户态网络驱动框架，能够实现单核数千万PPS（包每秒）的惊人性能。然而，DPDK强制要求使用hugepage的设计决策常常让初学者困惑：为什么不能使用传统的malloc/new分配内存？hugepage究竟解决了什么根本性问题？
本文将从内存管理的底层原理出发，深入分析DPDK使用hugepage的技术必然性，揭示这一设计选择背后的深层架构考量。
1. 澄清关键概念：Hugepage不是&amp;quot;大内存分配&amp;quot; #1.1 常见的概念误区 #许多开发者错误地认为hugepage就是&amp;quot;分配大块内存&amp;quot;的机制，这是对hugepage本质的根本性误解。
错误理解：
// 误以为这就是hugepage的作用 char* large_buffer = malloc(1024 * 1024 * 1024); // 分配1GB内存 正确理解： Hugepage是操作系统内存管理粒度的改变，而不是应用层面的内存分配大小问题。
1.2 Hugepage的本质定义 #标准内存管理：
操作系统以4KB为单位管理物理内存 每个虚拟内存页对应一个4KB的物理内存页 页表项记录虚拟页到物理页的映射关系 Hugepage内存管理：
操作系统以2MB或1GB为单位管理物理内存 每个虚拟内存页对应一个2MB/1GB的物理内存页 大幅减少页表项数量，改变内存管理的基本粒度 1.3 malloc vs hugepage的本质差异 #malloc分配大内存的实际情况 #char* buffer = malloc(1024 * 1024 * 1024); // 分配1GB 虚拟内存视角：
应用程序看到连续的1GB虚拟地址空间 地址范围：[0x100000000 - 0x140000000] 物理内存实际情况：
需要的4KB页面数：1GB ÷ 4KB = 262,144个页面 页表项数量：262,144个 物理内存布局：完全分散，可能遍布整个物理内存空间 典型的物理地址映射： 虚拟页0x100000000 → 物理页0x87654000 虚拟页0x100001000 → 物理页0x12345000 虚拟页0x100002000 → 物理页0x98765000 .</description></item><item><title>深入理解Hugepage与TLB：原理、机制与性能优化</title><link>https://code-agree.github.io/blog/2025-07-21-hugepage/</link><pubDate>Mon, 21 Jul 2025 03:07:49 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-07-21-hugepage/</guid><description>引言 #在现代高性能计算中，内存访问性能往往成为应用程序的瓶颈。虽然CPU性能在摩尔定律驱动下快速提升，但内存访问延迟的改善相对缓慢，导致了著名的&amp;quot;内存墙&amp;quot;问题。为了缓解这一问题，现代处理器和操作系统引入了多种机制，其中TLB（Translation Lookaside Buffer）和Hugepage是两个关键的技术。
本文将深入探讨这两种技术的工作原理，以及它们如何协同工作来提升系统性能。
1. 虚拟内存基础 #1.1 虚拟内存系统概述 #现代操作系统普遍采用虚拟内存管理，每个进程都拥有独立的虚拟地址空间。虚拟地址需要通过页表（Page Table）转换为物理地址才能进行实际的内存访问。
1.2 x86-64页表结构 #在x86-64架构中，虚拟地址使用48位有效位，采用4级页表结构：
虚拟地址 (48位有效位)： [47:39] PML4索引 (9位) -&amp;gt; PML4表 (Page Map Level 4) [38:30] PDPT索引 (9位) -&amp;gt; 页目录指针表 (Page Directory Pointer Table) [29:21] PD索引 (9位) -&amp;gt; 页目录表 (Page Directory) [20:12] PT索引 (9位) -&amp;gt; 页表 (Page Table) [11:0] 页内偏移 (12位) -&amp;gt; 4KB页面内偏移 1.3 地址转换过程 #标准4KB页面的地址转换需要遍历完整的4级页表：
physical_addr translate_address(virtual_addr vaddr) { // 1. 从CR3寄存器获取PML4表基址 pml4_entry = PML4_BASE + ((vaddr &amp;gt;&amp;gt; 39) &amp;amp; 0x1FF) * 8; // 2.</description></item><item><title>C++数组类型的底层原理分析</title><link>https://code-agree.github.io/blog/2025-07-17-c++origin_array/</link><pubDate>Thu, 17 Jul 2025 03:21:09 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-07-17-c++origin_array/</guid><description>std::array 与 int a[10] 的区别与优点 #从类型系统角度 #std::array 是一个类模板，而 int a[10] 是一个内建数组类型。这个根本区别导致了它们在C++类型系统中的行为差异。
类型退化 #传统数组 int a[10] 在作为函数参数传递时会退化(decay)为指针 int*，导致数组大小信息丢失。这种退化是C语言遗留问题，在C++中仍然存在：
void func(int arr[10]) { // 实际上arr的类型是int*，大小信息已丢失 sizeof(arr); // 返回指针大小，而非数组大小 } 而 std::array 是一个完整的对象类型，传递时保留其完整类型信息：
void func(std::array&amp;lt;int, 10&amp;gt;&amp;amp; arr) { sizeof(arr); // 正确返回整个数组大小 } 作为值类型 #std::array 是真正的&amp;quot;值类型&amp;quot;，可以:
完整复制（不会退化为指针） 用于函数返回值 在STL容器中存储 参与比较操作（支持==, &amp;lt;等运算符） 不可赋值性与返回限制的底层原理 #原生数组存在两个重要限制：
T buffer1[10]; T buffer2[10]; buffer1 = buffer2; // 编译错误 auto get_buffer() -&amp;gt; ??? { return buffer; // 错误：无法直接返回原生数组 } 1. 不能直接赋值的底层原理：</description></item><item><title>C++17核心特性深度解析：constexpr if、std::optional与std::string_view</title><link>https://code-agree.github.io/blog/2025-07-17-c++17_new_feature/</link><pubDate>Thu, 17 Jul 2025 02:07:40 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-07-17-c++17_new_feature/</guid><description>引言 #C++17作为C++标准的重要里程碑，引入了众多革命性的特性，其中constexpr if、std::optional和std::string_view三个特性在性能优化和代码表达力方面具有深远影响。本文将深入解析这三个特性的设计理念、实现机制，以及它们在现代C++开发特别是高性能计算场景中的应用价值。
1. constexpr if：编译时条件分支的革命 # constexper是C++11引入的
1.1 基本概念与语法 #constexpr if是C++17引入的编译时条件语句，允许在模板中根据编译时常量表达式有条件地包含或排除代码分支。
template&amp;lt;typename T&amp;gt; constexpr auto process_data(T data) { if constexpr (std::is_integral_v&amp;lt;T&amp;gt;) { return data * 2; // 只有整数类型才会编译此分支 } else if constexpr (std::is_floating_point_v&amp;lt;T&amp;gt;) { return data * 1.5; // 只有浮点类型才会编译此分支 } else { return data; // 其他类型的默认处理 } } 1.2 与传统SFINAE的对比 #传统SFINAE方式：
// C++11/14 复杂的SFINAE实现 template&amp;lt;typename T&amp;gt; typename std::enable_if_t&amp;lt;std::is_integral_v&amp;lt;T&amp;gt;, T&amp;gt; process_data(T data) { return data * 2; } template&amp;lt;typename T&amp;gt; typename std::enable_if_t&amp;lt;std::is_floating_point_v&amp;lt;T&amp;gt;, T&amp;gt; process_data(T data) { return data * 1.</description></item><item><title>C++ map与unordered_map详解</title><link>https://code-agree.github.io/blog/2025-07-17-map_unordered_map/</link><pubDate>Thu, 17 Jul 2025 01:44:22 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-07-17-map_unordered_map/</guid><description>基本概念 #map（有序映射） # 定义：基于键值对的有序关联容器 头文件：#include &amp;lt;map&amp;gt; 特点：元素按键值自动排序存储 unordered_map（无序映射） # 定义：基于键值对的无序关联容器 头文件：#include &amp;lt;unordered_map&amp;gt; 特点：元素无序存储，通过哈希表实现快速访问 底层实现原理 #map的底层实现：红黑树(BST + 自平衡) #数据结构 #template&amp;lt;typename Key, typename Value&amp;gt; struct MapNode { std::pair&amp;lt;Key, Value&amp;gt; data; // 键值对 MapNode* left; // 左子节点 MapNode* right; // 右子节点 MapNode* parent; // 父节点 bool color; // 红色(true) 或 黑色(false) }; 红黑树特性 # 每个节点要么是红色，要么是黑色 根节点是黑色 所有叶子节点（NIL）是黑色 红色节点的两个子节点都是黑色（不能有连续的红色节点） 从任意节点到其每个叶子的所有简单路径都包含相同数目的黑色节点 平衡机制与时间复杂度分析 #红黑树通过旋转和重新着色维持平衡，这是其时间复杂度为O(log n)的根本原因：
为什么是O(log n)？
树高度控制：红黑树的特性保证了树的高度不会超过2*log₂(n+1) 路径长度限制：最长路径不超过最短路径的2倍 操作路径：查找、插入、删除都沿着从根到叶的路径进行 // 查找操作的时间复杂度分析 Node* find(const Key&amp;amp; key) { Node* current = root; int steps = 0; // 统计步数 while (current !</description></item><item><title>高频交易系统中的背压机制设计讨论</title><link>https://code-agree.github.io/blog/2025-07-15-backpress/</link><pubDate>Tue, 15 Jul 2025 19:36:31 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-07-15-backpress/</guid><description>摘要 #在高频交易（HFT）系统中，当市场数据突发性爆增时，如何在保证超低延迟的前提下防止系统过载是一个关键技术挑战。本文深入分析了背压机制的原理、常见实现方式，并针对HFT系统的特殊需求，设计了一套基于数据优先级分层的混合背压策略。通过理论分析证明，该方案在保证关键数据零丢失的同时，能够有效应对trade数据的burst场景。
1. 背景与问题定义 #1.1 HFT系统的数据特征 #高频交易系统通常需要处理三类核心市场数据：
BBO（Best Bid Offer）数据：实时更新，对策略决策至关重要，频率约1000-10000次/秒 Orderbook数据：通常100ms更新一次，提供市场深度信息，数据量中等 Trade数据：实时更新，频率极高且具有突发性（burst）特征，正常情况下1000-5000次/秒，burst时可达50000+次/秒 1.2 Burst问题的本质 #在某些市场事件（如重大新闻发布、大单成交）触发下，trade数据可能在毫秒级时间窗口内激增至正常流量的10-100倍。这种突发性负载会导致：
内存溢出：缓冲区被大量trade数据填满 延迟恶化：处理延迟从微秒级恶化到毫秒级 数据丢失：关键的BBO和orderbook更新被遗漏 系统崩溃：极端情况下导致OOM或死锁 2. 背压机制理论基础 #2.1 背压的定义与数学模型 #背压（Backpressure）是一种流控制机制，当系统下游处理能力不足时，向上游传递&amp;quot;减缓输入&amp;quot;的信号，从而维持系统稳定性。
设系统输入速率为λ（events/second），处理速率为μ，缓冲区大小为B：
稳定条件：λ ≤ μ 缓冲区利用率：ρ = λ/μ 背压触发阈值：当缓冲区占用率 &amp;gt; θ（通常θ = 0.8）时启动 当λ &amp;gt; μ时，缓冲区积压量呈线性增长：
积压量(t) = (λ - μ) × t + 初始积压 背压机制的目标是通过动态调整有效输入速率λ&amp;rsquo;，使得λ&amp;rsquo; ≤ μ，从而保证系统稳定性。
2.2 背压的质量评估指标 # 延迟保障：P99延迟 &amp;lt; 目标阈值 吞吐保持：关键数据处理率 ≥ 99% 系统稳定性：内存使用率 &amp;lt; 安全阈值 数据完整性：重要数据丢失率 &amp;lt; 0.01% 3. 常见背压机制分析 #3.1 阻塞式背压（Blocking Backpressure） #原理：当缓冲区满时，阻塞生产者直到有空间可用。</description></item><item><title>深度解析：Asio在Linux平台的I/O模型本质</title><link>https://code-agree.github.io/blog/2025-07-09-asio/</link><pubDate>Wed, 09 Jul 2025 02:19:26 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-07-09-asio/</guid><description>在现代C++网络编程中，Boost.Asio（或standalone asio）是使用最广泛的异步I/O库之一。然而，关于Asio的I/O模型本质，特别是在Linux平台上的实现机制，存在很多误解。本文将深入剖析Asio在Linux平台的真实面目。
本质定位 #Asio的真实身份 #Asio在Linux平台上的本质：同步非阻塞I/O + I/O多路复用 + 回调机制的高级封装
这意味着：
✅ 不是传统的阻塞I/O：提供了异步编程接口 ❌ 不是真正的异步I/O：底层仍使用同步系统调用 ✅ 是异步编程框架：通过回调机制模拟异步编程体验 核心理解 #// Asio给你的印象（异步风格API） socket.async_read_some(buffer(data), [](error_code ec, size_t bytes) { // 看起来像异步回调 process_data(data, bytes); }); // 但Linux下的实际执行（简化） epoll_wait(epfd, events, 128, -1); // 同步等待事件 ssize_t n = read(fd, buffer, size); // 同步读取 callback(n); // 调用用户回调 关键洞察：Asio提供了异步的编程体验，但不是异步的执行机制。
工作原理 #事件驱动的执行模型 #Asio在Linux上实现了Reactor模式，而不是Proactor模式：
// Reactor模式的典型流程 class AsioReactor { public: void async_read(socket&amp;amp; s, buffer b, handler h) { // 1. 注册读取意图 register_read_intent(s.</description></item><item><title>I/O模型深度解析：多路复用、同步与异步的本质区别</title><link>https://code-agree.github.io/blog/2025-07-09-bio_nio/</link><pubDate>Wed, 09 Jul 2025 01:57:53 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-07-09-bio_nio/</guid><description>在高性能网络编程中，I/O模型的选择往往决定了系统的并发能力和性能表现。然而，关于I/O多路复用、同步I/O、异步I/O等概念，许多开发者存在理解上的误区。本文将深入剖析这些概念的本质区别，澄清常见的混淆点。
常见的概念混淆 #误区一：I/O多路复用就是异步I/O #这是最常见的误解。实际上，I/O多路复用本质上仍然是同步I/O。准确地说，多路复用的事件检测阶段是阻塞的（epoll_wait会阻塞），而实际的I/O读写阶段仍是同步操作。它只是提供了&amp;quot;等待多个同步I/O事件 + 显式事件通知&amp;quot;的机制，而不是真正的异步执行。
误区二：非阻塞I/O就是异步I/O #非阻塞I/O（NIO）虽然不会让线程阻塞等待，但仍然是同步I/O，因为应用程序需要主动调用系统调用并立即处理返回结果（包括&amp;quot;暂时无数据&amp;quot;的情况）。
系统层面的I/O模型分类 #1. 同步阻塞I/O (BIO) #特点：应用程序发起I/O调用后，线程阻塞等待操作完成。
int sockfd = socket(AF_INET, SOCK_STREAM, 0); char buffer[1024]; // 线程会阻塞在这里，直到有数据到达 ssize_t n = read(sockfd, buffer, sizeof(buffer)); if (n &amp;gt; 0) { process_data(buffer, n); } 应用场景：
连接数较少的服务 对实时性要求不高的应用 通常配合多线程使用 2. 同步非阻塞I/O (NIO) #特点：应用程序发起I/O调用后立即返回，需要主动检查操作状态。
int sockfd = socket(AF_INET, SOCK_STREAM, 0); // 设置为非阻塞模式 int flags = fcntl(sockfd, F_GETFL, 0); fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); char buffer[1024]; ssize_t n = read(sockfd, buffer, sizeof(buffer)); if (n &amp;gt; 0) { // 成功读取数据 process_data(buffer, n); } else if (n == -1 &amp;amp;&amp;amp; errno == EAGAIN) { // 暂时无数据，需要稍后重试 // 这是关键：应用程序需要处理&amp;#34;未完成&amp;#34;状态 } else { // 发生错误 handle_error(); } 关键理解：read()调用立即返回，但返回的可能是&amp;quot;操作状态&amp;quot;而不是&amp;quot;完成的数据&amp;quot;。</description></item><item><title>DPDK + WebSocket客户端内存管理故障深度定位实录</title><link>https://code-agree.github.io/blog/2025-07-05-dpdk_application/</link><pubDate>Sat, 05 Jul 2025 01:38:06 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-07-05-dpdk_application/</guid><description>问题背景 #在开发基于DPDK的高性能WebSocket客户端时，遇到了典型的内存管理问题。该客户端使用了QuickWS框架，集成F-Stack网络栈和OpenSSL，在连接Binance WebSocket API进行高频数据接收测试时出现段错误。
技术栈概览 # 网络栈: DPDK + F-Stack WebSocket库: QuickWS (自定义高性能框架) SSL/TLS: OpenSSL 3.x 内存分配器: Flash Allocator (自定义分配器) 缓冲区: Ring Buffer with Flash Allocator 目标: 高吞吐量实时数据接收性能测试 故障现象 #Connected to Binance WebSocket stream! fd: 1 Accepted protocols: , extensions: Thread 1 &amp;#34;binance_client&amp;#34; received signal SIGSEGV, Segmentation fault. 定位过程 #第一阶段：环境问题排查 #初始现象: 程序在DPDK初始化阶段就出现问题
EAL: Auto-detected process type: SECONDARY EAL: Fail to recv reply for request /var/run/dpdk/rte/mp_socket:bus_vdev_mp 解决方案: 清理DPDK残留资源
sudo rm -rf /var/run/dpdk/rte/mp_socket* sudo rm -rf /dev/hugepages/* 关键发现: DPDK多进程模式的资源竞争会导致初始化挂起。</description></item><item><title>strace 完全使用指南</title><link>https://code-agree.github.io/blog/2025-07-03-strace/</link><pubDate>Thu, 03 Jul 2025 05:00:56 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-07-03-strace/</guid><description>strace 完全使用指南 #目录 # strace 简介 基础语法 核心参数详解 过滤和跟踪选项 输出格式控制 性能分析参数 实用场景示例 输出解读指南 性能调优技巧 最佳实践 1. strace 简介 #1.1 什么是 strace #strace 是 Linux 系统下的系统调用跟踪工具，它可以：
监控进程执行的所有系统调用 显示系统调用的参数和返回值 统计系统调用的执行时间和频率 跟踪信号传递过程 分析程序的系统级行为 1.2 主要用途 #性能分析 → 找出系统调用瓶颈 故障排查 → 定位程序异常原因 安全审计 → 监控程序系统访问 逆向分析 → 理解程序运行机制 系统调优 → 优化系统调用使用 2. 基础语法 #2.1 命令格式 ## 基础语法 strace [选项] [命令] strace [选项] -p &amp;lt;进程ID&amp;gt; # 示例 strace ls /tmp # 跟踪 ls 命令 strace -p 1234 # 跟踪进程ID为1234的进程 strace -e trace=network curl baidu.</description></item><item><title>DPDK性能验证技术分享</title><link>https://code-agree.github.io/blog/2025-07-03-dpdk_check/</link><pubDate>Thu, 03 Jul 2025 04:46:05 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-07-03-dpdk_check/</guid><description>目录 # DPDK性能优势概述 性能验证维度 系统调用分析 上下文切换监控 CPU使用效率分析 内存访问优化验证 网络性能基准测试 性能指标解读 验证方法总结 1. DPDK性能优势概述 #1.1 传统网络栈 vs DPDK架构 #传统网络栈流程：
应用程序 → Socket API → 内核网络栈 → 网卡驱动 → 硬件 ↑ 系统调用开销 ↑ 内核态/用户态切换 ↑ 数据拷贝 ↑ 中断处理 DPDK流程：
应用程序 → DPDK API → PMD → 硬件 ↑ 用户态直接操作 ↑ 零拷贝 ↑ 轮询模式 ↑ CPU绑定 1.2 理论性能提升 # 优化点 传统方式 DPDK方式 预期提升 延迟 50-100μs 5-20μs 3-10倍 吞吐量 1-5 Gbps 10-100 Gbps 10-20倍 CPU效率 50-70% 80-95% 1.</description></item><item><title>高频交易系统中的WebSocket网络缓冲区优化技术</title><link>https://code-agree.github.io/blog/2025-07-01-tcp_perf/</link><pubDate>Tue, 01 Jul 2025 18:06:02 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-07-01-tcp_perf/</guid><description>摘要 #本文深入探讨了高频交易系统中WebSocket连接的网络缓冲区优化技术，重点关注极低延迟性能优化。文章详细分析了TCP层优化、Socket缓冲区调优、大页内存应用以及网络栈各层次的性能优化策略，为构建微秒级延迟的交易系统提供了全面的技术指南。
1. 引言 #在现代金融市场中，高频交易系统的竞争优势很大程度上取决于其网络栈的性能。WebSocket作为一种全双工通信协议，已成为高频交易系统连接交易所和市场数据提供商的重要技术。然而，标准WebSocket实现通常无法满足高频交易对极低延迟的苛刻要求，这些系统需要微秒级别的响应时间。
本文旨在提供一个全面的网络缓冲区优化框架，从TCP底层协议到WebSocket应用层，系统性地探讨如何将延迟降至最低，尤其是通过优化网络缓冲区结构和内存访问模式。
2. 网络栈基础概念与缓冲区架构 #2.1 TCP与Socket的关系与区别 #在深入优化之前，需要明确TCP与Socket这两个核心概念的区别与联系：
概念层面 #TCP (传输控制协议):
是一种通信协议，定义了数据如何在网络上可靠传输的规则 是OSI模型中的传输层协议 规定了如何建立连接、传输数据、处理丢包、确保顺序、流量控制等机制 是一组规则和标准，而非具体实现 Socket (套接字):
是一个编程接口/抽象，是应用程序与网络协议交互的途径 可以看作是网络通信的&amp;quot;端点&amp;quot; 是操作系统提供的API，让应用程序能够使用网络功能 Socket不仅可以使用TCP，还可以使用UDP、Unix域等协议 比喻说明 #可以通过这个比喻理解：
TCP是一种语言和交流规则(如中文+礼仪规范) Socket是允许人们使用这种语言交流的电话机 代码层面区别 #TCP体现为协议参数和行为：
// 这些是TCP协议相关的选项 setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &amp;amp;flag, sizeof(flag)); setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE, &amp;amp;flag, sizeof(flag)); Socket体现为创建和管理通信端点：
// 创建套接字 int sockfd = socket(AF_INET, SOCK_STREAM, 0); // SOCK_STREAM指定TCP协议 // 设置套接字选项 setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &amp;amp;rcvbuf, sizeof(rcvbuf)); // 连接、发送、接收数据 connect(sockfd, ...); send(sockfd, ...); recv(sockfd, ...); 缓冲区层面的区别 #TCP缓冲区:</description></item><item><title>C++中inline函数为何比普通函数调用更快：深入解析</title><link>https://code-agree.github.io/blog/2025-06-24-inline_function_optimization/</link><pubDate>Tue, 24 Jun 2025 16:16:15 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-inline_function_optimization/</guid><description>目录 # 普通函数调用的开销 inline函数的优化 inline函数的局限性与权衡 示例代码与分析 什么时候使用inline函数？ 现代编译器的优化 总结 1. 普通函数调用的开销 #在C++中，普通函数调用涉及以下步骤，这些步骤会引入性能开销：
栈帧的创建与销毁： 调用函数时，程序需要保存当前函数的状态（例如寄存器中的值），为被调用函数分配栈空间，设置栈帧（包括返回地址、参数传递、局部变量等）。 函数返回时，需要恢复调用者的栈帧状态。 这些操作涉及栈指针（SP）和基指针（BP）的调整，以及内存的读写操作。 参数传递与返回值： 参数需要通过栈或寄存器传递到被调用函数，这可能涉及内存拷贝（尤其是对于较大的结构体或对象）。 返回值也需要通过寄存器或内存传递回调用者。 跳转开销： 函数调用需要将程序计数器（PC）跳转到被调用函数的地址（通过call指令），返回时再跳回调用者（通过ret指令）。 对于直接调用（call到已知地址），现代CPU的分支预测器几乎可以做到100%准确，因此很少引发流水线刷新。跳转的主要开销来自call/ret指令本身的执行、栈帧的建立与拆除，以及跳转目标代码可能不在指令缓存（I-Cache）中导致的缓存未命中。对于间接调用（如虚函数调用，通过函数指针跳转），由于目标地址在运行时才确定，分支预测失败的概率较高，此时才更容易导致流水线刷新。 寄存器上下文保存/恢复： 调用函数可能导致寄存器内容的保存与恢复（例如调用者保存的寄存器或被调用者保存的寄存器），增加额外的指令开销。 这些步骤虽然在现代CPU上非常快，但对于频繁调用的函数（例如小型、简单函数），这些开销可能占函数执行时间的显著比例。
2. inline函数的优化 #inline关键字建议编译器将函数的代码直接嵌入到调用处，而不是生成函数调用。这种内联（inlining）优化可以显著减少上述开销，原因如下：
消除函数调用开销： 内联函数的代码直接嵌入到调用处，省去了栈帧的创建与销毁、参数传递、返回值的处理以及跳转指令。 程序无需执行call和ret指令，也避免了可能的流水线刷新。 优化机会增加： 编译器在优化阶段可以看到内联函数的完整代码上下文，可以应用更多的优化技术，例如： 常量折叠：如果内联函数的参数是常量，编译器可以直接计算结果。 死代码消除：如果内联函数中某些分支在调用上下文中永远不会执行，编译器可以剔除这些代码。 循环展开或指令重排：内联后，编译器可以更好地调整指令顺序，优化CPU缓存利用率或减少分支跳转。 减少指令数： 对于小型函数，函数调用的开销可能比函数体本身的执行时间还长。内联后，函数体的代码直接嵌入，减少了额外的指令（如push、pop、call、ret等）。 3. inline函数的局限性与权衡 #虽然inline函数通常更快，但它并非总是最佳选择，以下是一些需要注意的点：
代码膨胀： 内联函数会将函数代码复制到每个调用点，如果函数体较大或调用点很多，可能导致生成的机器代码体积显著增加。这可能导致： 指令缓存（I-Cache）效率下降：代码体积过大可能无法完全放入CPU的指令缓存，增加缓存未命中（cache miss）。 可执行文件变大：增加编译后二进制文件的大小。 编译器的自主决定： inline关键字只是一个建议，现代编译器（如GCC、Clang、MSVC）会根据自己的优化策略决定是否内联。 编译器可能忽略inline关键字（例如函数体过大或过于复杂），也可能自动内联未标记为inline的函数（称为自动内联）。 例如，O2或O3优化级别下，编译器会根据函数的大小、调用频率等因素智能选择是否内联。 递归函数： 递归函数通常无法完全内联，因为内联会导致无限展开。编译器可能只内联部分递归调用（例如尾递归优化）。 调试难度： 内联函数的代码在调试时可能不可见，因为它们被展开后不再作为独立的函数存在，可能影响调试体验。 4. 示例代码与分析 #以下是一个简单的例子，展示普通函数调用与内联函数的差异：
#include &amp;lt;iostream&amp;gt; // 普通函数 int add(int a, int b) { return a + b; } // 内联函数 inline int inline_add(int a, int b) { return a + b; } int main() { int x = 5, y = 10; int result1 = add(x, y); // 普通函数调用 int result2 = inline_add(x, y); // 内联函数调用 std::cout &amp;lt;&amp;lt; result1 &amp;lt;&amp;lt; &amp;#34; &amp;#34; &amp;lt;&amp;lt; result2 &amp;lt;&amp;lt; std::endl; return 0; } 普通函数调用（add）：</description></item><item><title>深入理解 False Sharing：实测原子操作与缓存行对齐对性能的影响</title><link>https://code-agree.github.io/blog/2025-06-23-cache_false_sharing_analysis/</link><pubDate>Mon, 23 Jun 2025 15:39:27 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-23-cache_false_sharing_analysis/</guid><description>目录 #1. 引言 # False Sharing概念介绍 文章研究目标 2. 测试设计概览 # 测试用例矩阵 测试方法说明 3. 样例运行结果 # 普通变量 + False Sharing 普通变量 + 无False Sharing 原子变量 + False Sharing 原子变量 + 无False Sharing 4. 现象分析与原理解释 # False Sharing如何降低性能 alignas(64)避免False Sharing的原理 cache miss升高的原因分析 原子变量性能开销分析 5. 深入理解缓存一致性与原子操作 # MESI缓存一致性协议详解 普通变量与原子变量的对比 原子变量 + False sharing的性能影响 CPU指令层面的差异 缓存一致性协议的影响 微架构层面的详细分析 6. 实战优化建议 # 不同场景的优化策略 7. 总结 # False Sharing的关键要点 核心结论 8. 参考资料与相关阅读 #9. 附录：完整测试代码 # 引言 #在现代多核处理器架构中，缓存系统在性能中扮演着至关重要的角色。然而，当多个线程同时操作位于同一缓存行（Cache Line）内的不同变量时，即使它们并未共享变量本身，也可能导致频繁的缓存一致性协议交互，这就是著名的性能杀手——False Sharing。</description></item><item><title>C++原子操作内存序性能分析：seq_cst vs relaxed</title><link>https://code-agree.github.io/blog/2025-06-21-memory_order_performance_analysis/</link><pubDate>Sat, 21 Jun 2025 00:47:52 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-21-memory_order_performance_analysis/</guid><description>摘要 #本文分析了C++原子操作中不同内存序(memory ordering)对性能的影响，特别是比较了默认的顺序一致性(seq_cst)与宽松(relaxed)内存序在x86-64架构上的性能差异。通过实验测试、性能分析和汇编代码检查，我们发现即使在内存模型较强的x86架构上，不同内存序的选择仍然会产生可测量的性能差异。
1. 实验设计 #1.1 测试程序 #我们设计了两个版本的测试程序，它们在固定时间内执行原子变量的读取和计数操作，唯一区别是原子变量读取时使用的内存序不同：
seq_cst版本 (默认内存序):
void worker_seq_cst() { while (running_) { // 默认使用 seq_cst counter_.fetch_add(1, std::memory_order_relaxed); busy_loop(); } } relaxed版本 (显式指定宽松内存序):
void worker_relaxed_load() { while (running_.load(std::memory_order_relaxed)) { counter_.fetch_add(1, std::memory_order_relaxed); busy_loop(); } } 1.2 编译与执行环境 #测试程序使用以下命令编译：
g++ -std=c++11 -O0 -pthread atomic_test_seq_cst.cpp -o test_gcc_seq_cst g++ -std=c++11 -O0 -pthread atomic_test_relaxed.cpp -o test_gcc_relaxed 每个程序运行5秒钟，记录在此期间完成的操作次数。同时使用perf工具收集性能数据：
perf record -e cpu-clock:pppH ./test_gcc_seq_cst perf record -e cpu-clock:pppH ./test_gcc_relaxed 2. 实验结果 #2.1 执行计数结果 # 版本 操作计数 seq_cst 26,494,108 relaxed 26,660,082 性能差异：</description></item><item><title>LockFreeEventBus：从死锁案例到性能剖析</title><link>https://code-agree.github.io/blog/2025-06-20-lockfree_eventbus_performance_analysis/</link><pubDate>Fri, 20 Jun 2025 14:38:58 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-20-lockfree_eventbus_performance_analysis/</guid><description>概述 #本文是 LockFreeEventBus 的完整记录，分上下两部分：
上篇（第 1–5 节）：它的来历——一次真实的生产死锁排查，以及如何把基于互斥锁的 EventBus 重构为&amp;quot;无锁队列 + 异步分发&amp;quot;； 下篇（第 6–10 节）：对重构后的 LockFreeEventBus 做机制剖析与性能瓶颈分析——RTTI 分发、智能指针、false sharing 三大开销，以及针对性的优化建议。 背景阅读：文中涉及的无锁队列基础（内存序、环形缓冲、SPSC/MPMC 取舍）见SPSC 队列设计。
1. 问题发现：一次例行监控中的停顿 #在高频交易系统中，每一毫秒都至关重要。一次例行的系统监控中，注意到系统偶尔会出现短暂的停顿。通过日志分析，发现 MarketDataReader 的 readingLoop() 函数只执行了一次就停止了。
2. 定位：日志与 GDB 线程堆栈 #首先查看 MarketDataReader 的日志：
[2024-09-01 13:02:08.472] [main_logger] [MarketDataReader.cpp:38] [info] [thread 4048966] [start] Starting market data reader... [2024-09-01 13:02:08.472] [main_logger] [MarketDataReader.cpp:40] [info] [thread 4048966] [start] Starting start,and running_ = true [2024-09-01 13:02:08.489] [main_logger] [MarketDataReader.cpp:63] [info] [thread 4048967] [readingLoop] Starting reading loop.</description></item><item><title>高频交易中的订单数据结构设计与性能优化实战</title><link>https://code-agree.github.io/blog/2025-06-19-how_to_design_order_inlocalmemory/</link><pubDate>Thu, 19 Jun 2025 19:58:31 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-19-how_to_design_order_inlocalmemory/</guid><description>主题：基于并发读写性能优化的订单数据结构重构与底层机制剖析
目录 # 一、业务背景：订单状态的高并发维护 二、常见设计陷阱：char[] 字符串 ID 与哈希表的性能瓶颈 三、优化目标：极致的并发 + O(1) 访问性能 四、核心优化：整数 ID + array 映射结构 五、底层原理解析：为什么 array + int ID 更快? 1. 内存寻址机制(指针偏移) 2. CPU Cache Line 利用与伪共享问题 3. 避免堆分配与内存碎片 4. 内存序(Memory Ordering)选择与原子操作 5. 整数ID分配和回收机制 六、性能测试数据 七、关键组件优化示例 1. OrderBook实现优化 2. RingBuffer优化 八、NUMA架构下的内存访问优化 九、最终方案优势对比总结 十、结语：高频系统的设计哲学 一、业务背景：订单状态的高并发维护 #在高频交易(HFT)系统中，我们需要对数百万级别的订单状态进行并发读写，以支撑如下操作：
✅ 新增订单(add_order(order_id)) ✅ 修改订单状态(如 fill_qty, status 等) ✅ 高频查询订单状态(如成交均价、当前剩余量等) 这些操作高并发、延迟敏感，需要 O(1) 级别的响应，并且不能产生性能抖动或不可控的锁竞争。
二、常见设计陷阱：char[] 字符串 ID 与哈希表的性能瓶颈 #在早期系统中，常见的设计是以字符串 ID 作为订单主键，例如：
struct Order { char id[32]; char instId[16]; .</description></item><item><title>编译器优化级别技术解析</title><link>https://code-agree.github.io/blog/2025-06-19-compile_perf/</link><pubDate>Thu, 19 Jun 2025 04:00:00 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-19-compile_perf/</guid><description>1. 优化级别的本质与编译过程 #编译器优化是将源代码转换为更高效机器码的系统性过程，每个优化级别代表了不同的转换策略集合。要理解这些级别，首先需要了解编译器的工作流程：
词法分析 → 2. 语法分析 → 3. 语义分析 → 4. 中间表示生成 → 5. 优化 → 6. 代码生成 优化级别主要影响第5步，决定应用哪些转换算法及其激进程度。
2. -O0：零优化的底层机制 #核心原理 #-O0的本质是直接映射：保持源代码与生成的机器码之间的一一对应关系，几乎不进行任何转换。
底层实现机制 # 变量分配策略：
每个变量都分配独立的栈空间 即使是临时变量也会写回内存 不进行寄存器重用优化 指令生成逻辑：
严格按照源代码顺序生成指令 保留所有中间计算步骤 不合并冗余操作 函数调用处理：
严格遵循标准调用约定 保存和恢复所有可能被修改的寄存器 不进行任何内联或尾调用优化 技术深度剖析 #int calculate(int a, int b) { int temp = a * 2; return temp + b; } 在-O0级别，编译器生成的伪汇编代码：
calculate: push rbp ; 保存基址指针 mov rbp, rsp ; 建立新的栈帧 mov DWORD PTR [rbp-20], edi ; 存储参数a mov DWORD PTR [rbp-24], esi ; 存储参数b mov eax, DWORD PTR [rbp-20] ; 加载a add eax, eax ; a*2 mov DWORD PTR [rbp-4], eax ; 存储temp mov edx, DWORD PTR [rbp-4] ; 加载temp到edx mov eax, DWORD PTR [rbp-24] ; 加载b到eax add eax, edx ; b+temp pop rbp ; 恢复基址指针 ret ; 返回 这种实现方式的内存访问模式是：</description></item><item><title>Perf Report 分析完全指南 - 高频交易系统性能优化</title><link>https://code-agree.github.io/blog/2025-06-18-how_to_use_perf/</link><pubDate>Wed, 18 Jun 2025 03:03:07 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-18-how_to_use_perf/</guid><description>目录 # 高频交易系统性能优化思路 Perf 基础知识 perf record 命令参数详解 采样事件类型 Perf 能分析的关键指标 Perf Report 输出解析 列含义详解 分析方法论 高级分析技巧 实际优化流程 关键指标解读 使用Perf分析内存性能指标 高频交易系统案例分析 高频交易系统性能优化思路 #在高频交易系统中，微秒级的延迟差异可能直接影响交易策略的有效性和盈利能力。使用perf进行性能分析是优化高频交易系统的关键步骤。以下是一个系统化的优化思路：
1. 性能基准建立 #关键指标:
端到端延迟: 从行情接收到下单的完整路径时间 吞吐量: 每秒处理的订单/行情数量 尾延迟: 95/99/99.9百分位延迟 CPU利用率: 核心交易路径的CPU使用情况 # 建立基准性能数据 perf stat -e cycles,instructions,cache-references,cache-misses,branches,branch-misses -o perf_base.data -a -g ./strategyTrade 命令参数解释:
cycles: CPU周期数，用于测量程序执行所需的处理器周期总量 instructions: 执行的指令数，结合cycles可计算IPC(每周期指令数)，评估CPU利用效率 cache-references: 缓存访问次数，表示程序对CPU缓存的总访问量 cache-misses: 缓存未命中次数，高缓存未命中率会导致处理器等待内存，增加延迟 branches: 分支指令执行次数，反映程序中条件判断和跳转的频率 branch-misses: 分支预测失败次数，高失败率会导致流水线刷新，降低CPU效率 -o: 指定输出文件名 -a: 收集所有CPU核心的数据，全系统视图 -g: 收集调用图信息，便于分析函数调用关系 输出示例及解读:
Performance counter stats for &amp;#39;./strategyTrade&amp;#39;: 12,345,678,901 cycles # 总CPU周期数 24,680,046,512 instructions # 总指令数，指令/周期比约为2.</description></item><item><title>C++模板类用法详解 - 以LockFreeRingBuffer为例</title><link>https://code-agree.github.io/blog/2025-06-24-cpp_template_class_guide/</link><pubDate>Tue, 10 Jun 2025 22:48:21 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-cpp_template_class_guide/</guid><description>1. 分类 #有三种不同的模版类型，
Function templates class templates Variable templates 1.1. function templates #template&amp;lt;typename T&amp;gt; T max(T a, T b) { return (a &amp;gt; b) ? a : b; } // 使用：编译器自动推导类型 int x = max(3, 7); // T = int double y = max(3.14, 2.71); // T = double 多参数模版 template&amp;lt;typename T, typename U&amp;gt; auto add(T a, U b) { return a + b; } 函数模板的显式实例化 // 声明模板函数 template&amp;lt;typename T&amp;gt; void process(T value) { // 实现.</description></item><item><title>MmAvellaneda Stoikov</title><link>https://code-agree.github.io/blog/2025-06-24-avellaneda_stoikov_market_making/</link><pubDate>Mon, 19 May 2025 13:38:50 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-avellaneda_stoikov_market_making/</guid><description>这篇论文 《Optimal High-Frequency Market Making》 实现并分析了 Avellaneda-Stoikov (2008) 的高频做市定价模型，并引入了一个动态库存控制模块，用于优化限价单的挂单量，以在保证盈利的同时控制库存风险。下面是详细解读：
📌 一、研究背景与动机 #高频做市商（HFT market makers）通过在订单簿中持续挂出买卖限价单来提供流动性，赚取 买卖价差（spread） 和 交易所提供的挂单返利（rebate）。但这同时会产生库存风险（inventory risk），即买入或卖出过多后，价格波动带来的风险。
Avellaneda-Stoikov 模型是其中一个经典的高频做市定价框架，它在假设股票价格服从布朗运动的基础上，通过求解最优控制问题得出最优报价策略。
📌 二、模型框架 #2.1 定价模型（Pricing） #基于 Avellaneda &amp;amp; Stoikov (2008)：
股票价格服从布朗运动： $dS_t = \sigma dW_t$ 市场深度与成交概率关系：$\lambda(\delta) = A e^{-\kappa \delta}$ 做市商目标是最大化终端时刻 $T$ 时的指数效用函数： $$ \max_{\delta_a, \delta_b} \mathbb{E}[-e^{-\gamma (X_T + q_T S_T)}] $$
推导结果是：
中间价（Indifference Price）： $$ r(s, t) = s - q\gamma\sigma^2(T - t) $$
最优总挂单价差（Spread）： $$ \delta_a + \delta_b = \gamma\sigma^2(T - t) + \ln\left(1 + \frac{\gamma}{\kappa} \right) $$</description></item><item><title>行情数据解析优化最佳实践</title><link>https://code-agree.github.io/blog/2025-06-24-perf_tool_usage_guide/</link><pubDate>Wed, 30 Apr 2025 03:54:56 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-perf_tool_usage_guide/</guid><description>原始解析方案的性能瓶颈 #原始的 Binance 聚合交易数据解析实现存在多个性能瓶颈，这在高频交易系统中尤为关键。主要问题包括：
使用 std::stod 进行字符串到浮点数转换：
result.data.price = std::stod(std::string(price_str)); result.data.quantity = std::stod(std::string(qty_str)); 这里存在两个严重问题：
std::stod 在底层实现中需要处理各种格式和本地化，导致计算开销大 每次调用都创建了临时 std::string 对象，增加了内存分配和释放的开销 创建临时的 padded_string 对象：
simdjson::padded_string padded_json{json}; simdjson::dom::element doc = parser.parse(padded_json); 这会导致额外的内存分配和复制，特别是在高频率处理消息时变得非常明显。
使用低效的字符串复制方法：
strncpy(result.data.symbol, doc[&amp;#34;s&amp;#34;].get_string().value().data(), sizeof(result.data.symbol) - 1); 标准的 strncpy 没有利用现代 CPU 的 SIMD 指令集优势。
异常处理成本：在解析热路径中大量使用 try-catch 结构，这会导致编译器生成额外代码，影响性能。
重复获取 JSON 节点：多次访问相同的 JSON 节点，每次都需要进行字符串哈希查找。
优化方案 #为了解决上述问题，我们实施了多层次的优化策略：
1. 自定义快速解析路径 #创建了一个专门针对 Binance 聚合交易数据格式的快速解析函数，完全跳过通用 JSON 解析器：
bool fastParseAggTrade(const std::string_view&amp;amp; json, Common::QuoteData::AggTradeData&amp;amp; data) noexcept { // 快速检查消息类型 const char* type_pattern = &amp;#34;\&amp;#34;e\&amp;#34;:\&amp;#34;aggTrade\&amp;#34;&amp;#34;; if (json.</description></item><item><title>TLS会话恢复（Session Resumption）</title><link>https://code-agree.github.io/blog/2025-06-24-session_resumption_techniques/</link><pubDate>Fri, 07 Mar 2025 19:24:12 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-session_resumption_techniques/</guid><description>1. 会话恢复简介 #什么是会话恢复？ #TLS会话恢复是TLS协议的一项优化特性，允许客户端和服务器基于之前建立的安全会话快速恢复通信，跳过完整的握手过程。在TLS 1.3中，会话恢复主要通过**PSK（Pre-Shared Key，预共享密钥）**机制实现，而在TLS 1.2及更早版本中，也可以通过Session ID或Session Ticket实现。
为什么需要会话恢复？ # 性能优化： 完整握手（TLS 1.3）：1-RTT 会话恢复：1-RTT（或0-RTT） 显著减少连接建立时间 资源节省： 降低CPU开销（避免重复密钥交换） 减少网络带宽占用 2. TLS 1.3中的会话恢复机制 #工作流程对比 #完整握手（TLS 1.3） #Client Server | ClientHello | |--------------------&amp;gt;| | ServerHello | | EncryptedExt | | Certificate | | CertVerify | | Finished | |&amp;lt;--------------------| | Finished | |--------------------&amp;gt;| | NewSessionTicket | |&amp;lt;--------------------| RTT：1次往返 服务器在握手后发送NewSessionTicket，包含PSK和有效期信息。 会话恢复（1-RTT） #Client Server | ClientHello | | (with PSK) | |--------------------&amp;gt;| | ServerHello | | Finished | |&amp;lt;--------------------| | Finished | |--------------------&amp;gt;| RTT：1次往返 客户端使用之前保存的PSK直接恢复会话。 0-RTT（可选） #Client Server | ClientHello | | (with PSK + Early Data) | |--------------------&amp;gt;| | ServerHello | | Finished | |&amp;lt;--------------------| | Finished | |--------------------&amp;gt;| RTT：0次往返（早期数据随首次请求发送） 注意：0-RTT有重放攻击风险，仅适用于幂等请求。 3.</description></item><item><title>使用 Tailscale 实现 MacOS 设备远程连接教程</title><link>https://code-agree.github.io/blog/2025-06-24-screen_sharing_techniques/</link><pubDate>Sun, 19 Jan 2025 11:15:08 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-screen_sharing_techniques/</guid><description>我来帮你编写一份详细的技术教程，介绍如何使用 Tailscale 在 MacOS 设备间实现远程连接。
使用 Tailscale 实现 MacOS 设备远程连接教程 #准备工作 # 确保两台 MacOS 设备都能正常访问互联网 准备一个 Tailscale 账号（可以使用 Google、GitHub 等账号登录） 详细步骤 #第一步：安装 Tailscale #在两台 MacOS 设备上分别安装 Tailscale：
访问 Tailscale 官网 (https://tailscale.com/download) 下载 MacOS 版本的安装包 打开下载的 .dmg 文件，将 Tailscale 拖入应用程序文件夹 第二步：登录和配置 # 在两台设备上启动 Tailscale 点击菜单栏的 Tailscale 图标 使用相同的账号登录 登录成功后，Tailscale 会自动为设备分配 IP 地址 点击菜单栏图标可以查看分配的 IP 地址（通常格式为 100.xx.xx.xx） 第三步：开启远程访问 #在被控制的 MacOS 设备上：
打开系统偏好设置 选择&amp;quot;共享&amp;quot; 勾选&amp;quot;远程管理&amp;quot;或&amp;quot;屏幕共享&amp;quot; 配置访问权限，可以选择： 允许所有用户 仅允许特定用户 第四步：建立连接 #在控制端 MacOS 设备上：
打开访达（Finder） 在菜单栏选择&amp;quot;前往&amp;quot; → &amp;ldquo;连接服务器&amp;rdquo;（或按下 Command + K） 在服务器地址栏输入：vnc://100.</description></item><item><title>共享内存多进程通信中的页面切换同步问题分析与解决</title><link>https://code-agree.github.io/blog/2025-06-24-fix_shared_page_position/</link><pubDate>Fri, 17 Jan 2025 04:21:23 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-fix_shared_page_position/</guid><description>问题现象 #在多进程共享内存通信中，发现读取进程出现异常：
写入进程（线程3002707）正常写入数据
读取进程（线程3002791）卡在固定位置：
page: 0 write_pos: 134209160 read_pos: 134199368 问题定位过程 #1. 初步分析 #首先观察到一个关键现象：
Binance的读写正常 Bitget的读取卡在固定位置 两个交易所使用相同的共享内存机制 2. 代码分析 #检查共享内存管理的核心类：
写入机制： template&amp;lt;typename T&amp;gt; bool write(const TypedFrame&amp;lt;T&amp;gt;&amp;amp; frame) { // ... if (write_pos + frame_size &amp;gt; page_size_) { switchToNextPage(); write_pos = current_write_pos_.load(std::memory_order_relaxed); continue; } // ... std::atomic&amp;lt;size_t&amp;gt;* shared_write_pos = reinterpret_cast&amp;lt;std::atomic&amp;lt;size_t&amp;gt;*&amp;gt;(current_page_-&amp;gt;getData()); shared_write_pos-&amp;gt;store(write_pos + frame_size, std::memory_order_release); } 页面切换： void Journal::switchToNextPage() { current_page_ = page_engine_-&amp;gt;getNextPage(); current_write_pos_.store(0, std::memory_order_relaxed); } Page* PageEngine::getNextPage() { current_page_index_++; if (current_page_index_ &amp;gt;= pages_.</description></item><item><title>Solana链上交易监控最佳实践：从logsSubscribe到全方位监控</title><link>https://code-agree.github.io/blog/2025-06-24-solana_monitoring_system/</link><pubDate>Fri, 20 Dec 2024 03:08:38 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-solana_monitoring_system/</guid><description>Solana链上交易监控最佳实践：从logsSubscribe到全方位监控 #背景介绍 #在Solana链上开发中，实时监控特定账户的交易活动是一个常见需求，特别是在构建跟单机器人这类对时效性要求较高的应用场景中。最初，我们可能会想到使用Solana提供的logsSubscribe WebSocket API来实现这个功能，因为它看起来是最直接的解决方案。然而，在实际应用中，我们发现这种方案存在一些限制和问题。
问题发现 #在使用logsSubscribe进行账户监控时，我们发现一个关键问题：某些确实发生的交易并没有被我们的监控系统捕获到。这个问题的发现促使我们深入研究Solana的交易日志机制，并最终设计了一个更全面的监控方案。
为什么会遗漏交易？ # 日志记录机制的局限性
程序可能不会在日志中明确记录所有涉及的账户地址 交易可能使用了PDA(Program Derived Address)或其他派生地址 某些DEX采用内部账户映射，而不是直接记录用户地址 mentions过滤器的限制
mentions匹配的是交易账户列表（accountKeys）中包含目标地址的交易，而非日志文本中出现的地址，因此只有当目标地址作为交易的显式参与账户时才会被捕获 无法捕获通过间接方式（如CPI调用中未在顶层accountKeys中列出）影响目标账户的交易 解决方案 #针对上述问题，我们设计了一个多维度监控方案，通过组合多种订阅方式来确保不会遗漏任何相关交易。
1. 三重订阅机制 #pub struct EnhancedTradeWatcher { target_account: Pubkey, ws_client: WebSocketClient, } impl EnhancedTradeWatcher { async fn setup_comprehensive_monitoring(&amp;amp;mut self) -&amp;gt; Result&amp;lt;()&amp;gt; { // 1. logsSubscribe - 捕获显式提及 let logs_sub = json!({ &amp;#34;jsonrpc&amp;#34;: &amp;#34;2.0&amp;#34;, &amp;#34;method&amp;#34;: &amp;#34;logsSubscribe&amp;#34;, &amp;#34;params&amp;#34;: [ { &amp;#34;mentions&amp;#34;: [self.target_account.to_string()], }, { &amp;#34;commitment&amp;#34;: &amp;#34;processed&amp;#34; } ] }); // 2. programSubscribe - 监控DEX程序 let dex_program_sub = json!</description></item><item><title>Solana链上交易监控技术分析</title><link>https://code-agree.github.io/blog/2025-06-24-solana_blockchain_analysis/</link><pubDate>Thu, 19 Dec 2024 01:32:32 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-solana_blockchain_analysis/</guid><description>Solana链上交易监控技术分析 #1. Solana DEX 交易形式 #1.1 直接 DEX 交易 #用户直接与 DEX 合约交互，交易流程简单直接。
用户钱包 -&amp;gt; DEX程序 (如Raydium/Orca) -&amp;gt; Token Program 特点：
交易日志简洁，主要包含单个 DEX 程序的调用 容易识别交易平台和交易对 Token Program 的 transfer 指令较少 1.2 聚合器交易（Jupiter） #通过聚合器路由到单个或多个 DEX。
用户钱包 -&amp;gt; Jupiter -&amp;gt; DEX1/DEX2/... -&amp;gt; Token Program 特点：
包含 Jupiter 合约调用 可能涉及多个 DEX 交易日志较长，包含多个内部指令 可能有复杂的代币交换路径 1.3 智能路由交易 #一笔交易通过多个 DEX 串联完成。
用户钱包 -&amp;gt; 聚合器 -&amp;gt; DEX1 -&amp;gt; DEX2 -&amp;gt; DEX3 -&amp;gt; Token Program 特点：
交易路径最复杂 涉及多次代币交换 目的是获得最优价格 包含多个 Token Program 的 transfer 指令 2.</description></item><item><title>WebSocket消息处理线程CPU亲和性导致的消息阻塞故障分析</title><link>https://code-agree.github.io/blog/2025-06-24-message_queue_overstocking_solutions/</link><pubDate>Fri, 13 Dec 2024 05:15:51 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-message_queue_overstocking_solutions/</guid><description>一、故障现象 #1.1 单endpoint模式故障 # 单个WebSocket连接时消息接收完全阻塞 日志显示消息处理线程启动后无法接收新消息 [2024-12-12 20:31:29.455] [error] [setThreadAffinity] Error calling pthread_setaffinity_np: 22 [2024-12-12 20:31:29.697] [info] Message thread started for endpoint: OkxPublic // 之后无消息接收日志 1.2 多endpoint模式部分正常 # 多个WebSocket连接时只有一个线程能正常接收消息 日志显示消息处理情况： [20:54:50.542] [thread 91374] Processing message for OkxPublic [20:54:50.640] [thread 91374] Processing message for OkxPublic // 只有一个线程在持续处理消息 二、系统架构分析 #2.1 WebSocket消息接收机制 #void WebSocketClient::receiveMessages(const MessageHandler&amp;amp; handler) { while (true) { try { // 1. 阻塞式接收WebSocket消息 int n = ws_-&amp;gt;receiveFrame(buffer.data(), buffer.size(), flags); // 2. 同步回调处理消息 if (n &amp;gt; 0) { handler(buffer.</description></item><item><title>高频交易系统中的大吞吐量订单发送机制</title><link>https://code-agree.github.io/blog/2025-06-24-order_sending_optimization/</link><pubDate>Thu, 12 Dec 2024 02:17:32 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-order_sending_optimization/</guid><description>1. 需求背景 #在高频交易系统中，我们面临一个典型场景：需要同时处理三个关联订单（三角套利）。这些订单必须几乎同时发出以确保套利的有效性。
关键挑战：
订单必须同时或几乎同时发出 系统需要处理高并发的订单组 需要保证订单处理的稳定性和可靠性 2. 当前使用的两种处理订单的机制 # 无锁队列机制 订单生成后进入一个无锁队列 多个线程从队列中取订单进行处理 订单的发送通过RestClient进行，RestClient负责管理HTTP连接池并发送请求 分片机制 订单生成后根据某种规则分配到不同的分片 每个分片由固定的线程处理 同一组的订单被分配到同一个分片，确保组内订单的处理一致性 RestClient同样负责订单的发送 class OrderShard { private: struct OrderGroup { uint64_t groupId; uint64_t timestamp; std::vector&amp;lt;Order&amp;gt; orders; }; std::queue&amp;lt;OrderGroup&amp;gt; orderQueue_; std::mutex mutex_; std::condition_variable cv_; RestClient restClient_; public: void addOrderGroup(OrderGroup group) { { std::lock_guard&amp;lt;std::mutex&amp;gt; lock(mutex_); orderQueue_.push(std::move(group)); } cv_.notify_one(); } void processOrders() { while (running_) { OrderGroup group; { std::unique_lock&amp;lt;std::mutex&amp;gt; lock(mutex_); cv_.wait(lock, [this] { return !orderQueue_.empty() || !</description></item><item><title>高性能订单执行系统设计方案1</title><link>https://code-agree.github.io/blog/2025-06-24-batch_order_processing/</link><pubDate>Fri, 06 Dec 2024 17:45:16 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-batch_order_processing/</guid><description>1. 背景问题 #1.1 性能挑战 # 高吞吐量订单处理需求 每个订单都需要 HTTP 请求 JWT Token 生成开销大 网络延迟敏感 1.2 主要痛点 # 单个订单发送造成网络请求过多 JWT Token 频繁生成浪费资源 大量订单并发可能导致系统瓶颈 2. 解决方案 #2.1 JWT Token 缓存机制 #class RestClient { private: static constexpr auto JWT_REFRESH_INTERVAL = std::chrono::seconds(110); // 预留刷新窗口 std::string getOrCreateJWT(const std::string&amp;amp; uri) { auto now = std::chrono::steady_clock::now(); if (!cache.token.empty() &amp;amp;&amp;amp; now &amp;lt; cache.expiryTime) { return cache.token; } cache.token = generateJWT(uri); cache.expiryTime = now + JWT_REFRESH_INTERVAL; return cache.token; } }; 优点：</description></item><item><title>高性能网络编程：io_uring 与内存优化技术详解</title><link>https://code-agree.github.io/blog/2025-06-24-io_uring_basics/</link><pubDate>Fri, 06 Dec 2024 06:04:25 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-io_uring_basics/</guid><description>0. 内存管理优化 #0.1 大页内存 (Huge Pages) #大页内存是一种内存管理优化技术，主要优势：
减少 TLB (Translation Lookaside Buffer) 缺失 减少页表项数量 提高内存访问效率 系统配置和检查：
# 检查系统大页配置 cat /proc/meminfo | grep Huge # 配置大页 echo 20 &amp;gt; /proc/sys/vm/nr_hugepages # 分配20个大页 0.2 内存锁定 (Memory Locking) #防止内存被交换到磁盘，确保数据始终在物理内存中：
# 检查内存锁定限制 ulimit -l # 修改限制（需要root权限） echo &amp;#34;* soft memlock unlimited&amp;#34; &amp;gt;&amp;gt; /etc/security/limits.conf 0.3 内存优化实现 #struct IOBuffer { char* data; size_t size; explicit IOBuffer(size_t s) : size(s) { // 1. 尝试使用大页内存 data = static_cast&amp;lt;char*&amp;gt;(mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0)); if (data == MAP_FAILED) { // 2.</description></item><item><title>高频交易场景下的多WS连接低延时方案设计</title><link>https://code-agree.github.io/blog/2025-06-24-multi_quote_data_processing/</link><pubDate>Tue, 03 Dec 2024 01:01:26 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-multi_quote_data_processing/</guid><description>1. 业务背景与挑战 #在高频交易系统中，需要同时维护多个WebSocket连接以订阅不同交易所的行情数据。主要挑战包括：
需要处理多个交易所的并发连接 对消息处理延迟有严格要求 需要保证数据处理的稳定性 系统资源（CPU、内存）的高效利用 2. 传统方案的局限 #2.1 传统消息队列方案 #// 常见的消息处理流程 WebSocket接收 -&amp;gt; 消息队列 -&amp;gt; 处理线程池 -&amp;gt; 业务处理 存在的问题：
消息经过队列带来额外延迟 线程切换开销大 内存拷贝次数多 资源竞争导致性能不稳定 3. 优化方案设计 #3.1 核心设计理念 # 零拷贝数据处理 CPU亲和性绑定 预分配内存 每个连接独立处理 3.2 关键组件设计 #struct ConnectionContext { // 连接基础信息 std::shared_ptr&amp;lt;WebSocketClient&amp;gt; client; std::string endpoint_name; // 性能优化相关 int cpu_core{-1}; // CPU核心绑定 char* direct_buffer{nullptr}; // 预分配缓冲区 static constexpr size_t BUFFER_SIZE = 64 * 1024; std::shared_ptr&amp;lt;MessageProcessor&amp;gt; dedicated_processor; // 资源管理 ~ConnectionContext() { if (direct_buffer) { munlock(direct_buffer, BUFFER_SIZE); munmap(direct_buffer, BUFFER_SIZE); } } // 禁用拷贝以保证资源安全 ConnectionContext(const ConnectionContext&amp;amp;) = delete; ConnectionContext&amp;amp; operator=(const ConnectionContext&amp;amp;) = delete; }; 3.</description></item><item><title>付鹏HSBC</title><link>https://code-agree.github.io/blog/2025-06-24-fupeng_trading_system/</link><pubDate>Sun, 01 Dec 2024 21:06:34 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-fupeng_trading_system/</guid><description>HSBC 速记 汇丰私人财富规划
玺越世家 · 臻享沙龙 上海站
（速记稿）
时间：2024 年 11 月 24 日
地点：上海浦东文华东方酒店 LG1 层东方厅
主持人：女士们，先生们，各位尊敬的来宾，我是陈佳昊（音），我是汇丰私人财富规划上海分区总经理，我代表上海汇丰私人财富规划欢迎各位的莅临。
今天有很多新朋友，也有很多老朋友，我在周五的时候问过后台同事报名报了多少了，他告诉我们已经快要接近 200 人了，但从今天的规模来看，我感觉好像今天的人数还要再超过一些。
当然了，有一些是原先的老客户，也有很多是慕名而来，看到这次邀请的是付鹏先生，所以慕名而来。也有一些新朋友。在付鹏先生上台之前，请允许我对汇丰私人财富规划做简短的介绍。
汇丰私人财富规划是全球的战略重点之一，老朋友都知道，汇丰私人财富规划成立于 2020 年，距今刚好四年，在四年的过程中集团一直在给我们大力注资，也是集团里最重要的项目之一。
为什么聚焦在中国市场上？大家很多人都明白，中国中产阶级的人数在世界上占有量是最庞大的，随着中国经济的高速发展，中国人财富管理的需求逐步提升到很高的水准。所以，私人财富规划也会变成汇丰的重要战略之一。
介绍一下发展历史，从 2020 年汇丰私人财富规划成立，先是在上海和广州，总部离这里不远，汇丰总部就在国金，欢迎大家去坐一坐。逐步进入到杭州、深圳、北京、佛山，今年在苏州、成都开立了分支机构。
2020 年汇丰私人财富规划才刚刚成立，那汇丰的历史又是怎么样的？汇丰简称叫 HSBC，很多人会问 HSBC 四个字母分别代表着什么，可以跟大家简单介绍一下，H 代表的是香港的意思，S 代表的是上海的意思。很多人印象中以为汇丰是一家外资银行，但其实大家有所不知，其实汇丰在清朝的时候就在外滩已经设立了总部，现在这栋楼交给了浦发银行。1949 年之后，汇丰因为历史的原因退出了中国，在 WTO 之后回到了中国。
汇丰 1865 年成立至今已经有 100 多年了，那时候还是清朝的同治年间，同时已经在全球的 62 个国家还有 3900 多名客户，这段历史和这么大的分布也是汇丰很多同事内心的骄傲。我们跟很多客户做沟通的时候，经常会把这段历史拿出来跟大家讲一讲，就像这头石狮子，很多人都见过，但很多人都不知道它的历史，很多人在海报、广告、港元大钞上看过这个石狮子，原来在外滩上也有两座，现在放在上海博物馆里，前一阵儿我在博物馆参观的时候还看到了这两只石狮子，上面还有很多历史的痕迹，比如说战争而留下的弹孔，就在人民广场的博物馆里，大家有兴趣的话可以去看一下。
财富大矩阵与中国内地市场，汇丰集团对于中国私人财富规划业务的重视程度，在大矩阵中承担了很重要的地位。
每 100 位客户中，会有 87 位客户将汇丰私人财富规划视作为提供财富重要的主要品牌，提出了很多好评，82% 的调研者打出 9-10 分的高分。
也有一些比较有意思的话，如：“对产品内容的保障满意，公司大有保障；甄汇生活有一定的吸引力，汇丰的产品较贵但也愿意买，因为对汇丰私人财富规划师的认可。”
这两年提出一句比较新的 Slogan“懂你关心的，给你安心的”。
今天的活动我们邀请到了一位重量级嘉宾，他曾任职于雷曼兄弟、所罗门投资集团等全球顶尖金融机构，从事对冲基金等相关工作。他就是东北证券首席经济学家付鹏先生。让我们欢迎付鹏先生为我们带来《2024 年年终回顾和 2025 年展望——对冲风险 VS 软着陆》主题分享，有请付鹏先生！
付鹏：正值年底，虽然刚才汇丰一直强调大家不录音不录像，但大概率你挡不住。我在这儿讲话会谨慎一些，非常小心谨慎，大概率会有人透露出去，放到 YouTube 上，基本上所有见我都说付总我在 YouTube 上看过你的视频，我说那都是盗版的，靠盗版发财的也不少。</description></item><item><title>OrderBook 本地维护方案设计</title><link>https://code-agree.github.io/blog/2025-06-24-orderbook_implementation/</link><pubDate>Wed, 27 Nov 2024 02:35:19 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-orderbook_implementation/</guid><description>OrderBook 本地维护方案设计 #一、业务背景 #OrderBook（订单簿）是反映市场深度和流动性的核心数据结构，其维护质量直接影响：
策略交易决策的准确性 风险控制的有效性 市场定价的及时性 1.1 业务价值 # 价格发现
实时反映市场供需状态 提供多层次价格信息 展示市场深度分布 交易决策支持
最优价格确定（NBBO） 流动性评估 交易成本估算 风险管理
市场异常监控 流动性风险评估 价格波动追踪 二、技术方案 #2.1 核心数据结构 #class LockFreeOrderBook { private: // 基础信息 std::string symbol_; // 状态管理 std::atomic&amp;lt;uint64_t&amp;gt; last_update_time_{0}; std::atomic&amp;lt;uint64_t&amp;gt; last_sequence_{0}; std::atomic&amp;lt;bool&amp;gt; initialized_{false}; // 价格档位存储 using BidMap = tbb::concurrent_ordered_map&amp;lt;double, PriceLevel, std::greater&amp;lt;&amp;gt;&amp;gt;; using AskMap = tbb::concurrent_ordered_map&amp;lt;double, PriceLevel, std::less&amp;lt;&amp;gt;&amp;gt;; BidMap bids_; // 买盘 - 降序（最高价优先） AskMap asks_; // 卖盘 - 升序（最低价优先） }; // 价格档位结构 struct PriceLevel { double price; double quantity; uint64_t update_time; }; // 深度数据结构 struct DepthData { std::vector&amp;lt;PriceLevel&amp;gt; bids; std::vector&amp;lt;PriceLevel&amp;gt; asks; uint64_t sequence_num; uint64_t timestamp; }; 2.</description></item><item><title>内存映射（mmap）与零拷贝技术：深入理解和实践</title><link>https://code-agree.github.io/blog/2025-06-24-zero_copy_optimization/</link><pubDate>Tue, 22 Oct 2024 01:23:46 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-zero_copy_optimization/</guid><description>1. 概述 #内存映射（mmap）是一种将文件或设备映射到内存的方法，而零拷贝是一种减少或避免数据在内核空间和用户空间之间不必要复制的技术。这两个概念密切相关，但又有所不同。
2. mmap 是零拷贝吗？ #答案是：mmap 本身不是零拷贝技术，但它可以实现零拷贝的效果。
2.1 mmap 的工作原理 # 当调用 mmap 时，操作系统会在虚拟内存中创建一个新的内存区域。 这个内存区域会映射到文件系统缓存（page cache）中的物理页面。 当程序访问这个内存区域时，如果相应的页面不在内存中，会触发缺页中断，操作系统会从磁盘加载数据到内存。 2.2 为什么 mmap 可以实现零拷贝 # 一旦映射建立，用户进程可以直接读写这个内存区域，而无需在用户空间和内核空间之间进行数据复制。 对于读操作，数据从磁盘读入 page cache 后，可以直接被用户进程访问，无需额外复制。 对于写操作，修改直接发生在 page cache 上，操作系统会在适当的时候将修改同步到磁盘。 3. mmap 与传统 I/O 的比较 #3.1 传统 read 系统调用 #char buffer[4096]; ssize_t bytes_read = read(fd, buffer, sizeof(buffer)); 这个过程涉及两次数据拷贝：
从磁盘到内核缓冲区 从内核缓冲区到用户空间缓冲区 3.2 使用 mmap #void* addr = mmap(NULL, file_size, PROT_READ, MAP_PRIVATE, fd, 0); // 直接访问 addr 指向的内存 mmap 减少了一次数据拷贝。其原理是将用户空间的虚拟地址映射到页缓存（page cache）的物理页上，数据仍然先从磁盘加载到页缓存，但用户进程直接访问的就是页缓存中的同一份数据，无需再从内核缓冲区拷贝到用户缓冲区。</description></item><item><title>GitHub私有仓库协同开发指南</title><link>https://code-agree.github.io/blog/2025-06-24-project_management_best_practices/</link><pubDate>Wed, 16 Oct 2024 02:04:51 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-project_management_best_practices/</guid><description>目录 # 简介 仓库结构和分支策略 协作者权限管理 保护主分支 Pull Request 和代码审查流程 持续集成与部署 (CI/CD) 文档和沟通 最佳实践和注意事项 简介 #在没有高级 GitHub 功能的私有仓库中进行协同开发可能具有挑战性，但通过正确的实践和工具，我们可以建立一个高效、安全的开发环境。本指南总结了我们讨论的主要策略和技术。
仓库结构和分支策略 # 主分支：main（稳定、可部署的代码） 开发分支：main_for_dev（日常开发工作） 特性分支：从 main_for_dev 分出，用于开发新功能 工作流程：
从 main_for_dev 创建特性分支 在特性分支上开发 完成后，创建 Pull Request 到 main_for_dev 代码审查和测试 合并到 main_for_dev 定期将 main_for_dev 合并到 main 协作者权限管理 #GitHub 私有仓库提供以下权限级别：
Read Triage Write Maintain Admin 设置步骤：
进入仓库 &amp;ldquo;Settings&amp;rdquo; &amp;gt; &amp;ldquo;Collaborators and teams&amp;rdquo; 点击 &amp;ldquo;Add people&amp;rdquo; 或 &amp;ldquo;Add teams&amp;rdquo; 输入用户名并选择适当的权限级别 最佳实践：
遵循最小权限原则 定期审查和更新权限 保护主分支 #由于缺乏高级分支保护功能，我们采用以下策略：
团队约定：
禁止直接推送到 main 分支 所有更改通过 PR 进行 Git Hooks： 创建 pre-push hook（.</description></item><item><title>Fork机制详解：从基础到高级应用</title><link>https://code-agree.github.io/blog/2025-06-24-fork_system_call_analysis/</link><pubDate>Tue, 15 Oct 2024 02:45:13 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-fork_system_call_analysis/</guid><description>1. 引言 #Fork是Unix/Linux系统中最基本也是最强大的系统调用之一。它允许一个进程创建一个新的进程,这个新进程是原进程的一个几乎完全相同的副本。本次技术分享将深入探讨fork机制,从基本概念到高级应用。
2. Fork的基本原理 #2.1 什么是Fork #Fork是一个系统调用,用于创建一个新的进程。新进程（称为子进程）是调用进程（称为父进程）的一个几乎完全相同的副本。
2.2 Fork的工作原理 #当一个进程调用fork时:
系统会创建一个新的进程。 新进程与父进程共享只读的代码段(text segment),数据段、堆和栈则通过写时复制(COW)获得独立副本。 子进程获得父进程数据段、堆和栈的副本(写时复制),代码段由于是只读的,始终与父进程共享同一物理页面。 父进程和子进程继续执行fork调用之后的代码。 2.3 Fork的返回值 #Fork调用会返回两次:
在父进程中,返回子进程的PID。 在子进程中,返回0。 这允许程序区分父进程和子进程。
pid_t pid = fork(); if (pid &amp;gt; 0) { printf(&amp;#34;父进程\n&amp;#34;); } else if (pid == 0) { printf(&amp;#34;子进程\n&amp;#34;); } else { perror(&amp;#34;fork失败&amp;#34;); exit(1); } 3. Fork的高级特性 #3.1 写时复制 (Copy-on-Write) #为了提高效率,现代操作系统使用&amp;quot;写时复制&amp;quot;技术:
初始时,子进程与父进程共享同一物理内存。 只有当其中一个进程尝试修改内存时,才会创建该部分内存的副本。 这大大减少了fork的开销和内存使用。
3.2 文件描述符的继承 #子进程继承父进程的文件描述符。这意味着:
子进程可以访问父进程打开的文件。 父子进程共享文件偏移量。 int fd = open(&amp;#34;example.txt&amp;#34;, O_RDWR); if (fork() == 0) { // 子进程 write(fd, &amp;#34;Hello from child&amp;#34;, 16); } else { // 父进程 write(fd, &amp;#34;Hello from parent&amp;#34;, 17); } 3.</description></item><item><title>高频交易系统中的位域压缩技术</title><link>https://code-agree.github.io/blog/2025-06-24-bit_field_compression_techniques/</link><pubDate>Sun, 13 Oct 2024 03:18:35 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-bit_field_compression_techniques/</guid><description>1. 基础概念 #1.1 二进制表示 # 计算机使用二进制（0和1）存储和处理数据 1 byte = 8 bits 32位整数可以表示从 0 到 2^32 - 1 的数值 1.2 位操作基础 # 与操作 (&amp;amp;): 两位都为1时结果为1，否则为0 或操作 (|): 至少一位为1时结果为1，否则为0 异或操作 (^): 两位不同时结果为1，相同时为0 非操作 (~): 将每一位取反 左移 (&amp;laquo;): 将所有位向左移动，右侧补0 右移 (&amp;raquo;): 将所有位向右移动，左侧补0或符号位 示例：
unsigned int a = 5; // 0101 unsigned int b = 3; // 0011 unsigned int and_result = a &amp;amp; b; // 0001 (1) unsigned int or_result = a | b; // 0111 (7) unsigned int xor_result = a ^ b; // 0110 (6) unsigned int not_result = ~a; // 11111111111111111111111111111010 (4294967290) unsigned int left_shift = a &amp;lt;&amp;lt; 1; // 1010 (10) unsigned int right_shift = a &amp;gt;&amp;gt; 1;// 0010 (2) 2.</description></item><item><title>高频交易系统中的市场数据存储优化</title><link>https://code-agree.github.io/blog/2025-06-24-efficient_reading_techniques/</link><pubDate>Sun, 29 Sep 2024 01:36:04 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-efficient_reading_techniques/</guid><description>1. 背景介绍 #在高频交易系统中，市场数据的快速读取和处理是关键性能指标之一。我们的系统使用共享内存来存储和访问实时市场数据，其中 MarketDataStore 类负责管理这些数据。本文将讨论如何优化 MarketDataStore 中的 readLatestData 函数，以提高数据读取的效率。
2. 初始实现 #最初的 readLatestData 函数实现如下：
std::optional&amp;lt;MappedTickerData&amp;gt; MarketDataStore::readLatestData(const std::string&amp;amp; symbol) const { std::shared_lock&amp;lt;std::shared_mutex&amp;gt; lock(mutex); size_t offset = calculateOffset(symbol); MappedTickerData data; if (dataFile-&amp;gt;read(&amp;amp;data, offset, sizeof(MappedTickerData))) { if (data.timestamp != 0 &amp;amp;&amp;amp; std::string(data.product_id) == symbol) { return data; } else { LOG_WARN(&amp;#34;readLatestData symbol = {} failed&amp;#34;, symbol); return std::nullopt; } } else { LOG_ERROR(&amp;#34;Failed to read data for symbol = {}&amp;#34;, symbol); return std::nullopt; } } 这个实现存在几个性能瓶颈：</description></item><item><title>高频交易系统中的重连机制最佳实践</title><link>https://code-agree.github.io/blog/2025-06-24-atomic_operations_reconnection_mechanism/</link><pubDate>Fri, 27 Sep 2024 01:35:21 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-atomic_operations_reconnection_mechanism/</guid><description>高频交易系统中的重连机制最佳实践 #背景 #在高频交易系统中，网络连接的稳定性至关重要。然而，由于网络波动或其他原因，连接可能会中断。为了确保系统的连续性和可靠性，需要实现一个高效的重连机制。然而，频繁的重连检查和处理可能导致重复重连，影响系统性能。
问题描述 #在现有实现中，主循环频繁检查 m_client-&amp;gt;needsReconnection()，如果需要重连，则调用 handleReconnect()。然而，由于主循环速度很快，可能在 resetReconnectionFlag() 生效前再次检查 needsReconnection()，导致重复调用 handleReconnect()。
解决方案 #通过使用原子操作和双重检查机制，确保重连过程的原子性和一致性，避免重复重连。
1. 定义连接状态管理 #使用原子变量来管理连接状态，确保线程安全。
class WebSocketClient { private: std::atomic&amp;lt;bool&amp;gt; isReconnecting{false}; std::atomic&amp;lt;bool&amp;gt; needsReconnection{false}; public: bool needsReconnection() const { return needsReconnection.load(std::memory_order_acquire); } bool tryInitiateReconnection() { bool expected = false; return isReconnecting.compare_exchange_strong(expected, true, std::memory_order_acq_rel); } void setNeedsReconnection(bool value) { needsReconnection.store(value, std::memory_order_release); } void resetReconnectionFlag() { needsReconnection.store(false, std::memory_order_release); isReconnecting.store(false, std::memory_order_release); } }; 2. 修改主循环 #在主循环中使用双重检查机制，确保重连过程的原子性。
void StrategyAndTrading::run() { initializeConnection(); marketDataReader-&amp;gt;start(); positionManager-&amp;gt;updatePositionsThread(); m_commonLib-&amp;gt;getConfigManager().configWatcher(); while (running_) { if (m_client-&amp;gt;needsReconnection() &amp;amp;&amp;amp; m_client-&amp;gt;tryInitiateReconnection()) { handleReconnect(); } // 执行其他高频交易逻辑 std::this_thread::sleep_for(std::chrono::microseconds(100)); // 微秒级的睡眠 } } 3.</description></item><item><title>高频交易系统优化：从数据读取到系统平衡的思考过程</title><link>https://code-agree.github.io/blog/2025-06-24-datareader_design_patterns/</link><pubDate>Wed, 25 Sep 2024 01:04:59 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-datareader_design_patterns/</guid><description>1. 初始问题：数据读取效率 #最初，我们关注的是市场数据读取器本身的效率问题。
1.1 轮询方式（初始状态） #void MarketDataReader::readingLoop() { while (running) { for (const auto&amp;amp; symbol : symbols_) { processSymbol(symbol); } std::this_thread::sleep_for(std::chrono::milliseconds(100)); } } 问题：持续轮询即使在没有新数据时也会消耗资源。
1.2 条件控制方式 #void MarketDataReader::readingLoop() { while (running) { std::unique_lock&amp;lt;std::mutex&amp;gt; lock(conditionMutex); dataCondition.wait(lock, [this] { return !running || !symbols_.empty(); }); for (const auto&amp;amp; symbol : symbols_) { processSymbol(symbol); } } } 改进：减少了不必要的CPU使用，但可能会在高频数据更新时引入延迟。
思考转变：这个阶段，我们主要关注如何提高单个组件（数据读取器）的效率。
2. 扩展考虑：数据读取对其他系统组件的影响 #随着对系统的深入思考，我们开始考虑数据读取器的行为如何影响整个系统，特别是订单流的执行效率。
2.1 资源竞争问题 #观察：尽管我们优化了数据读取器的效率，但数据读取线程占据太多的计算资源，也会进而影响订单处理的性能。即使在没有新数据可读时，频繁的检查也会占用宝贵的计算资源。
思考：
数据读取和订单处理是否在竞争同样的系统资源（CPU、内存、I/O）？ 如何在保证数据及时性的同时，不影响订单处理的响应速度？ 如何协调各个线程，使系统达到最低的时延？ 2.2 自适应间隔机制 #引入动态调整处理间隔的机制，以平衡数据读取和系统资源使用。
void MarketDataReader::readingLoop() { while (running) { auto start = std::chrono::steady_clock::now(); for (const auto&amp;amp; symbol : symbols_) { processSymbol(symbol); } auto end = std::chrono::steady_clock::now(); auto duration = std::chrono::duration_cast&amp;lt;std::chrono::microseconds&amp;gt;(end - start); if (duration &amp;lt; currentInterval) { std::this_thread::sleep_for(currentInterval - duration); } adjustInterval(); } } 思考转变：从单纯的效率优化转向了资源使用的平衡，考虑到了系统的整体性能。</description></item><item><title>实现高性能低延迟的交易系统设计</title><link>https://code-agree.github.io/blog/2025-06-24-high_performance_computing_principles/</link><pubDate>Fri, 20 Sep 2024 22:32:08 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-high_performance_computing_principles/</guid><description>高性能低延迟交易系统设计：技术分享 update #在高频交易和实时金融系统中，性能和延迟是关键因素。本文将分享一些设计和实现高性能低延迟交易系统的关键技术和策略。
1. 数据结构优化 #1.1 内存映射（Memory-Mapped）文件 #使用内存映射文件可以显著提高I/O性能，减少系统调用，并允许快速的进程间通信。
class MmapOrderBook { // 使用内存映射文件存储订单簿数据 }; 1.2 自定义内存池 #实现自定义内存池可以减少内存分配和释放的开销，提高内存使用效率。
template&amp;lt;typename T, size_t MaxSize&amp;gt; class MemoryPool { // 实现高效的内存分配和回收 }; 2. 并发控制 #2.1 细粒度锁 #使用细粒度锁可以减少锁竞争，提高并发性能。
std::array&amp;lt;std::shared_mutex, MAX_POSITIONS&amp;gt; m_positionMutexes; 2.2 无锁数据结构 #在关键路径上使用无锁数据结构可以进一步减少同步开销。
std::atomic&amp;lt;double&amp;gt; quantity; std::atomic&amp;lt;double&amp;gt; averagePrice; 3. 高效的更新策略 #3.1 增量更新 vs 全量更新 #根据具体场景选择合适的更新策略。增量更新适合频繁的小幅度变化，全量更新适合大幅度变化或定期同步。
void updatePosition(const char* instId, AssetType type, PositionSide side, double quantityDelta, double price); void syncPositionWithExchange(const char* instId, AssetType type, PositionSide side, double quantity, double price); 3.</description></item><item><title>高频交易系统中的高层锁定：必要性与实现</title><link>https://code-agree.github.io/blog/2025-06-24-mutex_performance_analysis/</link><pubDate>Wed, 18 Sep 2024 17:29:59 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-mutex_performance_analysis/</guid><description>在高频交易系统的开发中，我们经常面临着性能和正确性之间的权衡。最近，我们在优化订单处理流程时，发现了一个有趣的问题：是否需要在高层组件中实现锁定？本文将深入探讨这个问题，分析其必要性，并展示优化前后的实现。
背景 我们的系统主要由以下组件构成：
MmapOrderBook：核心数据存储，使用内存映射文件实现 PositionManager：负责仓位管理 OrderValidator：负责订单验证 OrderManager：负责订单处理流程 最初，我们的实现如下：
// OrderManager.cpp bool OrderManager::processOrder(const MmapOrderBook::Order&amp;amp; order) { if (!orderValidator_-&amp;gt;validateOrder(order)) { return false; } if (orderBook_-&amp;gt;addOrder(order)) { auto position = positionManager_-&amp;gt;getPosition(order.accountId, /* instrumentId */); if (position) { position-&amp;gt;quantity += order.isBuy ? order.quantity : -order.quantity; positionManager_-&amp;gt;updatePosition(*position); } // 发布订单已处理事件 return true; } return false; } 问题分析 虽然 MmapOrderBook 内部使用了分片锁来保证单个操作的线程安全，但我们发现这种方法在处理复合操作时可能存在问题。主要原因如下：
a) 复合操作的原子性： processOrder 方法包含多个相关操作（验证、添加、更新仓位），这些操作需要作为一个原子单元执行。
b) 避免竞态条件： 在验证订单和添加订单之间，系统状态可能发生变化，导致基于过时信息做出决策。
c) 保持不变量： 某些业务逻辑依赖于多个相关数据的一致状态，需要在整个操作过程中维护这些不变量。
d) 简化并发模型： 高层锁定可以简化并发模型，使代码更易于理解和维护。
e) 防止死锁： 复杂操作中可能需要获取多个低层锁，增加死锁风险。高层锁可以降低这种风险。</description></item><item><title>行情链路上的队列取舍与 WebSocket 接收优化</title><link>https://code-agree.github.io/blog/2025-06-24-advanced_queue_usage_patterns/</link><pubDate>Sun, 15 Sep 2024 04:03:51 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-advanced_queue_usage_patterns/</guid><description>行情链路上的队列取舍与 WebSocket 接收优化 #在高频交易(HFT)系统中,行情从交易所 WebSocket 到策略引擎的链路上,每一微秒的延迟都可能转化为实际的经济损失。本文讨论这条链路上的两个关键决策:WebSocket 消息接收机制怎么设计,以及行情数据要不要经过队列。
1. WebSocket消息接收机制优化 #在高频交易系统中,每一毫秒的延迟都可能导致巨大的经济损失。因此,优化WebSocket消息的接收机制对于系统的整体性能至关重要。
1.1 WebSocketClient类设计与实现 #以下是一个高效的WebSocketClient类的实现示例:
class WebSocketClient { public: using MessageHandler = std::function&amp;lt;void(const char*, size_t)&amp;gt;; WebSocketClient(/* 构造函数参数 */) : ws_(nullptr), running_(false) {} void receiveMessages(MessageHandler handler) { if (!ws_) { throw std::runtime_error(&amp;#34;WebSocket is not connected&amp;#34;); } constexpr size_t BUFFER_SIZE = 1024 * 1024; // 1MB buffer std::array&amp;lt;char, BUFFER_SIZE&amp;gt; buffer; int flags; while (running_) { try { int n = ws_-&amp;gt;receiveFrame(buffer.data(), buffer.size(), flags); if (n &amp;gt; 0) { handler(buffer.</description></item><item><title>故障复盘报告：内存映射文件中的 std::string 导致的段错误</title><link>https://code-agree.github.io/blog/2025-06-24-string_memory_mapping_techniques/</link><pubDate>Thu, 12 Sep 2024 15:23:23 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-string_memory_mapping_techniques/</guid><description>故障复盘报告：内存映射文件中的 std::string 导致的段错误 #1. 问题描述 #在使用内存映射文件存储订单数据的过程中，程序在重启后出现段错误。具体表现为在尝试访问存储在内存映射文件中的 Order 结构体的 id 字段时，程序崩溃。
2. 错误信息 #程序崩溃时的 GDB 调试信息如下：
Thread 2 &amp;#34;strategyandtrad&amp;#34; received signal SIGSEGV, Segmentation fault. [Switching to Thread 0x7ffff6f4c6c0 (LWP 446582)] __memcmp_sse2 () at ../sysdeps/x86_64/multiarch/memcmp-sse2.S:258 258 ../sysdeps/x86_64/multiarch/memcmp-sse2.S: No such file or directory. (gdb) bt #0 __memcmp_sse2 () at ../sysdeps/x86_64/multiarch/memcmp-sse2.S:258 #1 0x000055555556d79b in std::char_traits&amp;lt;char&amp;gt;::compare (__s1=0x7f4710000eb0 &amp;lt;error: Cannot access memory at address 0x7f4710000eb0&amp;gt;, __s2=0x7fffe8000c80 &amp;#34;ORD-1726124231791862593&amp;#34;, __n=23) at /usr/include/c++/12/bits/char_traits.h:385 #2 0x000055555559c599 in std::operator==&amp;lt;char&amp;gt; (__lhs=&amp;lt;error: Cannot access memory at address 0x7f4710000eb0&amp;gt;, __rhs=&amp;#34;ORD-1726124231791862593&amp;#34;) at /usr/include/c++/12/bits/basic_string.</description></item><item><title>高频交易系统配置管理方案分析</title><link>https://code-agree.github.io/blog/2025-06-24-config_management_in_hft_systems/</link><pubDate>Fri, 06 Sep 2024 01:47:52 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-config_management_in_hft_systems/</guid><description>高频交易系统配置管理方案分析 #当前方案概述 # graph TB CommonLib[&amp;#34;Common Library (MMAP)&amp;#34;] Exchange[&amp;#34;Exchange&amp;#34;] subgraph StrategyAndTrading[&amp;#34;StrategyAndTrading Component&amp;#34;] MDR[&amp;#34;MarketDataReader&amp;#34;] MDN[&amp;#34;MarketDataNormalizer&amp;#34;] SM[&amp;#34;StrategyManager&amp;#34;] subgraph Strategies[&amp;#34;Strategies&amp;#34;] S1[&amp;#34;Strategy 1&amp;#34;] S2[&amp;#34;Strategy 2&amp;#34;] SN[&amp;#34;Strategy N&amp;#34;] end OG[&amp;#34;OrderGenerator&amp;#34;] OV[&amp;#34;OrderValidator&amp;#34;] RP[&amp;#34;RiskProfiler&amp;#34;] RE[&amp;#34;RiskEvaluator&amp;#34;] OM[&amp;#34;OrderManager&amp;#34;] OE[&amp;#34;OrderExecutor&amp;#34;] OMO[&amp;#34;OrderMonitor&amp;#34;] PM[&amp;#34;PositionManager&amp;#34;] end CommonLib --&amp;gt;|1. Read MMAP| MDR MDR --&amp;gt;|2. Raw Market Data| MDN MDN --&amp;gt;|3. Normalized Data| SM SM --&amp;gt;|4. Distribute Data| Strategies Strategies --&amp;gt;|5. Generate Signals| OG OG --&amp;gt;|6. Create Orders| OV OV --&amp;gt;|7. Validated Orders| RP RP --&amp;gt;|8.</description></item><item><title>How to publish new blog</title><link>https://code-agree.github.io/blog/2025-06-24-how_to_publish_new_blog/</link><pubDate>Mon, 02 Sep 2024 12:36:27 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-how_to_publish_new_blog/</guid><description>workflow #目前已经实现GitHub Action，自动编译静态文件, Push到GitHub Page。
具体流程 # 在仓库 git@github.com:code-agree/MyBlogWebsiteRepo.git MyBlogWebsiteRepo/WebsiteRepo 使用 hugo命令 hugo new content ./content/blog/How_to_publish_new_blog.md 新增blog 将当前仓库的变更push到远端 由配置的GitHub action 自动触发 构建静态文件-&amp;gt;push到GitHub Page仓库 成功发布</description></item><item><title>C++ const 用法详解</title><link>https://code-agree.github.io/blog/two/</link><pubDate>Sun, 04 Aug 2024 00:13:28 +0800</pubDate><guid>https://code-agree.github.io/blog/two/</guid><description>Const #const 可以用来修饰变量、函数、指针等。
修饰变量 当修饰变量时，意味着该变量为只读变量，即不能被修改。
例如
const int a = 10; a = 20; //编译报错，a为只读，不可修改 但是可以通过一些指针类型转换操作const_cast ，修改这个变量。
例如
int main(){ const int a = 10; const int* p = &amp;amp;a; // p是指向const int类型的对象 int* q = const_cast&amp;lt;int*&amp;gt;(p); // 类型转换，将p转换成指向int型对象的指针 *q = 20; // 通过指针操作修改 const a的值 std::cout &amp;lt;&amp;lt; a &amp;lt;&amp;lt; std::ends; // 输出结果 仍然是10 return 0; } 输出结果不变，归功于编译器醉做了优化，编译时把代码替换为了如下所示。
std::cout &amp;lt;&amp;lt; &amp;quot;a = &amp;quot; &amp;lt;&amp;lt; 10 &amp;lt;&amp;lt; std::endl;
修饰函数参数，表示函数不会修改参数 void func(const int a) { // 编译错误，不能修改 a 的值 a = 10; } 修饰函数返回值 当修饰函数返回值时，表示函数的返回值为只读，不能被修改。好处是可以使函数的返回值更加安全，不会被误修改。</description></item><item><title>two First Post</title><link>https://code-agree.github.io/blog/2025-06-24-getting_started_guide/</link><pubDate>Sat, 03 Aug 2024 00:13:28 +0800</pubDate><guid>https://code-agree.github.io/blog/2025-06-24-getting_started_guide/</guid><description>This is my first blog post
int main(){ B b; return 0; } Badge # 新文章！ 短页码 # 警告！ 这个操作是破坏性的！ 别忘了在Twitter上关注我。 Button #button 输出一个样式化的按钮组件，用于突出显示主要操作。它有三个可选参数：
参数	描述 href	按钮应链接到的 URL。 target	链接的目标。 download	浏览器是否应下载资源而不是导航到 URL。此参数的值将是下载文件的名称。 示例:
Call to action 差分数组的主要适用场景是频繁对原始数组的某个区间的元素进行增减
比如说，我给你输入一个数组 nums，然后又要求给区间 nums[2..6] 全部加 1，再给 nums[3..9] 全部减 3，再给 nums[0..4] 全部加 2，再给&amp;hellip;
差分数组
diff[i] = nums[i] - nums[i - 1]; 构造差分数组
vector&amp;lt;int&amp;gt;diff(nums.size()); diff[0] = nums[0]; for (int i = 1; i &amp;lt; nums.</description></item><item><title>My First Post</title><link>https://code-agree.github.io/blog/firstpost/</link><pubDate>Sat, 03 Aug 2024 00:11:28 +0800</pubDate><guid>https://code-agree.github.io/blog/firstpost/</guid><description>这是我的第一篇blog，希望能分享更多的技术，生活、兴趣在这个Blog上。欢迎大家查看评论。
Welcome to my inaugural blog post! I&amp;rsquo;m excited to share more about technology, life experiences, and personal interests through this platform. Feel free to check out the comments section and join the conversation!</description></item></channel></rss>