第 8 讲 · oeAware 与中断绑核:把手工调优自动化

掌握 oeAware 的插件加载与实例使能;理解中断绑核解决什么问题;写一个网络程序验证中断位置对性能的影响。

手工绑核的局限

第 7 讲的 numactl 绑定很有效,但有个前提:你知道绑定策略是什么,而且负载不会变。

现实里负载会变。白天是读密集,晚上是写密集;高峰期线程数翻倍,低谷期只用两个核。静态绑定在负载变化时可能反而成为负担------比如把 128 个线程都绑到只有 48 个核的节点 0 上。

oeAware 是 openEuler 提供的低开销、感知式调优框架 ,它的思路是:让系统自己感知负载特征,动态调整资源分配。

oeAware 的架构

oeAware 把调优拆成三层,用订阅机制串联,每层做成可复用的插件(一个插件 = 一个 .so):

层级 职责 代表插件
采集层 收集系统/业务运行时数据 libpmu.so(PMU 采样)、libsystem_collector.solibdocker_collector.so
感知层 分析数据、识别场景 libthread_scenario.so(线程场景)、libanalysis_oeaware.so
调优层 执行具体调优动作 libsystem_tune.solibtune_numa.solibub_tune.so

关键设计:插件之间通过 topic 订阅。采集插件发布数据,感知插件订阅后分析,调优插件再订阅感知结果。所以加载插件有依赖顺序,oeAware 会自动处理。

安装与启动

bash 复制代码
# 安装(openEuler 24.03-LTS-SP3 起默认已安装)
sudo yum install oeAware-manager

# 启动服务
sudo systemctl start oeaware

# 修改 config.yaml 后必须重启才能生效
sudo systemctl restart oeaware

插件加载目录是 /usr/lib64/oeAware-plugin/

权限 :官方明确 oeAware 当前限于 root 组操作(SDK 支持 root 和 oeaware 组)。文件权限被严格校验,不要修改 :插件文件 440、可执行文件 750、配置文件 640,改了权限会导致加载失败。

核心命令:oeawarectl

命令 作用 示例
-l/--load <插件> 加载插件 oeawarectl -l libthread_collect.so
-r/--remove <插件> 卸载插件 oeawarectl -r libthread_collect.so
-q/--query [插件] 查询插件(不带参查全部) oeawarectl -q
-Q/--query-dep [实例] 查询实例依赖关系 oeawarectl -Q
-e/--enable <实例> 使能插件实例 oeawarectl -e tune_numa_mem_access
-d/--disable <实例> 去使能 oeawarectl -d tune_numa_mem_access
--list 列出可下载的包 oeawarectl --list
--info 查询调优实例信息 oeawarectl --info
-i/--install <包名> 安装插件包 oeawarectl -i numafast
analysis 运行分析模式 oeawarectl analysis -t 10

-q-Q 的区别要分清-q 查的是已加载的插件-Q 查的是运行中的实例及其依赖关系。后者更适合排查"为什么我的插件没生效"。

加载 NUMA 调优插件

libtune_numa.so外部插件,不在默认安装的 oeAware-manager 里,需要先装包:

bash 复制代码
# 1) 安装 numafast 包(会一并带来 NUMA 相关插件)
oeawarectl -i numafast

# 2) 如未自动加载,手动加载
oeawarectl -l libtune_numa.so

# 3) 使能实例
oeawarectl -e tune_numa_mem_access

# 4) 验证依赖关系
oeawarectl -Q

四个 NUMA 相关调优实例

oeAware 提供了四个互补的 NUMA 调优实例:

实例 做什么 适用场景
tune_numa_mem_access 周期性迁移线程和内存,减少跨 NUMA 访存 负载会变化的场景
numa_sched_tune 让线程在生命周期内保持在同一 NUMA 负载稳定的场景
scenario_numa 感知跨 NUMA 访存占比(只观测,不调优) 不确定该不该介入时先看数据
hardirq_tune 把网卡队列中断绑定到使用它的业务所在的 NUMA 网络密集型服务

选择依据 :负载稳定就绑死(numa_sched_tune);负载会变就动态迁移(tune_numa_mem_access);不确定就先观测(scenario_numa)。

查看和调整实例参数

bash 复制代码
# 查看命令行形式的参数说明
oeawarectl -e tune_numa_mem_access -cmd "--help cmd"

# 查看 YAML 形式的参数(字段更多)
oeawarectl -e tune_numa_mem_access -cmd "--help yaml"

tune_numa_mem_access 的主要参数:

参数 含义 取值
-i/--sampling-interval 采样间隔(ms) 100, 100000,默认 100
-t/--sampling-times 每次优化的采样次数 1, 1000,默认 10
-m/--tune-mode 调优模式 b(page+thread) / t(仅thread) / p(仅page)
-w/--load-way 负载分布方式 b(节点间均衡) / c(集中到较少NUMA)
--smt SMT 策略 off / phy-first(默认)

-w 的取舍b(均衡)适合吞吐型负载,c(集中)适合延迟敏感型------把线程集中到少数节点能减少跨节点通信,与第 7 讲 --interleave vs --membind 的取舍是同一个道理。

中断绑核:一个容易被忽略的瓶颈

网络数据包到达时,网卡产生中断,CPU 处理中断、把数据从内核态送到用户态,业务线程才能读到。

如果处理中断的核和运行业务的线程不在同一个 NUMA 节点,数据要跨节点搬运------延迟和带宽双重损失。

hardirq_tune 解决的就是这个。相关的还有:

实例 作用
hardirq_tune 网卡队列中断绑定到使用它的业务所在 NUMA
multi_net_path 多路径网卡调优,每个中断只处理本 NUMA 的业务
disk_adapt_ic 自适应磁盘中断聚合,动态调整 NVMe 中断合并参数(依赖 nvme-cli

disk_adapt_ic 的取舍:降低中断频率能提升吞吐,但会牺牲响应延迟。聚合多少个请求产生一次中断,需要按业务特性平衡------高吞吐存储和低延迟存储的答案不一样。

写一个程序观察中断行为

先看当前的中断分布:

bash 复制代码
# 查看每个核上的中断计数(对应网卡)
cat /proc/interrupts | head -3

# 查看网卡队列与 CPU 的对应关系
ls /sys/class/net/*/queues/ 2>/dev/null | head

# 查看某个中断号的亲和性设置
cat /proc/irq/<IRQ号>/smp_affinity_list

/proc/interrupts 的输出第一行是 CPU 编号,下面每行是一个中断源。观察中断计数是否集中在少数几个核上------如果所有网络中断都落在 CPU 0,那就是明显的瓶颈。

写一个简单的网络程序来产生可观察的负载:

c 复制代码
/*
 * tcp_server.c ------ 简单的 TCP 回显服务器,用于观察网络中断分布
 *
 * 编译:gcc -O2 -o tcp_server tcp_server.c
 * 运行:./tcp_server 9999
 *
 * 另开终端压测:for i in $(seq 1 20); do (echo hello | nc 127.0.0.1 9999) & done; wait
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define BUF_SIZE 4096

int main(int argc, char *argv[])
{
    int port = (argc > 1) ? atoi(argv[1]) : 9999;

    /* 1) 创建 TCP socket */
    int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_fd < 0) { perror("socket"); return 1; }

    /* 2) 允许端口复用,避免重启时 "Address already in use" */
    int opt = 1;
    setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    /* 3) 绑定地址与端口 */
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family      = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);   /* 监听所有网卡 */
    addr.sin_port        = htons((uint16_t)port);

    if (bind(listen_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        perror("bind"); close(listen_fd); return 1;
    }

    /* 4) 开始监听,backlog 设为 128 */
    if (listen(listen_fd, 128) < 0) { perror("listen"); close(listen_fd); return 1; }

    printf("监听端口 %d...\n", port);
    fflush(stdout);

    char buf[BUF_SIZE];

    /* 5) 主循环:接受连接并回显 */
    for (;;) {
        struct sockaddr_in peer;
        socklen_t peer_len = sizeof(peer);

        int conn_fd = accept(listen_fd, (struct sockaddr *)&peer, &peer_len);
        if (conn_fd < 0) { perror("accept"); continue; }

        /* 单线程逐个处理,逻辑简单、便于观察中断行为。
         * 生产环境应该用 epoll 或线程池,这里故意简化。 */
        ssize_t n;
        while ((n = read(conn_fd, buf, sizeof(buf))) > 0) {
            /* 原样写回。write 可能短写,这里简化处理。 */
            if (write(conn_fd, buf, (size_t)n) != n) break;
        }

        close(conn_fd);
    }

    close(listen_fd);
    return 0;
}

编译并运行:

bash 复制代码
gcc -O2 -o tcp_server tcp_server.c
./tcp_server 9999 &

# 另开终端,观察中断分布
watch -n 1 'cat /proc/interrupts | grep -E "eth|ens|enp"'

再用 nccurl 制造负载,看中断计数往哪些核上增长。记录中断集中的核,然后用 oeAware 或手动设置亲和性:

bash 复制代码
# 手动把某个中断绑定到指定核(例如 IRQ 号 100,绑到 CPU 4)
echo 4 | sudo tee /proc/irq/100/smp_affinity_list

# 用 oeAware 自动做这件事
oeawarectl -e hardirq_tune

config.yaml:持久化配置

每次重启都要重新使能实例太麻烦。oeAware 通过 /etc/oeAware/config.yaml 持久化:

yaml 复制代码
log_path: /var/log/oeAware
log_level: 1                     # 1=DEBUG 2=INFO 3=WARN 4=ERROR
enable_list:                     # 默认使能的插件
   - name: libtune_numa.so       # 只写 name:使能该插件的所有实例
   - name: libsystem_tune.so
     instances:                  # 写 instances:只使能列出的实例
      - stealtask_tune
      - dynamic_smt_tune
plugin_list:                     # 可下载的包
  - name: numafast
    description: NUMA tuning package
    url: https://example.com/numafast.rpm

注意日志级别的编码不同 :oeAware 的 config.yaml1/2/3/4(1=DEBUG),而 devkitksys 用的是 0/1/2/3(0=DEBUG)。这个不一致很容易踩坑。

改完配置要 systemctl restart oeaware

analysis 模式:先看数据再调优

如果不确定该使能哪些插件,先用分析模式:

bash 复制代码
oeawarectl analysis -t 10

输出分三段:Data Analysis (数据分析)、Analysis Conclusion (结论)、Analysis Suggestion(建议)------工具会直接告诉你该使能什么。

主要参数:

参数 含义
-t/--time <s> 分析时长(秒),范围 1--100,默认 30
-r/--realtime 实时显示报告
-v/--verbose 显示详细信息
--pid 分析指定进程
--out-path 报告输出路径

完整操作序列

bash 复制代码
# ── 安装与启动 ──
sudo yum install oeAware-manager
sudo systemctl start oeaware

# ── 查看当前状态 ──
oeawarectl -q                  # 已加载的插件
oeawarectl --info              # 调优实例信息
oeawarectl -Q                  # 运行中的实例及依赖

# ── 先用分析模式看数据 ──
oeawarectl analysis -t 10

# ── 按建议加载插件 ──
oeawarectl -i numafast
oeawarectl -e tune_numa_mem_access
oeawarectl -e hardirq_tune

# ── 查看参数 ──
oeawarectl -e tune_numa_mem_access -cmd "--help cmd"

# ── 用 KSYS 验证效果 ──
./ksys collect -d 30 -o /home/test/out/ ./your_app

# ── 停止 ──
oeawarectl -d tune_numa_mem_access

一个容易忽略的冲突

⚠️ libkperf 同一时刻只允许一个进程调用。 这意味着:如果你的程序或 perf 命令正在采样 PMU,oeAware 的采集插件可能拿不到数据,反之亦然。

做性能测试时要避免同时跑 perf 和 oeAware 的 PMU 采集。这是排查"oeAware 没效果"时的常见原因。

动手练习

  1. oeawarectl analysis -t 30 分析你的机器,对比它给出的建议与你用 numastat 手工得出的结论。
  2. 运行 tcp_server 并用 nc 制造负载,记录 /proc/interrupts 里网络中断集中的核;使能 hardirq_tune 后再观察一次分布变化。
  3. 分别用 -w b-w c 使能 tune_numa_mem_access 压测同一个服务,对比吞吐与延迟的差异。
  4. 同时跑 perf stat 和 oeAware 的 PMU 采集,验证 libkperf 的独占限制会以什么形式暴露(报错或采不到数据)。

进阶与拓展

  • 与 irqbalance 的关系 :irqbalance 是多数发行版默认启用的中断均衡守护进程,目标是把中断尽量均匀摊到各 CPU;hardirq_tune 的目标相反------按业务 NUMA 亲和把网卡中断集中到对的节点。两者都会改写中断亲和性,同启可能互相覆盖,实践上建议二选一(systemctl disable --now irqbalance 后再使能 hardirq_tune;具体冲突行为因版本而异,uncertain)。
  • oeAware 插件生态 :插件以 .so 形式分发,oeawarectl --list 查可下载的包(如 numafast),-i 安装后按需加载;oeAware 另提供 SDK(支持 root 和 oeaware 组)开发自研采集/感知/调优插件,产出的 .so 放入 /usr/lib64/oeAware-plugin/ 即可被加载。
  • 中断之外的软件路径 :内核还有 RPS(软件层把收包处理分发到其他 CPU,网卡只有单队列时尤其有用)和 XPS(控制发送队列与 CPU 的对应关系),与中断绑核互补;smp_affinity_list 写 CPU 编号,姊妹文件 smp_affinity 用十六进制位掩码,脚本里别混用。
  • 深入路径/proc/interrupts 各字段含义见 man 5 proc;各调优实例参数的全量说明以官方 oeAware 用户指南为准(见参考来源)。

参考来源

相关推荐
hyunbar7771 小时前
LangChain 实战:上下文工程长期记忆管理
人工智能
lucas_AI1 小时前
第 11 讲 · KAE 硬件加速:不改一行业务代码的性能提升
人工智能
hanbon1 小时前
技术参数响应表怎么写?
人工智能·招投标·ai写标书·评分表
byte轻骑兵1 小时前
【BlueZ 】sdp 模块:服务发现协议的用户态实现基础
linux·人工智能·bluez·电脑蓝牙·嵌入式蓝牙
小白学大数据1 小时前
Python 项目实战:用 Flask 构建生产级 MySQL 增删改查 REST API
开发语言·人工智能·python·mysql·flask
YangYang9YangYan1 小时前
资金分析校招 JD 拆解,现金流建模与数据分析能力怎么准备
大数据·人工智能·数据分析
名不经传的养虾人1 小时前
从0到1:企业级AI项目迭代日记 Vol.110|协作有了预算,延迟有了归因,知识有了保真
大数据·人工智能·机器人·ai编程·企业ai
孟郎郎1 小时前
Windows 环境使用 OpenCode 配置使用大模型
人工智能·windows·ai·大模型·开源软件·opencode