Red Hat Training

A Red Hat training course is available for RHEL 8

第 28 章 配置操作系统以优化 CPU 使用率

这部分论述了如何配置操作系统以优化其工作负载中的 CPU 利用率。

28.1. 监控和诊断处理器问题的工具

以下是 Red Hat Enterprise Linux 8 中用来监控和诊断处理器相关性能问题的工具:

  • turbostat 工具以指定间隔打印计数器结果,以帮助管理员发现服务器中的意外行为,如过度使用电源、未能进入深度睡眠状态或不必要的系统管理中断(SMI)。
  • numactl 实用程序提供了多个选项来管理处理器和内存关联。numactl 软件包包括 libnuma 库,它为内核支持的 NUMA 策略提供了一个简单的编程接口,并可用于比 numactl 应用程序进行更精细的调整。
  • numastat 工具显示操作系统及其进程的每 NUMA 节点内存统计信息,并显示管理员进程内存是分散到系统中还是集中到特定的节点上。这个工具由 numactl 软件包提供。
  • numad 是自动 NUMA 关联性管理守护进程。它监控系统中 NUMA 拓扑和资源使用情况,以便动态改进 NUMA 资源的分配和管理。
  • /proc/interrupts 文件显示中断请求(IRQ)编号、系统中各个处理器处理的类似中断请求数、发送中断的类型,以及响应所列中断请求的设备列表。
  • pqos 工具程序包括在 intel-cmt-cat 软件包中。它监控最近 Intel 处理器上的 CPU 缓存和内存带宽。它监控:

    • 每个周期(IPC)的说明。
    • 最后一个级别缓存 MISSES 的数量。
    • 程序在 LLC 中给定 CPU 占用空间中执行的 KB 大小。
    • 到本地内存的带宽(MBL)。
    • 到远程内存的带宽(MBR)。
  • x86_energy_perf_policy 管理员可利用工具定义性能和能源效率的相对重要性。当处理器选择在性能和能源效率之间取舍的选择时,可以利用这些信息来影响支持此功能的处理器。
  • taskset 工具由 util-linux 软件包提供。它允许管理员检索和设置正在运行的进程的处理器关联,或者启动具有指定处理器相关性的进程。

其它资源

  • turbostat(8)numactl(8)numastat(8)numa(7)numad(8)pqos(8)x86_energy_perf_policy(8)taskset(1) man page

28.2. 系统拓扑类型

在现代计算中,CPU 的概念令人困惑,因为大多数现代系统都有多个处理器。系统拓扑是这些处理器互相连接和其他系统资源的方式。这会影响系统和应用程序的性能,以及系统的调优注意事项。

以下是现代计算中使用的两种主要拓扑类型:

Symmetric Multi-Processor (SMP) topology
SMP 拓扑允许所有处理器在相同的时间里访问内存。但是,由于共享和相等内存访问本质上会强制从所有 CPU 进行序列化内存访问,因此 SMP 系统扩展限制现在通常被视为不可接受。因此,实际上,所有现代服务器系统都是 NUMA 计算机。
Non-Uniform Memory Access (NUMA) topology

NUMA 拓扑的开发时间比 SMP 拓扑更近。在 NUMA 系统中,一个套接字上对多个处理器进行物理分组。每个套接字都有一个专用的内存和处理器区域,其对该内存具有本地访问权限,它们统称为节点。位于同一节点上的处理器能够快速访问该节点的内存数据库,而且对不在节点的内存库的访问速度也较慢。

因此,访问非本地内存时性能会受到影响。因此,具有 NUMA 拓扑的系统上对性能敏感的应用应该访问与执行应用的处理器位于同一节点上的内存,并尽可能避免访问远程内存。

对性能敏感的多线程应用或许能够配置为在特定 NUMA 节点上执行,而非特定的处理器。这是否适合取决于您的系统以及应用程序的要求。如果多个应用程序线程访问相同的缓存数据,则可能适合将这些线程配置为在同一处理器上执行。但是,如果访问和缓存同一处理器中执行的不同数据的多个线程可能会驱除以前的线程访问的缓存数据。这意味着每个线程"缺少"缓存并浪费执行时间从内存中获取数据并将其替换缓存中。使用 perf 工具检查太多缓存未命中。

28.2.1. 显示系统拓扑

许多命令可帮助理解系统的拓扑。这个步骤描述了如何确定系统拓扑。

流程

  • 显示您的系统拓扑概述:

    $ numactl --hardware
    available: 4 nodes (0-3)
    node 0 cpus: 0 4 8 12 16 20 24 28 32 36
    node 0 size: 65415 MB
    node 0 free: 43971 MB
    [...]
  • 要收集 CPU 架构的信息,如 CPU、线程、内核、插槽和 NUMA 节点的数量:

    $ lscpu
    Architecture:          x86_64
    CPU op-mode(s):        32-bit, 64-bit
    Byte Order:            Little Endian
    CPU(s):                40
    On-line CPU(s) list:   0-39
    Thread(s) per core:    1
    Core(s) per socket:    10
    Socket(s):             4
    NUMA node(s):          4
    Vendor ID:             GenuineIntel
    CPU family:            6
    Model:                 47
    Model name:            Intel(R) Xeon(R) CPU E7- 4870  @ 2.40GHz
    Stepping:              2
    CPU MHz:               2394.204
    BogoMIPS:              4787.85
    Virtualization:        VT-x
    L1d cache:             32K
    L1i cache:             32K
    L2 cache:              256K
    L3 cache:              30720K
    NUMA node0 CPU(s):     0,4,8,12,16,20,24,28,32,36
    NUMA node1 CPU(s):     2,6,10,14,18,22,26,30,34,38
    NUMA node2 CPU(s):     1,5,9,13,17,21,25,29,33,37
    NUMA node3 CPU(s):     3,7,11,15,19,23,27,31,35,39
  • 查看您系统的图形表示:

    # yum install hwloc-gui
    # lstopo

    图 28.1. lstopo 输出

    lstopo
  • 查看详细的文本输出:

    # yum install hwloc
    # lstopo-no-graphics
    Machine (15GB)
      Package L#0 + L3 L#0 (8192KB)
        L2 L#0 (256KB) + L1d L#0 (32KB) + L1i L#0 (32KB) + Core L#0
            PU L#0 (P#0)
            PU L#1 (P#4)
           HostBridge L#0
        PCI 8086:5917
            GPU L#0 "renderD128"
            GPU L#1 "controlD64"
            GPU L#2 "card0"
        PCIBridge
            PCI 8086:24fd
              Net L#3 "wlp61s0"
        PCIBridge
            PCI 8086:f1a6
        PCI 8086:15d7
            Net L#4 "enp0s31f6"

其它资源

  • numactl(8)lscpu(1)lstopo(1) man page

28.3. 配置内核绑定时间

默认情况下,Red Hat Enterprise Linux 8 使用无空循环内核,它不会中断空闲 CPU 以降低功耗并允许新处理器利用深层次的睡眠状态。

Red Hat Enterprise Linux 8 还提供了动态无循环选项,对于对延迟敏感的工作负载(如高性能计算或实时计算)非常有用。默认情况下,动态无循环选项被禁用。红帽建议使用 cpu-partitioning Tuned 配置集为指定为 isolated_cores 的内核启用动态无循环选项。

这个步骤描述了如何手动启用动态无循环行为。

流程

  1. 要在特定内核中启用动态无循环行为,请使用 nohz_full 参数在内核命令行中指定这些内核。在 16 个内核系统中,将此参数附加到 /etc/default/grub 文件中的 GRUB_CMDLINE_LINUX 选项中:

    nohz_full=1-15

    这启用了内核 115 的动态无引号行为,将所有计时操作移至唯一未指定的内核(核心 0)。

  2. 要永久启用动态无循环行为,请使用编辑后的默认 文件重新生成 GRUB2 配置。在带有 BIOS 固件的系统中,执行以下命令:

    # grub2-mkconfig -o /etc/grub2.cfg

    在带有 UEFI 固件的系统中,执行以下命令:

    # grub2-mkconfig -o /etc/grub2-efi.cfg
  3. 当系统引导时,手动将 rcu 线程移到非延迟的内核中,在本例中为 core 0

    # for i in `pgrep rcu[^c]` ; do taskset -pc 0 $i ; done
  4. 可选: 使用内核命令行中的 isolcpus 参数将某些内核与用户空间任务隔离。
  5. 可选:将内核的 write-back bdi-flush 线程的 CPU 关联性设置为 housekeeping 内核:

    echo 1 > /sys/bus/workqueue/devices/writeback/cpumask

验证步骤

  • 系统重启后,验证 dynticks 是否启用:

    # journalctl -xe | grep dynticks
    Mar 15 18:34:54 rhel-server kernel: NO_HZ: Full dynticks CPUs: 1-15.
  • 验证动态无循环配置是否正常工作:

    # perf stat -C 1 -e irq_vectors:local_timer_entry taskset -c 1 sleep 3

    该命令测量 CPU 1 上的粘滞,同时指示 CPU 1 休眠 3 秒。

  • 默认内核计时器配置会在常规 CPU 上显示大约 3100 号:

    # perf stat -C 0 -e irq_vectors:local_timer_entry taskset -c 0 sleep 3
    
     Performance counter stats for 'CPU(s) 0':
    
                 3,107      irq_vectors:local_timer_entry
    
           3.001342790 seconds time elapsed
  • 配置动态无空循环内核后,您应该会看到大约 4 ticks:

    # perf stat -C 1 -e irq_vectors:local_timer_entry taskset -c 1 sleep 3
    
     Performance counter stats for 'CPU(s) 1':
    
                     4      irq_vectors:local_timer_entry
    
           3.001544078 seconds time elapsed

28.4. 中断请求概述

中断请求或 IRQ 是从硬件部分向处理器发送立即关注的信号。系统中的每个设备都会被分配一个或多个 IRQ 号,以允许它发送唯一中断。启用中断时,接收中断请求的处理器会立即暂停当前应用线程的执行,以解决中断请求。

由于中断停止正常操作,高中断率可能会严重降低系统性能。可以通过配置中断关联或在批处理中发送多个较低优先级中断(联合多个中断)来减少中断所花费的时间。

中断请求具有关联的关联性属性 smp_affinity,它定义处理中断请求的处理器。若要提高应用性能,可将中断关联和进程相关性分配到同一处理器或同一核心上的处理器。这允许指定的中断和应用程序线程共享缓存行。

在支持中断的系统中,修改中断请求的 smp_affinity 属性会设置硬件,以便在没有内核干预的情况下在硬件级别决定使用特定处理器提供中断。

28.4.1. 手动平衡中断

如果您的 BIOS 导出其 NUMA 拓扑,irqbalance 服务可自动在节点中为本地硬件请求服务提供中断请求。

流程

  1. 检查哪些设备与您要配置的中断请求对应。
  2. 查找您的平台的硬件规格。检查系统上的芯片组是否支持分发中断。

    1. 如果存在,您可以按照以下步骤中所述配置中断交付。此外,检查您的芯片组用于平衡中断的算法。某些 BIOS 拥有配置中断发送的选项。
    2. 如果没有,您的芯片组总是将所有中断路由到单个静态 CPU。您无法配置使用哪个 CPU。
  3. 检查您的系统上使用哪个 Advanced Programmable Interrupt Controller(APIC)模式:

    $ journalctl --dmesg | grep APIC

    在这里,

    • 如果您的系统使用 flat 以外的模式,您可以看到与 Setting APIC routing to physical flat 类似的行。
    • 如果没有看到这样的信息,您的系统将使用 flat 模式。

      如果您的系统使用 x2apic 模式,您可以通过在 bootloader 配置的内核命令行中添加 nox2apic 选项来禁用它。

      只有非物理扁平模式(flat)支持将中断分发到多个 CPU。这个模式只适用于有最多 8 CPU 的系统。

  4. 计算 smp_affinity mask。有关如何计算 smp_affinity mask 的更多信息,请参阅设置 smp_affinity 掩码

其它资源

  • journalctl(1)taskset(1) man page

28.4.2. 设置 smp_affinity 掩码

smp_affinity 值保存为代表系统中所有处理器的十六进制位掩码。每个位配置一个不同的 CPU。最不重要的位是 CPU 0。

掩码的默认值是 f,这意味着中断请求可以在系统中的任何处理器中处理。将此值设置为 1 表示只有处理器 0 可以处理中断。

流程

  1. 在二进制中,将值 1 用于处理中断的 CPU。例如:要设置 CPU 0 和 CPU 7 来处理中断,使用 0000000010000001 作为二进制代码:

    表 28.1. CPU 的二进制 Bit

    CPU

    15

    14

    13

    12

    11

    10

    9

    8

    7

    6

    5

    4

    3

    2

    1

    0

    二进制

    0

    0

    0

    0

    0

    0

    0

    0

    1

    0

    0

    0

    0

    0

    0

    1

  2. 将二进制代码转换为十六进制:

    例如,使用 Python 转换二进制代码:

    >>> hex(int('0000000010000001', 2))
    
    '0x81'

    在超过 32 个处理器的系统中,您必须为离散 32 位组分隔 smp_affinity 值。例如:如果您只希望 64 处理器系统的前 32 个处理器为中断请求提供服务,请使用 0xffffffff,00000000

  3. 特定中断请求的中断关联性值存储在关联的 /proc/irq/irq_number/smp_affinity 文件中。在此文件中设置 smp_affinity 掩码:

    # echo mask > /proc/irq/irq_number/smp_affinity

其它资源

  • journalctl(1)irqbalance(1)taskset(1) man page