3.3. 配置建議

Red Hat Enterprise Linux 提供一些工具來輔助系統管理員設定系統。這一段落將介紹有哪一些可用的工具,也會以例子說明這些工具如何被用來解決 Red Hat Enterprise Linux 7 中與處理器有關的效能問題。

3.3.1. 設定 kernel tick 的時間

預設中,Red Hat Enterprise Linux 7 使用的是 tickless kernel 。它不會透過插斷閒置的 CPU 來達到省電的效果,也會讓較新的處理器處於深沉睡眠狀態。
Red Hat Enterprise Linux 7 也提供動態 tickless 選項(預設為停用)。這個對延遲敏感的工作負載來說很實用,例如高效能運算或是及時運算。
若要在特定核心啟用動態 tickless 行為,就指定那些在 kernel 命令列上面有「nohz_full」參數的核心。如果使用 16 核心的系統,指定「 nohz_full=1-15」可以在核心 1 到 15 啟用動態 tickless 行為,並且將所有時程序移到唯一一個未被指定的核心上(core 0)。這個行為可以在開機時被暫時啟用,或是在「 /etc/default/grub」檔案持續地啟用。要持續地啟用,請執行「 grub2-mkconfig -o /boot/grub2/grub.cfg」指令來儲存設定。
要啟用動態 tickless 行為需要一些手動管理。
  • 當系統開機時,您必須手動將 rcu 執行緒移動至非延遲敏感核心。在這裡就是指 0 核心。
    # for i in `pgrep rcu[^c]` ; do taskset -pc 0 $i ; done
  • 在 kernel 命令列使用「isolcpus」參數將特定核心與使用者空間的工作隔離。
  • 選擇項,將 CPU 對 kernel 的寫回 bdi-flush 執行緒的親和性設定為核心:
    echo 1 > /sys/bus/workqueue/devices/writeback/cpumask
要確認動態 tickless 設定是否正常運作,請執行以下命令。「stress」是一個在 CPU 上空轉一秒的程式。
# perf stat -C 1 -e irq_vectors:local_timer_entry taskset -c 1 stress -t 1 -c 1
可以取代「stress」的指令碼類似「while :; do d=1; done」。另外一個可以取代「stress」的是以下連結的程式:〈https://dl.fedoraproject.org/pub/epel/6/x86_64/repoview/stress.html〉。
kernel 計時器設定預設為在忙碌的 CPU 上顯示 1000 個 tick:
# perf stat -C 1 -e irq_vectors:local_timer_entry taskset -c 1 stress -t 1 -c 1
1000 irq_vectors:local_timer_entry
設定動態 tickless kernel 後,您應該只會看見一個 tick。
# perf stat -C 1 -e irq_vectors:local_timer_entry taskset -c 1 stress -t 1 -c 1
1 irq_vectors:local_timer_entry

3.3.2. 設定硬體效能原則

x86_energy_perf_policy」工具允許系統管理員定義效能與能源效率相對的重要性。選擇 CPU 的高效能或高節能選項時,這項資訊就可以用來對支持此功能的 CPU 造成影響。
在預設情況下,此工具可以在所有處於「performance」(效能) 模式的處理器上執行。它需要處理器的支援,可透過「CPUID.06H.ECX.bit3」的狀態得知,還必須以 root 使用者權限執行。
x86_energy_perf_policy」是由「kernel-tools」套件所提供。欲了解使用「x86_energy_perf_policy」的細節請見 〈節 A.10, “x86_energy_perf_policy”〉,或是參考 man page:
$ man x86_energy_perf_policy

3.3.3. 用 taskset 設定程序親和性

taskset」工具是由「util-linux」套件所提供。「taskset」允許系統管理員取出以及設定執行中程序的處理器親和性,或是用特定的處理器親和性來啟動程序。

重要

taskset」不保證本機記憶體的配置。如果您需要其他的效能效益或是本機記憶體配置,Red Hat 建議您使用「numactl」來取代「taskset」。
欲了解更多關於「taskset」的資訊,請見〈節 A.16, “taskset”〉或是 man page:
$ man taskset

3.3.4. 用 numactl 管理 NUMA 親和性

系統管理員可以使用「numactl」來執行有特定排程或是記憶體放置原則的程序。「numactl」也可以為了共用的記憶體區段或檔案設定常設原則,以及設定程序的處理器親合性和記憶體親和性。
使用 NUMA 拓樸的系統中,當處理器跟記憶體插槽之間的距離變長,處理器的記憶體存取會變慢。因此,將應用程式設定為對效能敏感是很重要的,這樣才能將記憶體配置到最近的記憶體插槽。最好使用位於相同 NUMA 節點中的記憶體以及 CPU。
對效能敏感的多執行緒應用程式來說,與其被設定為在一個特定的處理器上執行,被設定為在特定 NUMA 節點上執行比較有效。這個設定是否合適則要視您的應用程式的系統以及需求而定。如果多重應用程式執行緒存取一樣的快取資料,那麼將這些執行緒設定為在同一個處理器上執行就很合適。但是,如果多重執行緒在同一個處理器上存取以及快取不同的資料,那麼每一個執行緒可以收回由前一個執行緒快取的資料。意思是,每一個執行緒「遺漏」(miss)快取,而且把執行時間浪費在從磁碟擷取資料上以及在快取裡取代它。您可以使用「perf」工具。就如同〈節 A.7, “perf”〉記載的一樣,此工具可以被用來檢查過多的快取遺漏。
numactl」提供許多選項來幫助您管理處理器以及記憶體親和性。欲了解細節,請見〈節 A.12, “numastat”〉或是man page:
$ man numactl

注意

numactl」套件包含「libnuma」程式庫。這個程式庫提供簡單的程式設計介面給由 kernel 支援的 NUMA 原則使用,這個介面還能被用來操作精密的微調,比「numactl」應用程式來的精密。更多資訊請見 man page:
$ man numa

3.3.5. 用 numad 自動管理 NUMA 親和性

numad」 是自動的 NUMA 親和性管理精靈。它透過監視 NUMA 拓樸以及系統內的資源使用方式來動態改善 NUMA 資源分配與管理。
numad」也提供放置前的建議服務來幫助程序的 CPU 以及記憶體的初始繫結。此服務可以透過許多工作管理系統來查詢。不管 numad 是以可執行檔或是服務來執行,這個放置前的建議服務都可使用。
欲了解如何使用「numad」,請見〈 節 A.14, “numad”〉或參考 man page:
$ man numad

3.3.6. 微調排程原則

Linux 排程器實施許多排程原則,這些原則用來決定執行緒在哪裡執行以及執行多久。排程原則有兩個主要的類別:標準原則以及及時原則。標準執行緒是用來執行一般優先順序的工作;及時原則是用來處理時間緊迫、不能插斷才能完成的工作。
及時執行緒不是在時間配量的前提之下。這個意思是及時執行緒會持續執行到它們封鎖、結束、自動讓出、或者被較高優先順序的執行緒優先占用。最低優先順序的及時執行緒會在任何應用標準原則的執行緒之前排程。

3.3.6.1. 排程原則

3.3.6.1.1. 用 SCHED_FIFO 靜態優先順序排程
SCHED_FIFO」(或者被稱為靜態優先順序排程)是一個定義每個執行緒的固定優先順序的即時原則。這個原則允許系統管理員改善回應事件的時間以及減少延遲。我們建議對時間敏感,且不長時間執行的工作可以應用此原則。
當「SCHED_FIFO」在使用中,排程器會依優先順序掃描全部「SCHED_FIFO」執行緒的清單,也會排程執行最優先順序的執行緒。「SCHED_FIFO」執行緒的優先權層級可以是介於 1 到 99 的任何一個整數(99 被視為是最高優先順序)。Red Hat 建議從較低的數字開始,當您識別延遲問題時再增加優先順序。

警告

由於及時執行緒並不是在時間配量的前提之下,Red Hat 不建議將優先順序設為 99。這會將您的程序設為與移轉以及看門狗執行緒相同的優先權層級。如果您的執行緒進入計算迴圈,而且這些執行緒被封鎖了,那麼它們就無法執行。有單一處理器的系統終究會懸置於這樣的狀況中。
系統管理員可以限制「SCHED_FIFO」頻寬來防止及時應用程式的程式設計師起始會壟斷處理器的及時工作。
/proc/sys/kernel/sched_rt_period_us
這個參數定義微秒的時間長度,它被認為是百分之百的處理序頻寬。預設值為「1000000 μs」或是一秒。
/proc/sys/kernel/sched_rt_runtime_us
這個參數定義微秒的時間長度,它專門執行及時執行緒。預設值為「950000 μs」或 0.95 秒。
3.3.6.1.2. 用 SCHED_RR 輪詢優先排程法
SCHED_RR」是「SCHED_FIFO」的變異輪詢法。當多重執行緒必須在同一個優先權層級上執行時,這個原則就很有幫助。
就像「SCHED_FIFO」,「SCHED_RR」是一個執行原則,被用來定義每一個執行緒的固定優先順序。排程器依照優先順序掃描所有「SCHED_RR」執行緒的清單,然後排程已經準備好要執行的最高優先順序的執行緒。但是,跟「SCHED_FIFO」不一樣的是,有同樣優先順序的執行緒會在某些時間配置下以輪詢法排程。
您可以用「sched_rr_timeslice_ms」kernel 參數(「/proc/sys/kernel/sched_rr_timeslice_ms」)將此時間配置的數值設定為毫秒。最低的數值是一毫秒。
3.3.6.1.3. 用 SCHED_OTHER 標準排程
SCHED_OTHER」是 Red Hat Enterprise Linux 7 的預設排程原則。這個原則使用完全公平排程(CFS)來允許處理器公平連接至所有執行緒。當大量執行緒或是資料輸送為優先時,應用這個原則是最有幫助的,因為它會隨著時間允許更有效率的執行緒排程。
當此原則被使用,排程器會建立一個動態優先順序清單。這是部分根據每個程序執行緒的精密值來建立的。系統管理員可以變更程序的精密值,但是不能直接變更排程器的動態優先順序。
欲了解變更精密程序的細節,請見「Red Hat Enterprise Linux 7 Deployment Guide」,此指南位於〈http://access.redhat.com/site/documentation/Red_Hat_Enterprise_Linux/〉。

3.3.6.2. 隔離 CPU

您可以使用「isolcpus」開機參數將一個或一個以上的 CPU 從排程器隔離。這樣可以防止排程器將任何使用者空間的執行緒排程置這個 CPU 上。
一旦 CPU 被隔離,您必須手動指派程序至被隔離的 CPU。您可以使用 CPU 親和性系統或是 numactl 指令。
要隔離系統上第三個或是第六個到第八個的 CPU,請將以下加至 kernel 命令列:
isolcpus=2,5-7
您也可以使用「Tuna」工具來隔離 CPU。「Tuna」可以在任何時間隔離 CPU,不是只有開機的時候。但是這種隔離的方法跟 isolcpus 參數有些微的不同,而且目前無法獲得與「isolcpus」相關的效能。請見〈節 3.3.8, “用 Tuna 來配置 CPU、執行緒,以及插斷親和性”〉以便了解更多關於此工具的資訊。

3.3.7. 設定插斷親和性

「插斷要求」有相關的親和性屬性。此屬性為「smp_affinity」,它定義控制插斷要求的處理器。要改善應用程式效能,請指派插斷親和性以及程序親和性至同一個處理器、或是同一個核心的多個處理器上。這允許被指定的插斷以及應用程式執行緒分享快取的樣式。
特定插斷要求的插斷親和性值儲存於相關的「/proc/irq/irq_number/smp_affinity」檔案中。「smp_affinity」是以十六進位位元遮罩來儲存,它代表系統內所有的處理器。它的預設值為「f」,意思是插斷要求可以在系統內任何處理器上被控制。將預設值設為「1」代表只有 0 號處理器可以控制插斷。
在內含超過 32 個處理器的系統中,您必須為了分開 32 位元群組分隔「smp_affinity」值。舉例來說,如果您只想要 64 處理器的系統中前 32 個處理器來服務插斷要求,您可以執行:
# echo 0xffffffff,00000000 > /proc/irq/IRQ_NUMBER/smp_affinity
或者,如果 BIOS 匯出它的 NUMA 拓樸,「irqbalance」服務可以使用此資訊來處理在本機硬體要求的服務節點上的插斷要求。欲了解「irqbalance」,請見〈節 A.1, “irqbalance”〉。

注意

在支援插斷轉向的系統中,修改插斷要求的「smp_affinity」可以建立硬碟。所以說,在沒有 kernel 的插斷之下,硬碟可以決定用哪些特定的處理器來處理插斷。欲了解更多關於插斷轉向的資訊,請見〈章 6, 網路功能〉。

3.3.8. 用 Tuna 來配置 CPU、執行緒,以及插斷親和性

Tuna」可以控制 CPU、執行緒、插斷親和性,以及提供它可以控制的每一個型別的實體許多動作。欲見「Tuna」功能的完整清單,請見〈節 A.2, “Tuna”〉。
為了要將所有執行緒從一個或多個被指定的 CPU 移走,請執行以下指令,並用您想要隔離的 CPU 編號取代「CPU」。
# tuna --cpus CPUs --isolate
要將一個 CPU 包含在一列可以執行特定執行緒的 CPU 清單,請執行以下指令,並且用您想要包含的 CPU 編號來取代「CPUs」。
# tuna --cpus CPUs --include
要將插斷要求移至一個被指定的 CPU,請執行以下指令,並且用 CPU 的編號取代「CPU」。另外,用您想要移動的插斷要求的逗點分隔清單來取代「IRQs」。
# tuna --irqs IRQs --cpus CPU --move
或者,您可以使用以下指令來找出所有「sfc1*」模式的插斷要求。
# tuna -q sfc1* -c7 -m -x
要變更原則以及一個執行緒的優先順序,請執行以下指令,用您想變更的執行緒來取代「thread」;將您想要用來執行執行緒的原則來取代「policy」;用 0(最低優先順序)到 99(最高優先順序)之間的整數來取代「level」。
# tuna --threads thread --priority policy:level