Too young, too simple. Sometimes, naive & stupid

CPU配置建议

红帽企业版 Linux 提供了大量工具来协助管理员配置系统。本章概述了可用的工具并提供了使用它们在红帽企业版 Linux 7 中解决与处理器相关的性能问题的实例。

配置内核滴答记号时间

默认情况下,红帽企业版 Linux 7 使用无时钟内核,它不会中断空闲 CPU 来减少用电量,并允许较新的处理器利用深睡眠状态。

红帽企业版 Linux 7 同样提供一种动态的无时钟设置(默认禁用),这对于延迟敏感型的工作负载来说是很有帮助的,例如高性能计算或实时计算。

要启用特定内核中的动态无时钟性能,在内核命令行中用 nohz_full 参数进行设定。在 16 核的系统中,设定 nohz_full=1-15 可以在 1 到 15 内核中启用动态无时钟内核性能,并将所有的计时移动至唯一未设定的内核中(0 内核)。这种性能可以在启动时暂时启用,也可以在 /etc/default/grub 文件中永久启用。要持续此性能,请运行 grub2-mkconfig -o /boot/grub2/grub.cfg 指令来保存配置。

启用动态无时钟性能需要一些手动管理。

  • 当系统启动时,必须手动将 rcu 线程移动至对延迟不敏感的内核,这种情况下为 0 内核。

    1
    for i in `pgrep rcu` ; do taskset -pc 0 $i ; done
  • 在内核命令行上使用 isolcpus 参数来将特定的内核与用户空间任务隔离开。

  • 可以选择性地为辅助性内核设置内核回写式 bdi-flush 线程的 CPU 关联:

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

验证动态无时钟配置是否正常运行,执行以下命令,其中 stress 是在 CPU 中运行 1 秒的程序。

1
perf stat -C 1 -e irq_vectors:local_timer_entry taskset -c 1 stress -t 1 -c 1

可替代 stress 的是一个脚本,该脚本的运行类似 while :; do d=1; done 。以下链接中的程序是另一个合适的替代程序:

默认的内核计时器配置在繁忙 CPU 中显示 1000 次滴答记号:

1
2
perf stat -C 1 -e irq_vectors:local_timer_entry taskset -c 1 stress -t 1 -c 1
1000 irq_vectors:local_timer_entry

动态无时钟内核配置下,用户只会看到一次滴答记号:

1
2
perf stat -C 1 -e irq_vectors:local_timer_entry taskset -c 1 stress -t 1 -c 1
1 irq_vectors:local_timer_entry

设置硬件性能策略

x86_energy_perf_policy 工具允许管理员定义性能与能效的相对重要性。当处理器在性能与能效间权衡选择时,此信息可用来改变支持这一特征的处理器。

默认情况下适用于所有在 performance 模式下的处理器,它要求处理器的支持,由 CPUID.06H.ECX.bit3 显示,且必须在有 root 特权的情况下运行。

使用 taskset 设置处理器关联

taskset 工具由 util-linux 数据包提供。Taskset 允许管理员恢复和设置进程中的处理器关联,或通过特定的处理器关联来启动一个进程。

重要

taskset 不能保证本地的内存配置。若需要本地内存配置的额外性能收益,推荐使用 numactl 来替代 taskset

使用 numaactl 管理 NUMA 关联

管理员可以通过特定的调度或内存安置策略来使用 numactl 运行进程。Numactl 也可以为共享内存片段或文件设置永久性策略,并设置处理器关联和进程的内存关联。

在 NUMA 拓扑系统中,处理器访问内存的速度会由于处理器和存储体之间距离的增加而减慢。因此,重要的是要对性能敏感的应用程序进行配置,以便它们能够从最近的且可能的存储体分配内存。最好是使用在同一 NUMA 节点的内存和 CPU。

对性能敏感的多线程应用程序经配置后在特定的 NUMA 节点上运行会比在特定的处理器上运行好处更多。这是否适合则取决于用户系统及应用程序的需求。如果多个应用程序线程访问同一缓存数据,那么对那些线程进行配置,使其在同一处理器上运行可能是合适的。但是,如果在同一处理器上运行的多线程访问及缓存的是不同数据,那么每个线程可能会收回之前线程访问的缓存数据。这就意味着每个线程会“缺失”缓存,会浪费运行时间来从磁盘中获取数据并在缓存中替代它。可以使用 perf 工具,用它来查看大量的缓存缺失。

Numactl 提供大量的选择来协助管理处理器及内存关联。

注意

numactl 数据包包括 libnuma 库。这个库提供了一个简单的编程接口至内核支持的 NUMA 策略,比起 numactl 应用程序,它可以用来进行更细致的调节。

使用 numad 进行自动化 NUMA 关联管理

numad 是一种自动化的 NUMA 关联管理后台程序。它对系统中的 NUMA 拓扑及资源用量进行监控,以便动态地改善 NUMA 资源配置及管理。

numad 也同样提供预先安置咨询服务,这一服务可以通过不同的作业管理系统来查询,并为处理器 CPU 的初始绑定及内存资源提供帮助。无论 numad 是以可执行程序或服务在运行,这一预先安置咨询都可用。

调节调度策略

Linux 调度器执行大量的调度原则,以此决定线程运行的位置和时长。调度原则主要有两类:普通原则和实时原则。普通线程用于普通优先级任务,实时原则用于具有时效性且必须无中断完成的任务。

实时线程不受时间间隔的控制,这意味着它们将一直运行直至它们阻拦、退出、主动让步或是被更高优先权的线程预先安置。最低优先权的实时线程会先于其他普通原则的线程进行调度。

调度原则

SCHED_FIFO 静态优先级调度

SCHED_FIFO (也叫做静态优先级调度)是一项实时策略,定义了每个线程的固定优先级。这一策略让管理员能改进事件响应的时间并减少延迟,这一策略建议无法运行较长时间且具有时效性的任务使用。

在使用 SCHED_FIFO 时,调度器会按优先级顺序扫描所有的 SCHED_FIFO 线程,并对准备运行的最高优先级线程进行调度。一个 SCHED_FIFO 线程的优先级级别可以是 1 至 99 之间的任何整数,99 是最高优先级。红帽建议一开始使用较小的数字,在确定了延迟问题后再增加优先级。

警告

由于实时线程不受时间间隔的控制,红帽不推荐设置 99 优先级。这会使同优先级的进程成为迁移线程或监控线程,如果线程进入一个计算机回路且这些线程被阻拦,它们将无法运行。这种情况下,单处理器系统最终会暂停。

管理员可以限制 SCHED_FIFO 的带宽以防止实时应用程序的程序员启用独占处理器的实时任务。

  • /proc/sys/kernel/sched_rt_period_us

    该参数以微秒为单位来定义时间,是百分之百的处理器带宽。默认值为 1000000 μs, 或1秒。

  • /proc/sys/kernel/sched_rt_runtime_us

    该参数以微秒为单位来定义时间,用来运行实时线程。默认值为 950000 μs, 或0.95秒。

SCHED_RR 轮循优先级调度

SCHED_RRSCHED_FIFO 的一个轮循变形。这一策略在同优先级的多线程需要运行时很有用。

正如 SCHED_FIFOSCHED_RR 是一项实时策略,定义了每个线程的固定优先级。调度器会按优先级顺序扫描所有的 SCHED_RR 线程,并对准备运行的最高优先级线程进行调度。但是,和 SCHED_FIFO 不同,同优先级的线程在一定的时间间隔内是以循环的方式进行调度的。

用户可以使用 sched_rr_timeslice_ms 内核参数,并毫秒为单位设定这一时间间隔 (/proc/sys/kernel/sched_rr_timeslice_ms) 。最小值为1毫秒。

SCHED_OTHER 普通调度

SCHED_OTHER 是红帽企业版 Linux 7 中默认的调度策略。这一策略使用 CFS (完全公平排程器)让处理器能够平等地访问用此策略调度的所有线程。这一策略在有大量线程或数据吞吐量优先时最为有用,因为它能够随着时间而更为有效地调度线程。

在使用这一策略时,调度器会创建一个动态优先级列表,此列表一部分是基于每个进程线程的进程优先级。管理员可以改变一个进程的进程优先级,但是不能直接改变调度器的动态优先级列表。

隔离 CPU

用户可以使用 isolcpus 开机参数来从调度器隔离一个或多个 CPU,以此防止调度器在此 CPU 上调度任何用户空间的线程。

一旦 CPU 被隔离,用户须手动分配进程至被隔离的 CPU,或使用 CPU 关联系统呼叫或 numactl 命令。

将系统中第三和第六 CPU 隔离至第八 CPU,添加如下至内核命令行:

1
isolcpus=2,5-7

用户也可使用 Tuna 工具来隔离CPU。Tuna 可以随时隔离 CPU,不仅仅局限于启动时。但这种隔离方法与 isolcpus 参数略有不同,并且目前尚未实现与 isolcpus相关的性能收益。

设置中断关联

中断请求有一个相关的关联属性smp_affinity, 它能确定处理中断请求的处理器。若要提高应用程序的性能,就将中断关联和进程关联分配至同一处理器或分配至同一内核的处理器。这可以使特定的中断和应用程序线程共享高速缓存线路。

特定中断请求的中断关联值存储在相关的 /proc/irq/*irq_number*/smp_affinity 文件下。smp_affinity 是存储为十六进制的位掩码来代表系统中所有的处理器。默认值为 f,这意味着一个中断请求可以在系统中任何处理器中处理。如果将这个值设为 1 则意味着只有 0 位处理器可以处理这一中断。

在超过 32 个处理器的系统中,用户须将 smp_affinity的值限定为分散的 32 位组。例如,如果一开始只想使用 64 位处理器系统中的 32 位处理器来处理一个中断请求,可以运行:

1
echo 0xffffffff,00000000 > /proc/irq/IRQ_NUMBER/smp_affinity

此外,如果 BIOS 导出其 NUMA 拓扑,irqbalance 服务可以使用此信息来处理节点上的中断请求,对请求服务的硬件来说此节点是本地节点。

注意

如果在支持中断驱动且可以修改一个中断请求的 smp_affinity 系统中设置硬件,那么特定的处理器处理一个中断请求的决策就是硬件级别,它不会受内核的干扰。

使用 Tuna 配置 CPU、线程和中断关联

Tuna 能够控制 CPU、线程及中断关联,并能给其所能控制的每类实体提供大量操作。

要从一个或多个特定的 CPU 中移除所有线程,请运行如下命令,使用想要隔离的 CPU 数量来替换 CPUs

1
tuna --cpus CPUs --isolate

要在可运行特定线程的 CPU 列表中加入一个 CPU,请运行如下命令,使用想要加入的 CPU 数量来替换 CPUs

1
tuna --cpus CPUs --include

要将一个中断请求移动至特定的 CPU,请运行如下命令,用 CPU 数量替换 CPU,用想要移动且使用逗号分隔的中断请求列表替换 IRQs

1
tuna --irqs IRQs --cpus CPU --move

此外,用户可以使用如下命令来找到所有 sfc1* 模式的中断请求。

1
tuna -q sfc1* -c7 -m -x

要改变一个线程的策略和优先级,请运行如下命令,使用想要改变的线程替换 thread,使用需要的线程运行策略名称替换 policy,用从 0(最低优先级)至 99(最高优先级)间的一个整数替换 level

1
tuna --threads thread --priority policy:level