实际项目请根据侧边目录手动跳转至实际项目部分,本项目实践学习及来源于
Building Low Latency Applications with C++.pdf
部分基础知识来源:
https://weedge.github.io/perf-book-cn/zh/,
https://arxiv.org/abs/2309.04259
Testing and Tuning Market Trading Systems _ Algorithms in -- Timothy Masters -- 1st ed_ 2018, Berkeley, CA, 2018 -- Apress _ Imprint_ Apress -- 9781484241721 -- 60d1a98d33720c15e03ee33c4ae10155 -- Anna’s Archive.pdf
optimizing_cpp.pdf
项目代码地址:https://github.com/zzxscodes/zquant-system
低延迟系统开发基础
1. CPU亲和性及NUMA架构
isolcpus:内核启动参数CPU隔离- 编辑 /etc/default/grub 。
- 修改 GRUB_CMDLINE_LINUX_DEFAULT 这一行, 在引号内添加 isolcpus=cpu号列表 (例如: isolcpus=2,3 isolcpus=1,4-7)。
- 执行 sudo update-grub (Debian/Ubuntu) ( sudo grub2-mkconfig -o /boot/grub2/grub.cfg(RHEL/CentOS/Fedora)) 并 sudo reboot。
复制代码 taskset:命令行工具CPU绑定- 启动进程: taskset -c 1 ./my_app (在CPU 1上运行)
- 修改运行中进程:taskset -pc 3 <PID> (将PID进程移至CPU 3)
- 查询进程:taskset -pc <PID>
- 绑定进程下的线程: ps -T -p <PID> taskset -p -c <CPU列表> <TID>
复制代码 pthread_setaffinity_np(sched_setaffinity):线程(进程)CPU绑定- #pragma once
- #include <iostream>
- #include
- #include <thread>
- #include <unistd.h>
- #include <sys/syscall.h>
- namespace Common {
- /// Set affinity for current thread to be pinned to the provided core_id.
- inline auto setThreadCore(int core_id) noexcept {
- cpu_set_t cpuset;
- CPU_ZERO(&cpuset);
- CPU_SET(core_id, &cpuset);
- return (pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset) == 0);
- }
- /// Creates a thread instance, sets affinity on it, assigns it a name and
- /// passes the function to be run on that thread as well as the arguments to the function.
- template<typename T, typename... A>
- inline auto createAndStartThread(int core_id, const std::string &name, T &&func, A &&... args) noexcept {
- auto t = new std::thread([&]() {
- if (core_id >= 0 && !setThreadCore(core_id)) {
- std::cerr << "Failed to set core affinity for " << name << " " << pthread_self() << " to " << core_id << std::endl;
- exit(EXIT_FAILURE);
- }
- std::cerr << "Set core affinity for " << name << " " << pthread_self() << " to " << core_id << std::endl;
- std::forward<T>(func)((std::forward(args))...);
- });
- using namespace std::literals::chrono_literals;
- std::this_thread::sleep_for(1s);
- return t;
- }
- }
- /*
- * sched_setaffinity 和 pthread_setaffinity_np 的区别:
- *
- * 1. 作用对象:
- * - pthread_setaffinity_np: 作用线程,通过 pthread_t 句柄来指定要绑定的线程。
- * - sched_setaffinity: 作用进程。它通过进程ID (PID) 来指定要绑定的进程。
- * 一个进程被绑定,所有子线程也会被限制在这个CPU中。
- * 2. 可移植性:
- * - pthread_setaffinity_np: GNU C 库(glibc)扩展。
- * - sched_setaffinity: 标准 Linux 系统调用。
- */
复制代码 11.内存对齐与典型内存布局优化
- #!/usr/bin/env bash
- #
- # simple_cpuset_example.sh
- #
- # 演示如何使用 cpuset 将一个任务绑定到专属的 CPU 核心。
- # --- 配置 ---
- # 为后台任务分配的常规核心
- SYSTEM_CORE="0"
- # 为专属任务保留的核心
- EXCLUSIVE_CORE="1"
- set -e
- # 确保脚本以 root 权限运行
- if [[ $(id -u) -ne 0 ]]; then
- echo "此脚本必须以 root 身份运行。"
- exit 1
- fi
- # 1. 创建 cgroup 目录
- echo "--> 正在创建 cpuset 核心池..."
- mkdir -p /sys/fs/cgroup/cpuset/exclusive_tasks
- # 2. 配置独占核心池
- # 将核心 1 分配给这个池
- echo "${EXCLUSIVE_CORE}" > /sys/fs/cgroup/cpuset/exclusive_tasks/cpuset.cpus
- # 将其标记为 CPU 独占
- echo "1" > /sys/fs/cgroup/cpuset/exclusive_tasks/cpuset.cpu_exclusive
- # 将内存节点 0 分配给这个池(通常一个核心只有一个内存节点)
- echo "0" > /sys/fs/cgroup/cpuset/exclusive_tasks/cpuset.mems
- # 3. 运行一个任务并将其绑定到独占核心池
- echo "--> 正在启动一个无限循环并绑定到核心 ${EXCLUSIVE_CORE}..."
- # 启动一个后台任务
- while true; do :; done &
- TASK_PID=$!
- echo "任务 PID: ${TASK_PID}"
- # 将任务的 PID 写入独占池的 tasks 文件中
- echo "${TASK_PID}" > /sys/fs/cgroup/cpuset/exclusive_tasks/tasks
- echo "任务已成功绑定。可以使用 'top' 或 'htop' 检查 PID ${TASK_PID} 是否正在核心 ${EXCLUSIVE_CORE} 上运行。"
- echo "要停止任务,请运行:kill ${TASK_PID}"
复制代码 std::enable_if 作为模板默认参数
除了返回类型,std::enable_if也可作为模板的默认参数,语法更简洁:- lscpu -e # 假设输出显示 CPU 0 和 CPU 8 都属于 CORE 0。这意味着它们是同一个物理核心上的两个“兄弟”逻辑核心。
- # 查看 CPU 8 的在线状态 (1 代表在线)
- cat /sys/devices/system/cpu/cpu8/online
- # 将 CPU 8 设置为离线 (禁用)
- echo 0 > /sys/devices/system/cpu/cpu8/online
- # 查看在线的CPU数量,会比原来少1
- nproc
- # 或者再次运行 lscpu,会看到 CPU 8 显示为 no (离线)
- lscpu -e | grep "cpu8"
- # 此时,操作系统调度器不会再向 CPU 8 分配任何任务。CPU 0 现在可以不受干扰地使用物理核心 0 的全部资源。
- #重启只需
- echo 1 > /sys/devices/system/cpu/cpu8/online
复制代码 Concepts(C++20)显式定义模板参数的约束,替代部分std::enable_if的复杂逻辑,使代码更易读。- # 清晰地列出CPU、核心、Socket的对应关系
- lscpu -e=CPU,CORE,SOCKET
- # 绑定策略进程到物理核8-15(跳过超线程核)
- taskset -c 8-15 ./strategy_engine
复制代码 9. 分支预测提示
直接在代码中告诉编译器哪个分支更有可能执行,帮助旧微架构生成更优的指令布局。在现代处理器上使用这些分支前缀会生成有效的 x86/x64 汇编,但可能不会在现代处理器上产生性能提升。
[code]// 使用#pragma once确保头文件只被包含一次,防止重复包含#pragma once#include #include // LIKELY 宏定义,用于告诉编译器条件表达式 x 为真的可能性很大// __builtin_expect 是 GCC 和 Clang 等编译器提供的内建函数// 它接受两个参数,第一个参数是一个表达式,第二个参数是期望的值// 这里将!!(x) 作为表达式,1 作为期望的值,表示 x 为真的可能性很大// !! 是双重逻辑非运算符,用于将 x 转换为布尔值#define LIKELY(x) __builtin_expect(!!(x), 1)// UNLIKELY 宏定义,用于告诉编译器条件表达式 x 为真的可能性很小// 同样使用 __builtin_expect 内建函数,将!!(x) 作为表达式,0 作为期望的值// 表示 x 为真的可能性很小#define UNLIKELY(x) __builtin_expect(!!(x), 0)// ASSERT 函数,用于在条件不满足时输出错误信息并终止程序// 使用 UNLIKELY 宏来标记条件判断,表明条件不成立的情况是很少发生的// 如果条件不成立,会输出错误信息到标准错误输出流,并调用 exit 函数终止程序inline auto ASSERT(bool cond, const std::string &msg) noexcept { if (UNLIKELY(!cond)) { std::cerr |