Saya mencoba memahami, pada tingkat tinggi, bagaimana satu utas berjalan di beberapa inti. Di bawah ini adalah pemahaman terbaik saya. Saya tidak percaya itu benar.
Berdasarkan bacaan saya tentang Hyper-threading , tampaknya OS mengatur instruksi semua utas sedemikian rupa sehingga mereka tidak saling menunggu. Kemudian front-end CPU mengatur lebih lanjut instruksi-instruksi tersebut dengan mendistribusikan satu utas untuk setiap inti, dan mendistribusikan instruksi independen dari setiap utas di antara siklus terbuka apa pun.
Jadi jika hanya ada satu utas, maka OS tidak akan melakukan optimasi. Namun, front-end CPU akan mendistribusikan set instruksi independen di antara masing-masing inti.
Menurut https://stackoverflow.com/a/15936270 , bahasa pemrograman tertentu dapat membuat lebih atau kurang utas, tetapi tidak relevan ketika menentukan apa yang harus dilakukan dengan utas tersebut. OS dan CPU menangani ini, jadi ini terjadi terlepas dari bahasa pemrograman yang digunakan.
Hanya untuk memperjelas, saya bertanya tentang menjalankan satu utas di beberapa inti, bukan tentang menjalankan beberapa utas pada satu inti.
Apa yang salah dengan ringkasan saya? Di mana dan bagaimana instruksi utas terpecah di antara beberapa inti? Apakah bahasa pemrograman itu penting? Saya tahu ini adalah topik yang luas; Saya berharap untuk pemahaman tingkat tinggi tentangnya.
sumber
Jawaban:
Sistem operasi menawarkan irisan waktu CPU untuk utas yang memenuhi syarat untuk dijalankan.
Jika hanya ada satu inti, maka sistem operasi menjadwalkan utas yang paling memenuhi syarat untuk dijalankan pada inti itu untuk suatu irisan waktu. Setelah irisan waktu selesai, atau ketika utas berjalan memblokir pada IO, atau ketika prosesor terganggu oleh peristiwa eksternal, sistem operasi mengevaluasi ulang utas apa yang akan dijalankan berikutnya (dan itu dapat memilih utas yang sama lagi atau yang berbeda).
Kelayakan untuk menjalankan terdiri dari variasi pada keadilan dan prioritas dan kesiapan, dan dengan metode ini berbagai utas mendapatkan waktu, beberapa lebih dari yang lain.
Jika ada beberapa core, N, maka sistem operasi menjadwalkan thread N yang paling memenuhi syarat untuk dijalankan pada core.
Affinity Prosesor adalah pertimbangan efisiensi. Setiap kali CPU menjalankan utas yang berbeda dari sebelumnya, ia cenderung melambat sedikit karena cache-nya hangat untuk utas sebelumnya, tetapi dingin ke yang baru. Dengan demikian, menjalankan utas yang sama pada prosesor yang sama dalam berbagai waktu adalah keuntungan efisiensi.
Namun, sistem operasi bebas untuk menawarkan satu irisan waktu-ulir pada CPU yang berbeda, dan itu dapat berputar melalui semua CPU pada irisan waktu yang berbeda. Namun, itu tidak bisa, seperti yang dikatakan @ gnasher729 , menjalankan satu utas pada banyak CPU secara bersamaan.
Hyperthreading adalah metode di hardware dimana single ditingkatkan core CPU dapat mendukung pelaksanaan dua atau lebih yang berbeda thread secara bersamaan. (CPU semacam itu dapat menawarkan thread tambahan dengan biaya lebih rendah di real-estate silikon daripada core penuh tambahan.) Core CPU yang ditingkatkan ini perlu mendukung status tambahan untuk thread lain, seperti nilai register CPU, dan juga memiliki status koordinasi & perilaku yang memungkinkan pembagian unit-unit fungsional di dalam CPU tersebut tanpa menyatukan utas.
Hyperthreading, sementara secara teknis menantang dari perspektif perangkat keras, dari perspektif programmer, model eksekusi hanyalah model core CPU tambahan daripada yang lebih kompleks. Jadi, sistem operasi melihat core CPU tambahan, meskipun ada beberapa masalah afinitas prosesor baru karena beberapa thread hyperthreaded berbagi arsitektur cache satu core CPU.
Kita mungkin secara naif berpikir bahwa dua utas yang berjalan pada inti yang hiphread masing-masing berjalan setengah secepat masing-masing dengan inti penuh mereka sendiri. Tetapi ini belum tentu demikian, karena eksekusi satu thread penuh dengan siklus slack, dan beberapa jumlah dari mereka dapat digunakan oleh thread lain yang di-hyperhread. Lebih lanjut, bahkan selama siklus non-kendur, satu utas mungkin menggunakan unit fungsional yang berbeda dari yang lain sehingga eksekusi simultan dapat terjadi. CPU yang disempurnakan untuk hyperthreading mungkin memiliki beberapa unit fungsional tertentu yang banyak digunakan khusus untuk mendukungnya.
sumber
Tidak ada yang namanya thread tunggal yang berjalan di beberapa core secara bersamaan.
Namun itu tidak berarti bahwa instruksi dari satu utas tidak dapat dijalankan secara paralel. Ada mekanisme yang disebut instruksi pipelining dan eksekusi out-of-order yang memungkinkannya. Setiap inti memiliki banyak sumber daya yang berlebihan yang tidak digunakan oleh instruksi sederhana, sehingga beberapa instruksi tersebut dapat dijalankan bersama (selama yang berikutnya tidak tergantung pada hasil sebelumnya). Namun, ini masih terjadi di dalam satu inti.
Hyper-threading adalah jenis varian ekstrim dari ide ini, di mana satu inti tidak hanya mengeksekusi instruksi dari satu thread secara paralel, tetapi juga mencampur instruksi dari dua thread yang berbeda untuk mengoptimalkan penggunaan sumber daya lebih jauh.
Entri Wikipedia terkait: Perpipaan instruksi , eksekusi out-of-order .
sumber
a[i] = b[i] + c[i]
lingkaran, setiap iterasi adalah independen, sehingga memuat, menambah, dan menyimpan dari iterasi yang berbeda dapat terbang sekaligus. Itu harus mempertahankan ilusi bahwa instruksi dieksekusi dalam urutan program, tetapi misalnya toko yang melewatkan dalam cache tidak menunda utas (sampai kehabisan ruang di buffer toko).rangkuman: Menemukan dan mengeksploitasi paralelisme (tingkat instruksi) dalam program berulir tunggal dilakukan murni dalam perangkat keras, oleh inti CPU yang digunakan. Dan hanya melalui beberapa ratus instruksi, bukan pemesanan ulang skala besar.
Program single-threaded tidak mendapatkan manfaat dari CPU multi-core, kecuali bahwa hal - hal lain dapat berjalan pada core lain alih-alih mengambil waktu jauh dari tugas single-threaded.
OS TIDAK melihat ke dalam aliran instruksi dari thread. Itu hanya menjadwalkan utas ke inti.
Sebenarnya, setiap core menjalankan fungsi scheduler OS ketika perlu mencari tahu apa yang harus dilakukan selanjutnya. Penjadwalan adalah algoritma terdistribusi. Untuk lebih memahami mesin multi-core, anggap setiap core menjalankan kernel secara terpisah. Sama seperti program multi-utas, kernel ditulis sehingga kodenya pada satu inti dapat dengan aman berinteraksi dengan kodenya di inti lain untuk memperbarui struktur data bersama (seperti daftar utas yang siap dijalankan.
Bagaimanapun, OS terlibat dalam membantu proses multi-threaded mengeksploitasi paralelisme level-thread yang harus diekspos secara eksplisit dengan secara manual menulis program multi-threaded . (Atau oleh kompilator penjajaran otomatis dengan OpenMP atau sesuatu).
Inti CPU hanya menjalankan satu aliran instruksi, jika tidak dihentikan (tertidur hingga interupsi berikutnya, mis. Interupsi timer). Seringkali itu adalah utas, tetapi bisa juga berupa penangan interrupt kernel, atau kode kernel lain-lain jika kernel memutuskan untuk melakukan sesuatu selain hanya kembali ke utas sebelumnya setelah menangani dan mengganggu atau panggilan sistem.
Dengan HyperThreading atau desain SMT lainnya, inti CPU fisik berfungsi seperti beberapa inti "logis". Satu-satunya perbedaan dari perspektif OS antara CPU quad-core-dengan-hyperthreading (4c8t) dan mesin 8-core (8c8t) adalah bahwa OS yang sadar HT akan mencoba menjadwalkan utas untuk memisahkan inti fisik sehingga mereka tidak perlu t bersaing satu sama lain. Sebuah OS yang tidak tahu tentang hyperthreading hanya akan melihat 8 core (kecuali Anda menonaktifkan HT di BIOS, maka itu hanya akan mendeteksi 4).
Istilah " front-end" mengacu pada bagian inti CPU yang mengambil kode mesin, menerjemahkan instruksi, dan mengeluarkannya ke bagian inti yang tidak sesuai pesanan . Setiap inti memiliki front-end sendiri, dan itu bagian dari inti secara keseluruhan. Instruksi yang diambil adalah apa yang sedang dijalankan CPU.
Di dalam bagian inti yang tidak sesuai pesanan, instruksi (atau uops) dikirim ke port eksekusi ketika operand input mereka siap dan ada port eksekusi gratis. Ini tidak harus terjadi dalam urutan program, jadi ini adalah bagaimana CPU OOO dapat mengeksploitasi paralelisme tingkat instruksi dalam satu utas .
Jika Anda mengganti "inti" dengan "unit eksekusi" dalam ide Anda, Anda hampir benar. Ya, CPU mendistribusikan instruksi independen / uops ke unit eksekusi secara paralel. (Tapi ada campuran istilah, karena Anda mengatakan "front-end" padahal sebenarnya itu adalah scheduler instruksi-CPU alias Stasiun Reservasi yang mengambil instruksi yang siap dieksekusi).
Eksekusi out-of-order hanya dapat menemukan ILP di tingkat yang sangat lokal, hanya hingga beberapa ratus instruksi, bukan antara dua loop independen (kecuali mereka pendek).
Sebagai contoh, ASM setara dengan ini
akan berjalan secepat loop yang sama hanya menambah satu penghitung pada Intel Haswell.
i++
hanya tergantung pada nilai sebelumnyai
, sementaraj++
hanya tergantung pada nilai sebelumnyaj
, sehingga dua rantai dependensi dapat berjalan secara paralel tanpa merusak ilusi segala sesuatu yang dieksekusi dalam urutan program.Pada x86, loop akan terlihat seperti ini:
Haswell memiliki 4 port eksekusi integer, dan semuanya memiliki unit adder, sehingga dapat mempertahankan throughput hingga 4
inc
instruksi per jam jika semuanya independen. (Dengan latensi = 1, jadi Anda hanya perlu 4 register untuk memaksimalkan throughput dengan menjaga 4inc
instruksi dalam penerbangan. Bandingkan dengan vektor-FP MUL atau FMA: latensi = 5 throughput = 0,5 membutuhkan 10 vektor akumulator untuk menjaga 10 FMA dalam penerbangan untuk memaksimalkan throughput. Dan setiap vektor bisa 256b, menampung 8 float presisi tunggal).Cabang yang diambil juga merupakan hambatan: loop selalu membutuhkan setidaknya satu jam penuh per iterasi, karena throughput cabang yang diambil terbatas pada 1 per jam. Saya bisa memasukkan satu instruksi lagi ke dalam loop tanpa mengurangi kinerja, kecuali itu juga membaca / menulis
eax
atauedx
dalam hal ini akan memperpanjang rantai ketergantungan. Meletakkan 2 instruksi lebih banyak di loop (atau satu instruksi multi-uop yang kompleks) akan membuat hambatan di front-end, karena hanya dapat mengeluarkan 4 uops per jam ke dalam core out-of-order. (Lihat T&J SO ini untuk beberapa perincian tentang apa yang terjadi untuk loop yang bukan kelipatan dari 4 uops: loop-buffer dan cache uop membuat hal-hal menarik.)Dalam kasus yang lebih kompleks, menemukan paralelisme membutuhkan melihat jendela instruksi yang lebih besar . (mis. mungkin ada urutan 10 instruksi yang semuanya tergantung satu sama lain, kemudian beberapa yang independen).
Kapasitas Re-Order Buffer adalah salah satu faktor yang membatasi ukuran jendela out-of-order. Di Intel Haswell, ini 192 uops. (Dan Anda bahkan dapat mengukurnya secara eksperimental , bersama dengan kapasitas pengubahan nama register (ukuran file register).) Core CPU berdaya rendah seperti ARM memiliki ukuran ROB yang jauh lebih kecil, jika mereka melakukan eksekusi yang tidak sesuai pesanan sama sekali.
Perhatikan juga bahwa CPU perlu disalin, serta rusak. Jadi ia harus mengambil & mendekode instruksi dengan baik sebelum yang dieksekusi, lebih disukai dengan throughput yang cukup untuk mengisi ulang buffer setelah melewatkan siklus pengambilan. Cabang-cabang itu rumit, karena kita tidak tahu ke mana harus mengambilnya jika kita tidak tahu ke mana cabang itu pergi. Inilah sebabnya mengapa prediksi cabang sangat penting. (Dan mengapa CPU modern menggunakan eksekusi spekulatif: mereka menebak ke arah mana cabang akan pergi dan mulai mengambil / mendekode / mengeksekusi aliran instruksi tersebut. Ketika kesalahan prediksi terdeteksi, mereka memutar kembali ke kondisi baik-terakhir yang diketahui dan mengeksekusi dari sana.)
Jika Anda ingin membaca lebih lanjut tentang internal CPU, ada beberapa tautan di wiki tag Stackoverflow x86 , termasuk ke panduan microarch Agner Fog , dan ke tulisan lengkap David Kanter dengan diagram Intel dan AMD CPU. Dari penulisan mikroarsitektur Intel Haswell-nya , ini adalah diagram terakhir dari seluruh pipa dari inti Haswell (bukan seluruh chip).
Ini adalah diagram blok dari inti CPU tunggal . CPU quad-core memiliki 4 di chip, masing-masing dengan cache L1 / L2 mereka sendiri (berbagi cache L3, pengontrol memori, dan koneksi PCIe ke perangkat sistem).
Saya tahu ini sangat rumit. Artikel Kanter juga menunjukkan bagian-bagian ini untuk membicarakan tentang frontend secara terpisah dari unit eksekusi atau cache, misalnya.
sumber
inc
instruksi dalam siklus clock yang sama, ke 4 unit eksekusi ALU integernya.