簡體   English   中英

在新的Linux內核中,上下文切換速度要慢得多

[英]Context switches much slower in new linux kernels

我們希望將服務器上的操作系統從Ubuntu 10.04 LTS升級到Ubuntu 12.04 LTS。 不幸的是,似乎運行已經變為可運行的線程的延遲從2.6內核到3.2內核顯着增加。 事實上,我們得到的延遲數字很難相信。

讓我對測試更加具體。 我們有一個運行兩個線程的程序。 第一個線程獲取當前時間(使用RDTSC以滴答為單位),然后每秒發送一次條件變量。 第二個線程等待條件變量並在發出信號時喚醒。 然后它獲取當前時間(使用RDTSC以滴答為單位)。 計算第二個線程中的時間與第一個線程中的時間之間的差異,並在控制台上顯示。 在此之后,第二個線程再次等待條件變量。 大約第二次通過后,第一個線程將再次發出信號。

因此,簡而言之,我們得到一個線程,通過條件可變延遲測量一次一次地進行線程通信

在內核2.6.32中,這種延遲大約為2.8-3.5 us,這是合理的。 在內核3.2.0中,這種延遲已經增加到大約40-100 us。 我已經排除了兩台主機之間硬件的任何差異。 它們運行在相同的硬件上(雙插槽X5687 {Westmere-EP}處理器,運行頻率為3.6 GHz,具有超線程,speedtep和所有C狀態關閉)。 測試應用程序更改線程的親和力以在同一套接字的獨立物理核心上運行它們(即,第一個線程在Core 0上運行,第二個線程在Core 1上運行),因此沒有線程的彈跳套接字之間的核心或彈跳/通信。

兩台主機之間的唯一區別是,一台運行Ubuntu 10.04 LTS,內核為2.6.32-28(快速上下文切換盒),另一台運行最新的Ubuntu 12.04 LTS,內核為3.2.0-23(緩慢的上下文)開關盒)。 所有BIOS設置和硬件都相同。

內核是否有任何變化可以解釋線程被安排運行多長時間的這種荒謬的減速?

更新:如果您想在主機和Linux版本上運行測試,我已將代碼發布到pastebin供您閱讀。 編譯:

g++ -O3 -o test_latency test_latency.cpp -lpthread

運行(假設您至少有一個雙核盒子):

./test_latency 0 1 # Thread 1 on Core 0 and Thread 2 on Core 1

更新2 :經過大量內核參數搜索,內核更改和個人研究的帖子后,我已經找出了問題所在並已發布解決方案作為這個問題的答案。

在最近的內核的壞線程喚醒性能問題的解決方案與切換到做intel_idle從CPUIDLE司機acpi_idle ,在老版本的內核使用的驅動程序。 遺憾的是, intel_idle驅動程序忽略了用戶對C狀態的BIOS配置並intel_idle 到自己的曲調 換句話說,即使您完全禁用PC(或服務器)BIOS中的所有C狀態,此驅動程序仍會在短暫不活動期間強制啟用它們,這幾乎總是發生,除非所有核心消耗合成基准(例如,壓力) ) 在跑。 您可以在大多數兼容硬件上使用精彩的Google i7z工具監控C狀態轉換以及與處理器頻率相關的其他有用信息。

要查看您的設置中當前處於活動狀態的cpuidle驅動程序,只需在/sys/devices/system/cpucpuidle部分中cpuidle current_driver文件,如下所示:

cat /sys/devices/system/cpu/cpuidle/current_driver

如果您希望現代Linux操作系統具有最低的上下文切換延遲,請添加以下內核啟動參數以禁用所有這些省電功能:

在Ubuntu 12.04上,您可以通過將它們添加到/etc/default/grubGRUB_CMDLINE_LINUX_DEFAULT條目然后運行update-grub 要添加的引導參數是:

intel_idle.max_cstate=0 processor.max_cstate=0 idle=poll

以下是三個啟動選項的詳細信息:

intel_idle.max_cstate設置為零將將您的cpuidle驅動程序恢復為acpi_idle (至少根據該選項的文檔),或者將其完全禁用。 在我的盒是完全禁止(即,在顯示current_driver在文件/sys/devices/system/cpu/cpuidle產生的輸出none )。 在這種情況下,第二個引導選項, processor.max_cstate=0是不必要的。 但是,文檔指出將intel_idle驅動程序的intel_idle設置為零應將操作系統還原為acpi_idle驅動程序。 因此,我放入第二個啟動選項以防萬一。

processor.max_cstate選項將acpi_idle驅動程序的最大C狀態設置為零,希望同樣禁用它。 我沒有可以測試它的系統,因為intel_idle.max_cstate=0完全擊敗了我可用的所有硬件上的cpuidle驅動程序。 但是,如果您的安裝確實只使用第一個引導選項將您從intel_idle恢復為acpi_idle ,請告訴我第二個選項, processor.max_cstate是否執行了注釋中記錄的操作,以便我可以更新此答案。

最后,三個參數中的最后一個, idle=poll是一個真正的耗電量。 它將禁用C1 / C1E,這將消除最后剩余的延遲時間,代價是更多的功耗,所以只有在真正需要時才使用它。 對於大多數人來說這將是過度殺傷,因為C1 *延遲並不是那么大。 使用我在原始問題中描述的硬件上運行的測試應用程序,延遲從9 us到3 us。 對於高延遲敏感的應用程序(例如,金融交易,高精度遙測/跟蹤,高頻率數據采集等等),這無疑是一個顯着的減少,但可能不值得為絕大多數桌面應用。 確切知道的唯一方法是分析應用程序的性能改進與硬件功耗/熱量的實際增長,並權衡權衡。

更新:

在使用各種idle=*參數進行額外測試后,我發現如果硬件支持將idle設置為mwait是一個更好的主意。 似乎使用MWAIT/MONITOR指令允許CPU進入C1E,而不會在線程喚醒時間中添加任何明顯的延遲。 使用idle=mwait ,您將獲得更低的CPU溫度(與idle=poll相比),更少的功耗並仍然保持輪詢空閑循環的出色低延遲。 因此,基於這些發現,針對低CPU線程喚醒延遲的更新推薦的引導參數集是:

intel_idle.max_cstate=0 processor.max_cstate=0 idle=mwait

使用idle=mwait而不是idle=poll也可以幫助啟動Turbo Boost(通過幫助CPU保持低於其TDP [熱設計功率])和超線程(MWAIT是不消耗整個功能的理想機制)物理核心,同時避免較高的C狀態)。 然而,這在測試中尚未得到證實,我將繼續這樣做。

更新2:

mwait idle選項已從較新的3.x內核中刪除 (感謝用戶ck_進行更新)。 這給我們留下了兩個選擇:

idle=halt -應工作以及mwait ,但測試,以確保這是你的硬件情況。 HLT指令幾乎等同於具有狀態提示0的MWAIT 。問題在於需要中斷才能退出HLT狀態,而內存寫入(或中斷)可用於退出MWAIT州。 根據Linux內核在其空閑循環中使用的內容,這可以使MWAIT更有效。 所以,正如我所說的測試/配置文件,看它是否滿足您的延遲需求......

idle=poll - 性能最高的選項,犧牲功率和熱量。

也許變慢的是futex,它是條件變量的構建塊。 這將有所啟發:

strace -r ./test_latency 0 1 &> test_latency_strace & sleep 8 && killall test_latency

然后

for i in futex nanosleep rt_sig;do echo $i;grep $i test_latency_strace | sort -rn;done

這將顯示有趣的系統調用所采用的微秒,按時間排序。

在內核2.6.32上

$ for i in futex nanosleep rt_sig;do echo $i;grep $i test_latency_strace | sort -rn;done
futex
 1.000140 futex(0x601ac4, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601ac0, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000129 futex(0x601ac4, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601ac0, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000124 futex(0x601ac4, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601ac0, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000119 futex(0x601ac4, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601ac0, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000106 futex(0x601ac4, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601ac0, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000103 futex(0x601ac4, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601ac0, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000102 futex(0x601ac4, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601ac0, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 0.000125 futex(0x7f98ce4c0b88, FUTEX_WAKE_PRIVATE, 2147483647) = 0
 0.000042 futex(0x601b00, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000038 futex(0x601b00, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000037 futex(0x601b00, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000030 futex(0x601b00, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000029 futex(0x601b00, FUTEX_WAKE_PRIVATE, 1) = 0
 0.000028 futex(0x601b00, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000027 futex(0x601b00, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000018 futex(0x7fff82f0ec3c, FUTEX_WAKE_PRIVATE, 1) = 0
nanosleep
 0.000027 nanosleep({1, 0}, {1, 0}) = 0
 0.000019 nanosleep({1, 0}, {1, 0}) = 0
 0.000019 nanosleep({1, 0}, {1, 0}) = 0
 0.000018 nanosleep({1, 0}, {1, 0}) = 0
 0.000018 nanosleep({1, 0}, {1, 0}) = 0
 0.000018 nanosleep({1, 0}, {1, 0}) = 0
 0.000018 nanosleep({1, 0}, 0x7fff82f0eb40) = ? ERESTART_RESTARTBLOCK (To be restarted)
 0.000017 nanosleep({1, 0}, {1, 0}) = 0
rt_sig
 0.000045 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000040 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000038 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000035 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000034 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000033 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000032 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000032 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000031 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000031 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000028 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000028 rt_sigaction(SIGRT_1, {0x37f8c052b0, [], SA_RESTORER|SA_RESTART|SA_SIGINFO, 0x37f8c0e4c0}, NULL, 8) = 0
 0.000027 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000027 rt_sigaction(SIGRTMIN, {0x37f8c05370, [], SA_RESTORER|SA_SIGINFO, 0x37f8c0e4c0}, NULL, 8) = 0
 0.000027 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000025 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000025 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000023 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000023 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000022 rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
 0.000022 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000021 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000021 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000021 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000021 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000021 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000019 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0

在內核3.1.9上

$ for i in futex nanosleep rt_sig;do echo $i;grep $i test_latency_strace | sort -rn;done
futex
 1.000129 futex(0x601764, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601760, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000126 futex(0x601764, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601760, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000122 futex(0x601764, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601760, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000115 futex(0x601764, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601760, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000114 futex(0x601764, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601760, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000112 futex(0x601764, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601760, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 1.000109 futex(0x601764, FUTEX_WAKE_OP_PRIVATE, 1, 1, 0x601760, {FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 1}) = 1
 0.000139 futex(0x3f8b8f2fb0, FUTEX_WAKE_PRIVATE, 2147483647) = 0
 0.000043 futex(0x601720, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000041 futex(0x601720, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000037 futex(0x601720, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000036 futex(0x601720, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000034 futex(0x601720, FUTEX_WAKE_PRIVATE, 1) = 1
 0.000034 futex(0x601720, FUTEX_WAKE_PRIVATE, 1) = 1
nanosleep
 0.000025 nanosleep({1, 0}, 0x7fff70091d00) = 0
 0.000022 nanosleep({1, 0}, {0, 3925413}) = ? ERESTART_RESTARTBLOCK (Interrupted by signal)
 0.000021 nanosleep({1, 0}, 0x7fff70091d00) = 0
 0.000017 nanosleep({1, 0}, 0x7fff70091d00) = 0
 0.000017 nanosleep({1, 0}, 0x7fff70091d00) = 0
 0.000017 nanosleep({1, 0}, 0x7fff70091d00) = 0
 0.000017 nanosleep({1, 0}, 0x7fff70091d00) = 0
 0.000017 nanosleep({1, 0}, 0x7fff70091d00) = 0
rt_sig
 0.000045 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000044 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000043 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000040 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000038 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000037 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000036 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000036 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000035 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000035 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000035 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000035 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000034 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000031 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000027 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000027 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000027 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000027 rt_sigaction(SIGRT_1, {0x3f892067b0, [], SA_RESTORER|SA_RESTART|SA_SIGINFO, 0x3f8920f500}, NULL, 8) = 0
 0.000026 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000026 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000025 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000024 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000023 rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
 0.000023 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
 0.000022 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 0.000021 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
 0.000019 rt_sigaction(SIGRTMIN, {0x3f89206720, [], SA_RESTORER|SA_SIGINFO, 0x3f8920f500}, NULL, 8) = 0

我發現這個5歲的錯誤報告包含一個比較的“乒乓”性能測試

  1. 單線程libpthread互斥鎖
  2. libpthread條件變量
  3. 普通的舊Unix信號

我不得不補充一下

#include <stdint.h>

為了編譯,我用這個命令做了

g++ -O3 -o condvar-perf condvar-perf.cpp -lpthread -lrt

在內核2.6.32上

$ ./condvar-perf 1000000
NPTL
mutex                 elapsed:    29085 us; per iteration:   29 ns / 9.4e-05 context switches.
c.v. ping-pong test   elapsed:  4771993 us; per iteration: 4771 ns / 4.03 context switches.
signal ping-pong test elapsed:  8685423 us; per iteration: 8685 ns / 4.05 context switches.

在內核3.1.9上

$ ./condvar-perf 1000000
NPTL
mutex                 elapsed:    26811 us; per iteration:   26 ns / 8e-06 context switches.
c.v. ping-pong test   elapsed: 10930794 us; per iteration: 10930 ns / 4.01 context switches.
signal ping-pong test elapsed: 10949670 us; per iteration: 10949 ns / 4.01 context switches.

我得出結論,在內核2.6.32和3.1.9之間,上下文切換確實已經放慢了速度,盡管沒有你在內核3.2中觀察到的那么多。 我意識到這還沒有回答你的問題,我會繼續挖掘。

編輯:我發現更改進程的實時優先級(兩個線程)可以提高3.1.9的性能以匹配2.6.32。 但是,在2.6.32上設置相同的優先級會讓它變慢...去圖 - 我會更多地研究它。

這是我現在的結果:

在內核2.6.32上

$ ./condvar-perf 1000000
NPTL
mutex                 elapsed:    29629 us; per iteration:   29 ns / 0.000418 context switches.
c.v. ping-pong test   elapsed:  6225637 us; per iteration: 6225 ns / 4.1 context switches.
signal ping-pong test elapsed:  5602248 us; per iteration: 5602 ns / 4.09 context switches.
$ chrt -f 1 ./condvar-perf 1000000
NPTL
mutex                 elapsed:    29049 us; per iteration:   29 ns / 0.000407 context switches.
c.v. ping-pong test   elapsed: 16131360 us; per iteration: 16131 ns / 4.29 context switches.
signal ping-pong test elapsed: 11817819 us; per iteration: 11817 ns / 4.16 context switches.
$ 

在內核3.1.9上

$ ./condvar-perf 1000000
NPTL
mutex                 elapsed:    26830 us; per iteration:   26 ns / 5.7e-05 context switches.
c.v. ping-pong test   elapsed: 12812788 us; per iteration: 12812 ns / 4.01 context switches.
signal ping-pong test elapsed: 13126865 us; per iteration: 13126 ns / 4.01 context switches.
$ chrt -f 1 ./condvar-perf 1000000
NPTL
mutex                 elapsed:    27025 us; per iteration:   27 ns / 3.7e-05 context switches.
c.v. ping-pong test   elapsed:  5099885 us; per iteration: 5099 ns / 4 context switches.
signal ping-pong test elapsed:  5508227 us; per iteration: 5508 ns / 4 context switches.
$ 

由於與c狀態分開的pstate驅動程序,您可能還會看到處理器在更新的進程和Linux內核中單擊。 所以另外,要禁用它,你需要以下內核參數:

intel_pstate=disable

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM