<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Performance on Yu's Space</title><link>https://code-agree.github.io/tags/performance/</link><description>Recent content in Performance 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/performance/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>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>并发原语与内存序深度剖析：从 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>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>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>高性能网络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>深度解析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>深入理解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>高频交易系统中的背压机制设计讨论</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>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>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>行情数据解析优化最佳实践</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>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-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-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></channel></rss>