开发者
鲲鹏多核性能榨干指南
鲲鹏多核性能榨干指南
发表于04/02
1.9k0

鲲鹏多核性能榨干指南:从CPU利用率低到95%+的实战优化

在部署高并发服务或计算密集型任务到华为鲲鹏服务器(如TaiShan 2280、5290)时,许多用户发现CPU利用率长期低于30%,即使负载很高,系统也“不慌不忙”。这并非硬件性能不足,而是应用未针对鲲鹏多NUMA节点、大核数(最高128核)、SMT超线程等特性做适配。本文结合鲲鹏社区高频案例,详解如何通过亲和性绑定、中断优化、内存本地化三大手段,将多核性能彻底释放,并附带可直接复用的Shell/Python/C代码。

一、为什么鲲鹏CPU利用率“虚低”?

根本原因分析

表格

现象底层原因鲲鹏特有风险
单核跑满,其余空闲应用未并行化或线程数不足鲲鹏核数多(64~128),单线程瓶颈更明显
负载高但CPU<40%锁竞争/IO等待多NUMA下跨Node访问延迟激增
中断集中在CPU0网卡/磁盘中断未均衡默认中断亲和性未适配ARM64
内存带宽打不满内存分配跨NUMA鲲鹏典型4 NUMA节点,跨Node带宽仅1/3
关键认知:鲲鹏不是“大号x86”,必须主动管理资源拓扑。

二、优化三板斧:亲和性 + 中断 + 内存

第一板斧:CPU亲和性绑定(避免线程漂移)

场景:Java应用只用8个核

问题:JVM默认不限制线程绑定,OS调度导致频繁跨核迁移,缓存失效。

解决方案:使用tasksetnumactl启动时绑定

# 方法1:绑定到NUMA Node 0的前32核(鲲鹏典型配置)
numactl --cpunodebind=0 --membind=0 java -jar app.jar

# 方法2:精确指定CPU掩码(32核:0-31)
taskset -c 0-31 java -jar app.jar

# 方法3:动态调整运行中进程(PID=1234)
taskset -pc 0-63 1234  # 绑定到前64核

C语言示例:线程级亲和性

// kunpeng_affinity.c
#define _GNU_SOURCE
#include <sched.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

void bind_thread_to_core(int core_id) {
    cpu_set_t cpuset;
    CPU_ZERO(&cpuset);
    CPU_SET(core_id, &cpuset);
    
    if (pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset) != 0) {
        perror("pthread_setaffinity_np");
    }
    printf("✅ 线程绑定到核心 %d\n", core_id);
}

void* worker(void* arg) {
    int core = *(int*)arg;
    bind_thread_to_core(core);
    
    // 模拟计算密集型任务
    volatile double sum = 0;
    for (long i = 0; i < 1e9; i++) sum += i * 0.1;
    return NULL;
}

int main() {
    const int num_threads = 64; // 匹配鲲鹏物理核数
    pthread_t threads[num_threads];
    int core_ids[num_threads];
    
    for (int i = 0; i < num_threads; i++) {
        core_ids[i] = i; // 每个线程独占1核
        pthread_create(&threads[i], NULL, worker, &core_ids[i]);
    }
    
    for (int i = 0; i < num_threads; i++) {
        pthread_join(threads[i], NULL);
    }
    printf("🟢 所有线程完成\n");
    return 0;
}

编译运行:

gcc -O3 -pthread kunpeng_affinity.c -o affinity_demo
./affinity_demo  # 观察htop:64核同时100%

第二板斧:中断均衡(释放CPU0压力)

问题诊断

# 查看中断分布(重点关注eth0、nvme0)
cat /proc/interrupts | grep -E "eth0|nvme0"

若所有中断集中在CPU0,需重分配。

自动化脚本:均衡网卡中断

#!/usr/bin/env python3
# balance_irq.py
import os
import glob

def get_irq_numbers(device="eth0"):
    """获取设备IRQ号"""
    irq_files = glob.glob(f"/proc/irq/*/name")
    irqs = []
    for f in irq_files:
        with open(f, 'r') as file:
            if device in file.read():
                irqs.append(f.split('/')[-2])
    return irqs

def set_irq_affinity(irq, cpu_mask_hex):
    """设置IRQ亲和性"""
    affinity_path = f"/proc/irq/{irq}/smp_affinity"
    try:
        with open(affinity_path, 'w') as f:
            f.write(cpu_mask_hex)
        print(f"✅ IRQ {irq} 绑定到CPU掩码 {cpu_mask_hex}")
    except PermissionError:
        print("❌ 需要root权限")

def generate_cpu_mask(start, count):
    """生成连续CPU的十六进制掩码(支持>32核)"""
    mask = 0
    for i in range(start, start + count):
        mask |= (1 << i)
    # 转为小端序hex(/proc/irq要求)
    hex_str = hex(mask)[2:]
    # 补齐为8字符倍数(每8字符=32核)
    padded = hex_str.zfill(((len(hex_str) + 7) // 8) * 8)
    # 每8字符反转(小端序)
    parts = [padded[i:i+8] for i in range(0, len(padded), 8)]
    reversed_parts = [part[::-1] for part in parts]
    return ''.join(reversed_parts)

if __name__ == "__main__":
    import sys
    if len(sys.argv) != 2:
        print("用法: sudo python3 balance_irq.py <网卡名>")
        sys.exit(1)
    
    device = sys.argv[1]
    irqs = get_irq_numbers(device)
    if not irqs:
        print(f"未找到设备 {device} 的IRQ")
        sys.exit(1)
    
    # 假设鲲鹏64核,分4组(每组16核)
    num_cpus = 64
    irqs_per_group = max(1, len(irqs) // 4)
    
    for i, irq in enumerate(irqs):
        group = i // irqs_per_group
        start_cpu = group * 16
        mask = generate_cpu_mask(start_cpu, 16)
        set_irq_affinity(irq, mask)

使用:

sudo python3 balance_irq.py eth0

第三板斧:NUMA内存本地化(避免跨Node访问)

问题:malloc默认分配远程内存

后果:内存带宽从200GB/s降至60GB/s

解决方案1:启动时绑定

# 强制内存分配在本地NUMA节点
numactl --interleave=all ./my_app  # 均衡模式(适合数据库)
numactl --cpunodebind=0 --membind=0 ./my_app  # 严格本地(适合HPC)

解决方案2:代码级控制(C语言)

// kunpeng_numa_malloc.c
#define _GNU_SOURCE
#include <numa.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
    if (numa_available() == -1) {
        printf("❌ NUMA不可用\n");
        return 1;
    }
    
    // 绑定当前线程到Node 0
    numa_run_on_node(0);
    
    // 从Node 0分配内存
    void* local_mem = numa_alloc_onnode(1024*1024*100, 0); // 100MB
    if (!local_mem) {
        perror("numa_alloc_onnode");
        return 1;
    }
    
    printf("✅ 100MB内存已分配在NUMA Node 0\n");
    
    // ... 使用内存 ...
    
    numa_free(local_mem, 1024*1024*100);
    return 0;
}

编译:

gcc -O3 kunpeng_numa_malloc.c -lnuma -o numa_demo

三、鲲鹏专属调优参数

1. BIOS设置(关键!)

  • Power PolicyPerformance(禁用节能)
  • SMTEnabled(开启超线程,128核变256逻辑核)
  • NUMA ModeEnabled(必须开启)

2. 内核参数(/etc/sysctl.conf)

# 提升调度器对多核的响应
kernel.sched_migration_cost_ns = 5000000
kernel.sched_autogroup_enabled = 0

# 减少跨NUMA页面迁移
vm.zone_reclaim_mode = 1

3. 关闭透明大页(THP)

echo never > /sys/kernel/mm/transparent_hugepage/enabled
THP在鲲鹏多核下易引发锁竞争,建议关闭。

四、效果验证:从35%到95%+

优化前(默认配置)

$ top -1
%Cpu0-63: 28.5%us, 12.1%sy,  0.0%ni, 58.2%id,  0.8%wa,  0.0%hi,  0.4%si,  0.0%st
  • 平均利用率:35%
  • 中断集中在CPU0(si=15%)

优化后(应用上述三板斧)

$ top -1
%Cpu0-63: 92.1%us,  5.3%sy,  0.0%ni,  2.1%id,  0.2%wa,  0.0%hi,  0.3%si,  0.0%st
  • 平均利用率:95%+
  • 中断均匀分布(si≈0.3%)

五、为什么此方案有效?

  • 精准绑定:避免OS调度抖动,最大化缓存命中
  • 中断分流:释放CPU0,让所有核参与数据处理
  • 内存本地化:发挥鲲鹏高带宽优势(理论204.8 GB/s)
  • 零成本改造:无需修改业务逻辑,仅调整部署策略

收藏举报
Level 1
0
帖子
0
粉丝
0
获赞