새로운 리눅스 커널에서 컨텍스트 전환이 훨씬 느립니다.


99

서버의 OS를 Ubuntu 10.04 LTS에서 Ubuntu 12.04 LTS로 업그레이드하려고합니다. 안타깝게도 실행 가능한 스레드를 실행하기위한 지연 시간이 2.6 커널에서 3.2 커널로 크게 증가한 것 같습니다. 실제로 우리가 얻고있는 대기 시간 수치는 믿기 어렵습니다.

테스트에 대해 좀 더 구체적으로 말씀 드리겠습니다. 두 개의 스레드를 실행하는 프로그램이 있습니다. 첫 번째 스레드는 현재 시간 (RDTSC를 사용하는 틱 단위)을 가져온 다음 1 초에 한 번 조건 변수에 신호를 보냅니다. 두 번째 스레드는 조건 변수를 기다렸다가 신호를 받으면 깨어납니다. 그런 다음 현재 시간 (RDTSC를 사용하는 틱 단위)을 가져옵니다. 두 번째 스레드의 시간과 첫 번째 스레드의 시간 간의 차이가 계산되어 콘솔에 표시됩니다. 이 후 두 번째 스레드는 조건 변수를 다시 한 번 기다립니다. 약 1 초 후에 첫 번째 스레드가 다시 신호를 보냅니다.

즉, 결과적으로 1 초에 한 번씩 조건 변수 대기 시간 측정을 통해 스레드 간 통신을 얻습니다 .

커널 2.6.32에서이 지연 시간은 2.8-3.5 us 정도이며 합리적입니다. 커널 3.2.0에서이 지연 시간은 40-100 us 정도까지 증가했습니다. 두 호스트 간의 하드웨어 차이를 제외했습니다. 동일한 하드웨어에서 실행됩니다 (하이퍼 스레딩, 스피드 스텝 및 모든 C 상태가 꺼진 상태에서 3.6GHz에서 실행되는 듀얼 소켓 X5687 {Westmere-EP} 프로세서). 테스트 앱은 스레드의 선호도를 변경하여 동일한 소켓의 독립적 인 물리적 코어에서 실행하므로 (즉, 첫 번째 스레드는 Core 0에서 실행되고 두 번째 스레드는 Core 1에서 실행 됨) 스레드가 튀어 나오지 않습니다. 코어 또는 소켓 간의 바운싱 / 통신.

두 호스트의 유일한 차이점은 하나는 커널 2.6.32-28 (빠른 컨텍스트 스위치 상자)과 함께 Ubuntu 10.04 LTS를 실행하고 다른 하나는 커널 3.2.0-23 (느린 컨텍스트)과 함께 최신 Ubuntu 12.04 LTS를 실행하고 있다는 것입니다. 스위치 박스). 모든 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 : 커널 매개 변수, 커널 변경 사항 및 개인 연구에 대한 게시물을 많이 검색 한 후 문제가 무엇인지 파악하고이 질문에 대한 답변으로 솔루션을 게시했습니다.


1
추측 일 뿐이지 만 매개 변수를에서 변경하면 /proc/sys/kernel/*작동 할 수 있습니까? 작동하는 것을 찾으면 해당 구성을 /etc/sysctl.conf넣거나 파일을 넣어 /etc/sysctl.d/재부팅 후에도 유지되도록하십시오.
Carlos Campderrós

1
두 호스트간에 / proc / sys / kernel을 비교했지만 특히 일정 관련 구성 항목에서 의미있는 차이는 보이지 않았습니다.
Michael Goldshteyn

RDTSC가 반드시 코어간에 제대로 동기화되지는 않는다는 소문이 모호하게 기억되지만 이것이 문제라면 시간 반전이 나타날 것으로 예상합니다. 동일한 코어 에서 두 스레드를 모두 실행 하고 어떤 일이 발생하는지 확인 하기 위해 친화도를 조작 해 보셨습니까 ?
다윗은 주어

이 새로운 Intel 코어에서 RDTSC는 코어, 특히 동일한 CPU (즉, 동일한 소켓)의 코어에서 완벽하게 작동합니다. 흥미롭게도 두 스레드가 동일한 코어에서 실행되는 경우 지연 시간은 최신 커널에서 4-10 us로 내려갑니다. 3 이전 커널에서.
Michael Goldshteyn

일반적인 의견입니다. 동기화를 위해 TSC에 의존하는 것은 기껏해야 할 일이지만 특정 경우에는 하나의 물리적 칩에 두 개의 코어를 사용하기 때문에 실제로 작동해야합니다.
twalberg

답변:


95

최근 커널 의 잘못된 스레드 깨우기 성능 문제 에 대한 intel_idle해결책 acpi_idle은 이전 커널에서 사용되는 드라이버 인에서 cpuidle 드라이버로 전환하는 것과 관련이 있습니다. 안타깝게도 intel_idle드라이버는 C 상태에 대한 사용자의 BIOS 구성을 무시 하고 자체 조정에 맞춰 춤을 춥니 다 . 즉, PC (또는 서버) BIOS에서 모든 C 상태를 완전히 비활성화하더라도이 드라이버는 일시적인 비활성 기간 동안 계속 강제로 활성화합니다. 이는 모든 코어를 사용하는 합성 벤치 마크 (예 : 스트레스 )이 실행 중입니다. 대부분의 호환 하드웨어에서 멋진 Google i7z 도구 를 사용하여 프로세서 주파수와 관련된 기타 유용한 정보와 함께 C 상태 전환을 모니터링 할 수 있습니다 .

현재 설정에서 활성화 된 cpuidle 드라이버를 확인하려면 다음과 같이 섹션에 current_driver파일을 분류하십시오 .cpuidle/sys/devices/system/cpu

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

최신 Linux OS에서 컨텍스트 전환 대기 시간을 최소화하려면 다음 커널 부팅 매개 변수를 추가하여 이러한 절전 기능을 모두 비활성화하십시오.

Ubuntu 12.04에서는 GRUB_CMDLINE_LINUX_DEFAULT항목에 추가 /etc/default/grub한 다음 update-grub. 추가 할 부팅 매개 변수는 다음과 같습니다.

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

세 가지 부팅 옵션이 수행하는 작업에 대한 자세한 내용은 다음과 같습니다.

intel_idle.max_cstate0으로 설정 하면 cpuidle 드라이버가 acpi_idle(적어도 옵션 문서에 따라)로 되돌 리거나 완전히 비활성화됩니다. 내 상자에서 그것은 완전히 비활성화되어 있습니다 (즉, current_driver파일을 표시 /sys/devices/system/cpu/cpuidle하면 출력이 생성됩니다 none). 이 경우 두 번째 부팅 옵션 processor.max_cstate=0은 필요하지 않습니다. 그러나 설명서에는 intel_idle드라이버에 대해 max_cstate를 0으로 설정 하면 OS가 acpi_idle드라이버로 되돌아 가야한다고 명시되어 있습니다. 따라서 경우에 대비하여 두 번째 부팅 옵션을 넣었습니다.

processor.max_cstate옵션은 acpi_idle드라이버 의 최대 C 상태 를 0으로 설정하여이 기능도 비활성화합니다. 내가 intel_idle.max_cstate=0사용할 수있는 모든 하드웨어에서 cpuidle 드라이버를 완전히 녹아웃시키기 때문에 이것을 테스트 할 수있는 시스템이 없습니다 . 설치가에서 당신 되돌리기 않는 경우, intel_idleacpi_idle바로 첫 번째 부팅 옵션을, 두 번째 옵션이 있으면 알려 주시기 바랍니다 processor.max_cstate가 나는이 대답을 업데이트 할 수 있도록 의견에서 할 문서화 있었는지했다.

마지막으로 세 매개 변수 중 마지막 매개 변수는 idle=poll진짜 파워 돼지입니다. C1 / C1E를 비활성화하여 훨씬 더 많은 전력 소비를 희생하면서 마지막 남은 지연 시간을 제거하므로 실제로 필요할 때만 사용하십시오. 대부분의 경우 C1 * 지연 시간이 그다지 크지 않기 때문에 과잉입니다. 원래 질문에서 설명한 하드웨어에서 실행되는 테스트 응용 프로그램을 사용하면 지연 시간이 9 us에서 3 us로 늘어났습니다. 이는 지연 시간에 민감한 애플리케이션 (예 : 금융 거래, 고정밀 원격 측정 / 추적, 고주파수 데이터 수집 등)의 경우 확실히 상당한 감소이지만 대부분의 데스크톱 앱. 확실하게 알 수있는 유일한 방법은 애플리케이션의 성능 향상을 프로파일 링하는 것입니다.

최신 정보:

다양한 idle=*매개 변수로 추가 테스트를 한 후 하드웨어에서 지원하는 경우로 설정 idle하는 mwait것이 훨씬 더 나은 아이디어라는 것을 발견했습니다 . 사용 보인다 MWAIT/MONITOR지침은 CPU가 눈에 띄는 지연이 시간까지 스레드 여파에 추가하지 않고 C1E를 입력 할 수 있습니다. 를 사용하면 idle=mwaitCPU 온도가 낮아지고 (에 비해 idle=poll) 전력 사용량이 줄어들고 폴링 유휴 루프의 뛰어난 낮은 대기 시간을 계속 유지할 수 있습니다. 따라서 이러한 결과를 기반으로 낮은 CPU 스레드 깨우기 대기 시간에 대해 업데이트 된 권장 부팅 매개 변수 집합은 다음과 같습니다.

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

idle=mwait대신 대신을 사용 idle=poll하면 Turbo Boost (CPU가 TDP [Thermal Design Power] 이하로 유지되도록함으로써) 및 하이퍼 스레딩 (MWAIT이 동시에 전체 물리적 코어를 소비하지 않는 이상적인 메커니즘)의 시작에 도움 이 될 수 있습니다. 더 높은 C 상태를 피하는 시간). 이것은 아직 테스트에서 입증되지 않았으며 계속해서 수행 할 것입니다.

업데이트 2 :

mwait유휴 옵션이되어 새로운 3.x를 커널에서 제거 (업데이트에 대한 사용자 ck_ 덕분에). 그러면 두 가지 옵션이 있습니다.

idle=halt-와 같이 작동해야 mwait하지만 하드웨어에 해당하는지 테스트하십시오. 이 HLT명령어는 MWAITwith state hint 0과 거의 동일 합니다. 문제는 HLT 상태에서 벗어나려면 인터럽트가 필요하지만 MWAIT 상태에서 벗어나려면 메모리 쓰기 (또는 인터럽트)를 사용할 수 있다는 사실에 있습니다. Linux 커널이 유휴 루프에서 사용하는 것에 따라 MWAIT를 잠재적으로 더 효율적으로 만들 수 있습니다. 그래서 내가 테스트 / 프로필을 말하고 그것이 당신의 대기 시간 요구를 충족하는지 확인하십시오 ...

idle=poll -전력과 열을 희생하는 최고의 성능 옵션.


죄송합니다. 펌웨어에서 C 상태를 관리하는 이유는 무엇입니까? 일시 중단 상태는 런타임 상태이며 OS에서 설계에 따라 관리됩니다. 발견했듯이 런타임 일시 중지를 원하지 않으면 사용하지 마십시오.
Andy Ross

6
죄송하지만 C 상태, EIST 및 C1E는 BIOS에서 끌 수 있습니다. OS가 내 BIOS 설정을 존중할 것으로 예상합니다. 이 경우에는 끔찍한 도구와 문서를 고려할 때 특히 그렇습니다.
Michael Goldshteyn

4
바이오스를 통해 꺼 졌을 수도 있습니다. 나는 그것을 요구하는 관련 사양에서 아무것도 모릅니다. 죄송합니다. BIOS에서 "예상"하는 것은 반복적으로 당신을 물릴 것입니다. 최신 PC에서 펌웨어가 할 수있는 가장 좋은 일은 아무것도 아닙니다. 놀랐 다니 유감이지만 솔직히 이것은 사용자 오류입니다. 벤치 마크는 일시 중지 및 재개 시간을 측정했습니다.
Andy Ross

19
BIOS 기능 선택의 역할 중 하나는 장치를 활성화 / 비활성화하는 것입니다. 경우에 따라 이러한 선택이 OS (예 : 온 마더 보드 USB, eSATA 및 NIC)에서 강제로 적용됩니다. 다른 경우에는 OS가 사용자의 요구를 존중해야합니다 (예 : EIST, C 상태, 하이퍼 스레딩, 실행 비활성화, AES-NI, 가상화 등). BIOS는 OS 중립적 인 단일 중앙 장치 / 기능 선택 화면을 제공합니다. 이를 통해 사용자는 모두 동일한 하드웨어 기능을 사용하는 여러 개의 (아마도 매우 다른) OS를 호스트에 설치할 수 있습니다. 그러나이 답변은 주관적이므로 동의하지 않는 데 동의해야합니다.
Michael Goldshteyn

1
idle = mwait는 최근 3.x 커널에서 더 이상 지원되지 않습니다. lkml.org/lkml/2013/2/10/21 대체 조언이 있습니까?
ck_

8

더 느려진 것은 아마도 조건 변수의 구성 요소 인 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. 평범한 오래된 유닉스 신호

나는 추가해야했다

#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.

커널 3.2에서 관찰 한 것만 큼은 아니지만 커널 2.6.32와 3.1.9 사이의 컨텍스트 전환이 실제로 느려 졌다고 결론을 내립니다. 아직 귀하의 질문에 대한 답변이 아님을 알고 있습니다. 계속해서 파헤칠 것입니다.

편집 : 프로세스 (두 스레드 모두)의 실시간 우선 순위를 변경하면 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.
$ 

Fedora 및 CentOS에서 실행했지만 Ubuntu가 없습니다. 결과를 게시하겠습니다.
amdn

좋아, 나는 두 호스트 (즉, 다른 커널)에서 실행했으며 결과는 거의 차이가 없음을 보여줍니다. 따라서이 테스트는 차이점을 강조하지 않았습니다. futex 호출 시간은 소수점 네 번째 자리에서 다릅니다. 성능이 크게 저하됩니다. 잠깐만 요, 정수는 초 단위인가요? 난 그냥 당신이 당신의 결과를 게시하고 그들이 ... 내 유사하게 나타납니다 보았다
마이클 Goldshteyn에게

좋아요, 그것은 futex 구현을 배제합니다-우리는 컨텍스트 전환 이론으로 돌아 왔습니다. ...이 답변은 실제로 주석에 속하므로 자유롭게 삭제하십시오 ... 명령 형식을 지정하는 기능을 원했습니다.
amdn

예, 시간은 초 단위입니다. 1 초 이상 지속되는 futex에 대한 호출은 조건에서 대기중인 스레드를위한 것입니다.
amdn

그렇다면 결과에서 무엇을 얻습니까?
Michael Goldshteyn

1

c-states와 분리 된 pstate 드라이버 로 인해 최신 프로세스 및 Linux 커널에서 프로세서가 클릭 다운되는 것을 볼 수도 있습니다 . 따라서이를 비활성화하려면 다음 커널 매개 변수를 사용해야합니다.

intel_pstate=disable

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.