Konteks beralih jauh lebih lambat di kernel linux baru

99

Kami ingin meningkatkan OS di server kami dari Ubuntu 10.04 LTS ke Ubuntu 12.04 LTS. Sayangnya, tampaknya latensi untuk menjalankan thread yang dapat dijalankan telah meningkat secara signifikan dari kernel 2.6 ke kernel 3.2. Faktanya, angka latensi yang kami dapatkan sulit dipercaya.

Izinkan saya lebih spesifik tentang tes ini. Kami memiliki program yang menjalankan dua utas. Utas pertama mendapatkan waktu saat ini (dalam tanda centang menggunakan RDTSC) dan kemudian memberi sinyal variabel kondisi sekali dalam satu detik. Utas kedua menunggu pada variabel kondisi dan bangun ketika diberi sinyal. Ini kemudian mendapatkan waktu saat ini (di kutu menggunakan RDTSC). Perbedaan antara waktu di utas kedua dan waktu di utas pertama dihitung dan ditampilkan di konsol. Setelah ini, thread kedua menunggu variabel kondisi sekali lagi. Ini akan ditandai lagi oleh utas pertama setelah sekitar satu lintasan kedua.

Jadi, singkatnya kita mendapatkan komunikasi thread ke thread melalui pengukuran latensi variabel kondisi sekali dalam satu detik sebagai hasilnya.

Di kernel 2.6.32, latensi ini berada di urutan 2.8-3.5 us, yang masuk akal. Di kernel 3.2.0, latensi ini telah meningkat ke suatu tempat di urutan 40-100 kami. Saya telah mengecualikan perbedaan perangkat keras antara kedua host. Mereka berjalan pada perangkat keras yang sama (prosesor soket ganda X5687 {Westmere-EP} yang berjalan pada 3,6 GHz dengan hyperthreading, speedstep dan semua status C dimatikan). Aplikasi pengujian mengubah afinitas utas untuk menjalankannya pada inti fisik independen dari soket yang sama (mis., Utas pertama dijalankan pada Inti 0 dan utas kedua dijalankan pada Inti 1), jadi tidak ada utas yang terpental pada core atau memantul / komunikasi antar soket.

Satu-satunya perbedaan antara kedua host adalah yang satu menjalankan Ubuntu 10.04 LTS dengan kernel 2.6.32-28 (kotak sakelar konteks cepat) dan yang lainnya menjalankan Ubuntu 12.04 LTS terbaru dengan kernel 3.2.0-23 (konteks lambat kotak saklar). Semua pengaturan BIOS dan perangkat keras identik.

Apakah ada perubahan dalam kernel yang dapat menyebabkan kelambatan konyol ini dalam berapa lama waktu yang dibutuhkan untuk sebuah utas dijadwalkan untuk berjalan?

Pembaruan: Jika Anda ingin menjalankan pengujian pada host dan linux build Anda, saya telah memposting kode ke pastebin untuk dibaca dengan teliti. Kompilasi dengan:

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

Jalankan dengan (dengan asumsi Anda memiliki setidaknya kotak dual-core):

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

Pembaruan 2 : Setelah banyak mencari melalui parameter kernel, posting tentang perubahan kernel dan penelitian pribadi, saya telah menemukan apa masalahnya dan telah memposting solusi sebagai jawaban untuk pertanyaan ini.

Michael Goldshteyn
sumber
1
hanya tebakan, tetapi mungkin mengubah parameter dari /proc/sys/kernel/*mungkin berhasil? Jika Anda menemukan sesuatu yang berfungsi, masukkan konfigurasi itu /etc/sysctl.confatau file /etc/sysctl.d/untuk membuatnya tetap ada saat reboot.
Carlos Campderrós
1
Saya membandingkan / proc / sys / kernel antara dua host, tetapi tidak melihat perbedaan yang berarti, terutama dalam penjadwalan item konfigurasi terkait.
Michael Goldshteyn
Saya samar-samar mengingat desas-desus bahwa RDTSC tidak selalu disinkronkan dengan benar antar inti, tetapi saya berharap bahwa jika ini adalah masalah, Anda akan melihat pembalikan waktu. Sudahkah Anda mencoba mengutak-atik afinitas untuk menjalankan kedua utas pada inti yang sama dan melihat apa yang terjadi?
David Diberikan
Pada core Intel yang baru ini, RDTSC bekerja dengan sempurna di seluruh core, terutama core pada CPU yang sama (yaitu soket yang sama). Menariknya, jika kedua utas dijalankan pada inti yang sama, latensi turun menjadi 4-10 kami pada kernel yang lebih baru dan kira-kira. 3 kami di kernel lama.
Michael Goldshteyn
Hanya komentar umum - mengandalkan TSC untuk disinkronkan paling banter, meskipun dalam kasus khusus Anda, karena Anda menggunakan dua inti pada satu chip fisik, itu seharusnya benar-benar berfungsi.
twalberg

Jawaban:

95

Solusi untuk masalah kinerja thread wake up yang buruk di kernel baru-baru ini berkaitan dengan peralihan ke intel_idledriver cpuidle dari acpi_idle, driver yang digunakan di kernel lama. Sayangnya, intel_idlepengemudi mengabaikan konfigurasi BIOS pengguna untuk status-C dan menari mengikuti iramanya sendiri . Dengan kata lain, bahkan jika Anda benar-benar menonaktifkan semua status C di BIOS PC (atau server) Anda, driver ini akan tetap memaksanya selama periode tidak aktif yang singkat, yang hampir selalu terjadi kecuali jika semua inti memakan benchmark sintetis (mis., Stres ) sedang berlari. Anda dapat memantau transisi status C, bersama dengan informasi berguna lainnya yang terkait dengan frekuensi prosesor, menggunakan alat Google i7z yang luar biasa di sebagian besar perangkat keras yang kompatibel.

Untuk melihat driver cpuidle mana yang saat ini aktif di pengaturan Anda, cukup catkan current_driverfile di cpuidlebagian /sys/devices/system/cpusebagai berikut:

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

Jika Anda ingin OS Linux modern Anda memiliki latensi sakelar konteks serendah mungkin, tambahkan parameter boot kernel berikut untuk menonaktifkan semua fitur hemat daya ini:

Di Ubuntu 12.04, Anda dapat melakukan ini dengan menambahkannya ke GRUB_CMDLINE_LINUX_DEFAULTentri /etc/default/grubdan kemudian menjalankannya update-grub. Parameter boot yang akan ditambahkan adalah:

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

Berikut adalah detail berdarah tentang apa yang dilakukan ketiga opsi boot:

Mengatur intel_idle.max_cstateke nol akan mengembalikan driver cpuidle Anda ke acpi_idle(setidaknya sesuai dengan dokumentasi opsi), atau menonaktifkannya sepenuhnya. Di kotak saya itu benar-benar dinonaktifkan (yaitu, menampilkan current_driverfile dalam /sys/devices/system/cpu/cpuidlemenghasilkan output none). Dalam hal ini, opsi boot kedua processor.max_cstate=0tidak diperlukan. Namun, dokumentasi menyatakan bahwa pengaturan max_cstate ke nol untuk intel_idledriver harus mengembalikan OS ke acpi_idledriver. Oleh karena itu, saya memasukkan opsi boot kedua untuk berjaga-jaga.

The processor.max_cstatepilihan menetapkan negara maksimum C untuk acpi_idlepengemudi ke nol, mudah-mudahan nonaktifkan juga. Saya tidak memiliki sistem untuk menguji ini, karena intel_idle.max_cstate=0benar-benar menghentikan driver cpuidle pada semua perangkat keras yang tersedia untuk saya. Namun, jika instalasi Anda mengembalikan Anda dari intel_idleke acpi_idlehanya dengan opsi boot pertama, beri tahu saya jika opsi kedua, processor.max_cstatemelakukan apa yang didokumentasikan untuk dilakukan di komentar sehingga saya dapat memperbarui jawaban ini.

Terakhir, yang terakhir dari tiga parameter, idle=polladalah babi kekuatan nyata. Ini akan menonaktifkan C1 / C1E, yang akan menghapus sisa latensi terakhir dengan mengorbankan lebih banyak konsumsi daya, jadi gunakan yang itu hanya jika benar-benar diperlukan. Untuk sebagian besar hal ini akan berlebihan, karena latensi C1 * tidak terlalu besar. Menggunakan aplikasi pengujian saya yang berjalan pada perangkat keras yang saya jelaskan dalam pertanyaan asli, latensi berubah dari 9 kita menjadi 3 kita. Ini tentu saja merupakan pengurangan yang signifikan untuk aplikasi yang sangat sensitif latensi (misalnya, perdagangan keuangan, telemetri / pelacakan presisi tinggi, akuisisi data frekuensi tinggi, dll ...), tetapi mungkin tidak sepadan dengan daya listrik yang dikeluarkan untuk sebagian besar aplikasi desktop. Satu-satunya cara untuk mengetahui secara pasti adalah dengan membuat profil peningkatan kinerja aplikasi Anda vs.

Memperbarui:

Setelah pengujian tambahan dengan berbagai idle=*parameter, saya menemukan bahwa pengaturan idleke mwaitjika didukung oleh perangkat keras Anda adalah ide yang jauh lebih baik. Tampaknya penggunaan MWAIT/MONITORinstruksi memungkinkan CPU untuk memasuki C1E tanpa adanya latensi yang terlihat ditambahkan ke waktu bangun thread. Dengan idle=mwait, Anda akan mendapatkan suhu CPU yang lebih dingin (dibandingkan dengan idle=poll), penggunaan daya yang lebih sedikit, dan masih mempertahankan latensi rendah yang sangat baik dari loop idle polling. Oleh karena itu, rangkaian parameter boot yang saya rekomendasikan untuk latensi bangun thread CPU rendah berdasarkan temuan ini adalah:

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

Penggunaan idle=mwaitalih - alih idle=polljuga dapat membantu inisiasi Turbo Boost (dengan membantu CPU tetap di bawah TDP [Daya Desain Termal]) dan hyperthreading (yang MWAIT adalah mekanisme ideal untuk tidak mengonsumsi seluruh inti fisik sementara pada saat yang sama waktu menghindari status C yang lebih tinggi). Ini belum terbukti dalam pengujian, bagaimanapun, yang akan terus saya lakukan.

Perbarui 2:

The mwaitpilihan menganggur telah dihapus dari kernel 3.x yang lebih baru (terima kasih kepada pengguna ck_ untuk update). Itu membuat kita memiliki dua pilihan:

idle=halt- Seharusnya bekerja sebaik itu mwait, tetapi uji untuk memastikan bahwa ini masalahnya dengan perangkat keras Anda. The HLTinstruksi hampir setara dengan MWAITdengan negara petunjuk 0. Masalah terletak pada kenyataan bahwa interupsi diperlukan untuk keluar dari keadaan HLT, sementara write memori (atau interrupt) dapat digunakan untuk keluar dari negara MWAIT. Bergantung pada apa yang Kernel Linux gunakan dalam loop diamnya, ini dapat membuat MWAIT berpotensi lebih efisien. Jadi, seperti yang saya katakan, uji / profil dan lihat apakah itu memenuhi kebutuhan latensi Anda ...

dan

idle=poll - Opsi kinerja tertinggi, dengan mengorbankan daya dan panas.

Michael Goldshteyn
sumber
Maaf, tetapi mengapa Anda mengharapkan status C dikelola oleh firmware? Status penangguhan adalah status waktu proses, dan dikelola oleh OS berdasarkan desain. Seperti yang Anda temukan, jika Anda tidak ingin runtime suspend jangan gunakan itu.
Andy Ross
6
Maaf, tetapi C menyatakan, EIST dan C1E dapat dimatikan di BIOS. Saya berharap OS menghormati pengaturan BIOS saya. Ini terutama benar, mengingat perkakas dan dokumentasi yang menghebohkan dalam kasus ini.
Michael Goldshteyn
4
Dimatikan melalui bios Anda, mungkin. Saya tidak tahu apa pun dalam spesifikasi relevan yang mengharuskannya. Maaf, tapi "mengharapkan" apapun dari BIOS akan menggigit Anda berulang kali. Hal terbaik yang dapat dilakukan firmware di PC modern bukanlah apa-apa. Maaf Anda terkejut, tapi sejujurnya ini adalah kesalahan pengguna. Tolok ukur Anda mengukur waktu penangguhan dan lanjutkan.
Andy Ross
19
Salah satu peran pemilihan fitur BIOS adalah untuk mengaktifkan / menonaktifkan perangkat. Dalam beberapa kasus, pemilihan ini dipaksakan pada OS (mis., USB pada motherboard, eSATA dan NIC). Di tempat lain, OS diharapkan menghormati keinginan Anda (misalnya, EIST, status C, Hyperthreading, Execute Disable, AES-NI, Virtualisasi, dll ...). BIOS menyediakan satu perangkat pusat / permukaan pemilihan fitur yang OS netral. Hal ini memungkinkan pengguna untuk menginstal beberapa (mungkin sangat berbeda) OS pada host yang semuanya menggunakan fitur perangkat keras yang sama. Namun, jawaban ini subyektif jadi harus setuju untuk tidak setuju.
Michael Goldshteyn
1
idle = mwait tidak lagi didukung di kernel 3.x terbaru lkml.org/lkml/2013/2/10/21 ada saran alternatif?
ck_
8

Mungkin yang menjadi lebih lambat adalah futex, blok penyusun untuk variabel kondisi. Ini akan menjelaskan:

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

kemudian

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

yang akan menampilkan mikrodetik yang diambil untuk panggilan sistem yang menarik, diurutkan berdasarkan waktu.

Pada kernel 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

Pada kernel 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

Saya menemukan laporan bug berusia 5 tahun ini yang berisi tes kinerja "ping pong" yang sebanding

  1. libpthread mutex utas tunggal
  2. variabel kondisi libpthread
  3. sinyal Unix lama biasa

Saya harus menambahkan

#include <stdint.h>

untuk mengkompilasi, yang saya lakukan dengan perintah ini

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

Pada kernel 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.

Pada kernel 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.

Saya menyimpulkan bahwa antara kernel 2.6.32 dan 3.1.9 saklar konteks memang melambat, meskipun tidak sebanyak yang Anda amati di kernel 3.2. Saya menyadari ini belum menjawab pertanyaan Anda, saya akan terus menggali.

Sunting: Saya telah menemukan bahwa mengubah prioritas waktu nyata dari proses (kedua utas) meningkatkan kinerja pada 3.1.9 agar sesuai dengan 2.6.32. Namun, menyetel prioritas yang sama pada 2.6.32 membuatnya melambat ... lihat saja - Saya akan memeriksanya lebih lanjut.

Inilah hasil saya sekarang:

Pada kernel 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.
$ 

Pada kernel 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.
$ 
amdn
sumber
Saya menjalankannya di Fedora dan CentOS, tidak memiliki Ubuntu. Saya akan memposting hasil saya.
amdn
Oke, saya menjalankannya di kedua host (yaitu, dan kernel yang berbeda) dan hasilnya hampir tidak menunjukkan perbedaan. Jadi, tes ini tidak menunjukkan adanya perbedaan. Waktu panggilan futex berbeda di tempat desimal keempat - penurunan kinerja yang tidak signifikan. Err tunggu, apakah bilangan bulat dalam hitungan detik? Saya baru saja melihat Anda memposting hasil Anda dan hasilnya tampak mirip dengan saya ...
Michael Goldshteyn
Ok, itu mengesampingkan penerapan futex - kami kembali ke teori Anda tentang sakelar konteks .... jangan ragu untuk menghapus jawaban ini karena jawaban ini benar-benar ada dalam komentar ... Saya hanya ingin kemampuan untuk memformat perintah.
amdn
Ya, waktunya dalam hitungan detik ... panggilan ke futex yang berlangsung lebih lama dari satu detik adalah untuk thread yang menunggu dengan syarat.
amdn
Jadi, bagaimana jika ada yang Anda peroleh dari hasil?
Michael Goldshteyn
1

Anda mungkin juga melihat prosesor mengklik ke bawah dalam proses yang lebih baru dan kernel Linux karena driver pstate yang terpisah dari c-state. Jadi sebagai tambahan, untuk menonaktifkan ini, Anda menggunakan parameter kernel berikut:

intel_pstate=disable

Kyle Brandt
sumber