<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>HFT on Yu's Space</title><link>https://code-agree.github.io/tags/hft/</link><description>Recent content in HFT on Yu's Space</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Thu, 27 Aug 2026 00:10:00 +0800</lastBuildDate><atom:link href="https://code-agree.github.io/tags/hft/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>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>从网线到策略引擎：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>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>高频交易中的 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>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>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>高频交易系统中的背压机制设计讨论</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>高频交易系统中的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>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>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>共享内存多进程通信中的页面切换同步问题分析与解决</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>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>高频交易场景下的多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>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>高频交易系统中的位域压缩技术</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>Quant system</title><link>https://code-agree.github.io/projects/quant_system/</link><pubDate>Sat, 03 Aug 2024 00:00:00 +0800</pubDate><guid>https://code-agree.github.io/projects/quant_system/</guid><description>High-Frequency Trading System #Project Overview #Independently designed and developed a cutting-edge high-frequency trading system with industry-leading performance.
Key Features # Modular Architecture: Utilizing advanced C++17 and key design patterns Observer pattern for event-driven architecture Factory method for flexible algorithm creation Strategy pattern for interchangeable trading strategies Ultra-Low Latency Event Bus: Implemented using lock-free queues Optimized WebSocket: For high-throughput market data and order execution Memory-Mapped File I/O: Leveraging kernel-level page cache for asynchronous, low-latency disk operations Performance Metrics # Metric Performance Order Execution Latency &amp;lt; 50 μs Message Processing Throughput &amp;gt; 1,000 messages/second Technical Stack # C++ (C++17) WebSockets Lock-free algorithms Memory-mapped I/O SIMD optimization Event-driven architecture Specializations # Ultra-low latency systems Concurrent programming Design patterns Market microstructure Kernel-level optimizations Project Highlights # Advanced C++ Implementation: Leveraged cutting-edge C++17 features to create a robust and efficient system architecture.</description></item></channel></rss>