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


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_idle로 acpi_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.