Apa arti istilah "CPU terikat" dan "I / O terikat"?
Apa arti istilah "CPU terikat" dan "I / O terikat"?
Jawaban:
Cukup intuitif:
Suatu program terikat CPU jika ia akan pergi lebih cepat jika CPU lebih cepat, yaitu menghabiskan sebagian besar waktunya hanya menggunakan CPU (melakukan perhitungan). Sebuah program yang menghitung angka baru π biasanya akan terikat dengan CPU, itu hanya angka-angka.
Suatu program terikat I / O jika akan berjalan lebih cepat jika subsistem I / O lebih cepat. Sistem I / O yang tepat yang dimaksud dapat bervariasi; Saya biasanya mengasosiasikannya dengan disk, tetapi tentu saja jaringan atau komunikasi secara umum juga umum. Sebuah program yang melihat melalui file besar untuk beberapa data mungkin menjadi I / O terikat, karena bottleneck adalah pembacaan data dari disk (sebenarnya, contoh ini mungkin agak kuno hari ini dengan ratusan MB / s datang dari SSD).
CPU Bound berarti tingkat kemajuan proses dibatasi oleh kecepatan CPU. Tugas yang melakukan perhitungan pada sejumlah kecil angka, misalnya mengalikan matriks kecil, kemungkinan terikat CPU.
I / O Bound berarti laju kemajuan suatu proses dibatasi oleh kecepatan subsistem I / O. Tugas yang memproses data dari disk, misalnya, menghitung jumlah baris dalam file cenderung terikat I / O.
Batas memori berarti laju kemajuan suatu proses dibatasi oleh jumlah memori yang tersedia dan kecepatan akses memori itu. Tugas yang memproses data memori dalam jumlah besar, misalnya mengalikan matriks besar, kemungkinan adalah Memory Bound.
Batas cache berarti tingkat kemajuan proses dibatasi oleh jumlah dan kecepatan cache yang tersedia. Tugas yang hanya memproses lebih banyak data daripada yang cocok dengan cache akan terikat cache.
I / O Bound akan lebih lambat dari Memory Bound akan lebih lambat dari Cache Bound akan lebih lambat dari CPU Bound.
Solusi untuk terikat I / O tidak harus mendapatkan Memori lebih banyak. Dalam beberapa situasi, algoritma akses dapat dirancang di sekitar batasan I / O, Memori atau Cache. Lihat Algoritma Cache Oblivious .
Multi-threading
Dalam jawaban ini, saya akan menyelidiki satu kasus penggunaan penting yang membedakan antara pekerjaan yang dibatasi CPU vs IO: saat menulis kode multi-utas.
RAM I / O terikat contoh: Jumlah Sum
Pertimbangkan program yang menjumlahkan semua nilai dari satu vektor:
#define SIZE 1000000000
unsigned int is[SIZE];
unsigned int sum = 0;
size_t i = 0;
for (i = 0; i < SIZE; i++)
/* Each one of those requires a RAM access! */
sum += is[i]
Memparalelkannya dengan memisahkan array secara merata untuk masing-masing inti Anda adalah kegunaan terbatas pada desktop modern yang umum.
Sebagai contoh, pada Ubuntu 19.04 saya, laptop Lenovo ThinkPad P51 dengan CPU: CPU Intel Core i7-7820HQ (4 core / 8 thread), RAM: 2x Samsung M471A2K43BB1-CRC (2x 16GiB) Saya mendapatkan hasil seperti ini:
Perhatikan bahwa ada banyak perbedaan antara proses. Tapi saya tidak bisa meningkatkan ukuran array lebih jauh karena saya sudah di 8GiB, dan saya tidak berminat untuk statistik di beberapa berjalan hari ini. Namun ini seperti menjalankan biasa setelah melakukan banyak menjalankan manual.
Kode benchmark:
pthread
Kode sumber C POSIX digunakan dalam grafik.
Dan di sini adalah versi C ++ yang menghasilkan hasil yang analog.
Saya tidak tahu arsitektur komputer yang cukup untuk sepenuhnya menjelaskan bentuk kurva, tetapi satu hal yang jelas: perhitungan tidak menjadi 8x lebih cepat seperti yang diharapkan secara naif karena saya menggunakan semua 8 thread saya! Untuk beberapa alasan, 2 dan 3 utas adalah yang optimal, dan menambahkan lebih banyak hanya membuat segalanya lebih lambat.
Bandingkan ini dengan pekerjaan yang terikat CPU, yang sebenarnya mendapatkan 8 kali lebih cepat: Apa arti 'nyata', 'pengguna' dan 'sistem' dalam output waktu (1)?
Alasannya adalah semua prosesor berbagi bus memori tunggal yang terhubung ke RAM:
CPU 1 --\ Bus +-----+
CPU 2 ---\__________| RAM |
... ---/ +-----+
CPU N --/
jadi bus memori dengan cepat menjadi hambatan, bukan CPU.
Ini terjadi karena menambahkan dua angka membutuhkan satu siklus CPU, memori membaca membutuhkan sekitar 100 siklus CPU pada perangkat keras 2016.
Jadi pekerjaan CPU yang dilakukan per byte input data terlalu kecil, dan kami menyebutnya proses yang terikat IO.
Satu-satunya cara untuk mempercepat perhitungan itu lebih jauh, adalah mempercepat akses memori individu dengan perangkat keras memori baru, misalnya memori multi-channel .
Meng-upgrade ke jam CPU yang lebih cepat misalnya tidak akan sangat berguna.
Contoh lainnya
multiplikasi matriks terikat CPU pada RAM dan GPU. Masukan berisi:
2 * N**2
angka, tetapi:
N ** 3
multiplikasi dilakukan, dan itu sudah cukup untuk paralelisasi menjadi layak untuk N. praktis besar
Inilah sebabnya mengapa pustaka multiplikasi matriks CPU paralel seperti berikut ada:
Penggunaan cache membuat perbedaan besar pada kecepatan implementasi. Lihat misalnya contoh perbandingan GPU didaktik ini .
Lihat juga:
Jaringan adalah contoh prototipe IO-terikat.
Bahkan ketika kami mengirim satu byte data, masih butuh waktu besar untuk mencapai tujuannya.
Memparalelkan permintaan jaringan kecil seperti permintaan HTTP dapat menawarkan keuntungan kinerja yang sangat besar.
Jika jaringan sudah pada kapasitas penuh (mis. Mengunduh torrent), paralelisasi masih dapat meningkatkan tingkatkan latensi (mis. Anda dapat memuat halaman web "pada saat yang sama").
Operasi dummy C ++ CPU terikat yang mengambil satu angka dan berderak banyak:
Penyortiran tampaknya merupakan CPU berdasarkan percobaan berikut: Apakah C ++ 17 Algoritma Paralel sudah diterapkan? yang menunjukkan peningkatan kinerja 4x untuk jenis paralel, tetapi saya ingin memiliki konfirmasi yang lebih teoretis juga
Cara mengetahui apakah Anda terikat CPU atau IO
IO Non-RAM terikat seperti disk, jaringan ps aux
:, lalu periksa apakah CPU% / 100 < n threads
. Jika ya, Anda terikat IO, misalnya pemblokiran read
hanya menunggu data dan penjadwal melewatkan proses itu. Kemudian gunakan alat lebih lanjut sudo iotop
untuk memutuskan IO mana yang merupakan masalah sebenarnya.
Atau, jika eksekusi cepat, dan Anda menentukan jumlah utas, Anda dapat melihatnya dengan mudah dari time
kinerja yang meningkat ketika jumlah utas meningkat untuk pekerjaan yang terikat CPU: Apa arti 'nyata', 'pengguna' dan 'sistem' di output waktu (1)?
Ikatan RAM-IO: sulit diketahui, karena waktu tunggu RAM termasuk dalam CPU%
pengukuran, lihat juga:
Beberapa opsi:
GPU
GPU memiliki hambatan IO ketika Anda pertama kali mentransfer data input dari RAM yang dapat dibaca CPU biasa ke GPU.
Oleh karena itu, GPU hanya bisa lebih baik daripada CPU untuk aplikasi yang terikat CPU.
Namun begitu data ditransfer ke GPU, ia dapat beroperasi pada byte itu lebih cepat daripada CPU, karena GPU:
memiliki lebih banyak pelokalan data daripada kebanyakan sistem CPU, sehingga data dapat diakses lebih cepat untuk beberapa core daripada yang lain
mengeksploitasi paralelisme data dan mengorbankan latensi dengan hanya melompati data apa pun yang tidak siap untuk segera dioperasikan.
Karena GPU harus beroperasi pada data input paralel besar, lebih baik lewati saja ke data berikutnya yang mungkin tersedia alih-alih menunggu data saat ini tersedia dan memblokir semua operasi lain seperti kebanyakan CPU lakukan.
Karena itu GPU dapat lebih cepat daripada CPU jika aplikasi Anda:
Pilihan desain ini awalnya menargetkan aplikasi rendering 3D, yang langkah utamanya adalah seperti yang ditunjukkan pada Apa itu shader di OpenGL dan untuk apa kita membutuhkannya?
jadi kami menyimpulkan bahwa aplikasi itu terikat CPU.
Dengan munculnya GPGPU yang dapat diprogram, kita dapat mengamati beberapa aplikasi GPGPU yang berfungsi sebagai contoh operasi terikat CPU:
Pemrosesan Gambar dengan shader GLSL?
Operasi pemrosesan gambar lokal seperti blur filter sangat paralel.
Apakah mungkin untuk membuat peta panas dari data titik pada 60 kali per detik?
Plot grafik peta panas jika fungsi yang diplotkan cukup kompleks.
https://www.youtube.com/watch?v=fE0P6H8eK4I "Dinamika Fluid Real-Time: CPU vs GPU" oleh Jesús Martín Berlanga
Memecahkan persamaan diferensial parsial seperti persamaan Navier Stokes dari dinamika fluida:
Lihat juga:
CPython Global Intepreter Lock (GIL)
Sebagai studi kasus cepat, saya ingin menunjukkan Python Global Interpreter Lock (GIL): Apa itu kunci juru bahasa global (GIL) di CPython?
Detail implementasi CPython ini mencegah banyak utas Python efisien menggunakan pekerjaan yang terikat CPU. Dokumen CPython mengatakan:
Detail implementasi CPython: Di CPython, karena Global Interpreter Lock, hanya satu utas yang dapat mengeksekusi kode Python sekaligus (walaupun pustaka yang berorientasi kinerja tertentu mungkin mengatasi batasan ini). Jika Anda ingin aplikasi Anda memanfaatkan sumber daya komputasi mesin multi-core dengan lebih baik, Anda disarankan untuk menggunakan
multiprocessing
atauconcurrent.futures.ProcessPoolExecutor
. Namun, threading masih merupakan model yang tepat jika Anda ingin menjalankan beberapa tugas yang terikat I / O secara bersamaan.
Oleh karena itu, di sini kita memiliki contoh di mana konten yang terikat CPU tidak cocok dan I / O terikat.
Ikatan CPU berarti program macet oleh CPU, atau unit pemrosesan pusat, sedangkan I / O terikat berarti program macet oleh I / O, atau input / output, seperti membaca atau menulis ke disk, jaringan, dll.
Secara umum, ketika mengoptimalkan program komputer, seseorang mencoba mencari hambatan dan menghilangkannya. Mengetahui bahwa program Anda terikat CPU membantu, sehingga orang tidak perlu mengoptimalkan hal lain.
[Dan dengan "bottleneck", maksud saya hal yang membuat program Anda menjadi lebih lambat daripada yang seharusnya.]
Cara lain untuk mengungkapkan ide yang sama:
Jika mempercepat CPU tidak mempercepat program Anda, itu mungkin I / O terikat.
Jika mempercepat I / O (mis. Menggunakan disk yang lebih cepat) tidak membantu, program Anda mungkin terikat CPU.
(Saya menggunakan "mungkin" karena Anda perlu mempertimbangkan sumber daya lain. Memori adalah salah satu contohnya.)
Ketika program Anda menunggu I / O (mis. Disk baca / tulis atau baca / tulis jaringan dll), CPU bebas untuk melakukan tugas-tugas lain bahkan jika program Anda dihentikan. Kecepatan program Anda sebagian besar akan bergantung pada seberapa cepat IO dapat terjadi, dan jika Anda ingin mempercepatnya, Anda harus mempercepat I / O.
Jika program Anda menjalankan banyak instruksi program dan tidak menunggu I / O, maka dikatakan terikat dengan CPU. Mempercepat CPU akan membuat program berjalan lebih cepat.
Dalam kedua kasus, kunci untuk mempercepat program mungkin bukan untuk mempercepat perangkat keras, tetapi untuk mengoptimalkan program untuk mengurangi jumlah IO atau CPU yang dibutuhkan, atau untuk membuatnya melakukan I / O sementara itu juga melakukan intensif CPU barang.
I / O terikat mengacu pada suatu kondisi di mana waktu yang diperlukan untuk menyelesaikan perhitungan ditentukan terutama oleh periode yang dihabiskan menunggu operasi input / output yang akan diselesaikan.
Ini adalah kebalikan dari tugas yang terikat CPU. Keadaan ini muncul ketika tingkat di mana data diminta lebih lambat daripada tingkat itu dikonsumsi atau, dengan kata lain, lebih banyak waktu dihabiskan meminta data daripada memprosesnya.
Lihat apa yang dikatakan Microsoft.
Inti dari pemrograman async adalah objek Tugas dan Tugas, yang memodelkan operasi asinkron. Mereka didukung oleh async dan menunggu kata kunci. Model ini cukup sederhana dalam banyak kasus:
Untuk kode I / O-terikat, Anda menunggu operasi yang mengembalikan Tugas atau Tugas di dalam metode async.
Untuk kode yang terikat CPU, Anda menunggu operasi yang dimulai pada utas latar belakang dengan metode Task.Run.
Kata kunci yang menunggu adalah tempat keajaiban terjadi. Ini menghasilkan kontrol untuk penelepon metode yang dilakukan menunggu, dan pada akhirnya memungkinkan UI menjadi responsif atau layanan menjadi elastis.
Contoh I / O-Bound: Mengunduh data dari layanan web
private readonly HttpClient _httpClient = new HttpClient();
downloadButton.Clicked += async (o, e) =>
{
// This line will yield control to the UI as the request
// from the web service is happening.
//
// The UI thread is now free to perform other work.
var stringData = await _httpClient.GetStringAsync(URL);
DoSomethingWithData(stringData);
};
Contoh terikat CPU: Melakukan Perhitungan untuk Game
private DamageResult CalculateDamageDone()
{
// Code omitted:
//
// Does an expensive calculation and returns
// the result of that calculation.
}
calculateButton.Clicked += async (o, e) =>
{
// This line will yield control to the UI while CalculateDamageDone()
// performs its work. The UI thread is free to perform other work.
var damageResult = await Task.Run(() => CalculateDamageDone());
DisplayDamage(damageResult);
};
Contoh di atas menunjukkan bagaimana Anda dapat menggunakan async dan menunggu untuk pekerjaan I / O-terikat dan CPU-terikat. Ini kuncinya yang dapat Anda identifikasi ketika pekerjaan yang perlu Anda lakukan adalah I / O-terikat atau CPU-terikat, karena itu dapat sangat mempengaruhi kinerja kode Anda dan berpotensi menyebabkan penyalahgunaan konstruksi tertentu.
Berikut adalah dua pertanyaan yang harus Anda tanyakan sebelum Anda menulis kode:
Apakah kode Anda akan "menunggu" untuk sesuatu, seperti data dari database?
- Jika jawaban Anda adalah "ya", maka pekerjaan Anda terikat I / O.
Apakah kode Anda akan melakukan perhitungan yang sangat mahal?
- Jika Anda menjawab "ya", maka pekerjaan Anda terikat CPU.
Jika pekerjaan yang Anda miliki terikat I / O, gunakan async dan tunggu tanpa Task.Run . Anda tidak harus menggunakan Perpustakaan Paralel Tugas. Alasan untuk ini diuraikan dalam artikel Async in Depth .
Jika pekerjaan yang Anda miliki terikat dengan CPU dan Anda peduli tentang responsif, gunakan async dan tunggu tetapi menelurkan pekerjaan di utas lain dengan Task.Run. Jika pekerjaan sesuai untuk konkurensi dan paralelisme, Anda juga harus mempertimbangkan menggunakan Perpustakaan Tugas Paralel .
Aplikasi terikat dengan CPU ketika kinerja aritmatika / logis / floating-point (A / L / FP) selama eksekusi sebagian besar mendekati kinerja puncak teoritis prosesor (data yang disediakan oleh pabrikan dan ditentukan oleh karakteristik prosesor). prosesor: jumlah inti, frekuensi, register, ALU, FPU, dll.).
Kinerja mengintip sangat sulit dicapai dalam aplikasi dunia nyata, karena tidak mungkin dikatakan mustahil. Sebagian besar aplikasi mengakses memori di berbagai bagian eksekusi dan prosesor tidak melakukan operasi A / L / FP selama beberapa siklus. Ini disebut Keterbatasan Von Neumann karena jarak yang ada antara memori dan prosesor.
Jika Anda ingin berada di dekat kinerja puncak CPU, strategi dapat mencoba untuk menggunakan kembali sebagian besar data dalam memori cache untuk menghindari memerlukan data dari memori utama. Algoritma yang mengeksploitasi fitur ini adalah perkalian matriks-matriks (jika kedua matriks dapat disimpan dalam memori cache). Ini terjadi karena jika ukuran matrik n x n
maka yang perlu Anda lakukan tentang 2 n^3
operasi hanya menggunakan 2 n^2
nomor data FP. Di sisi lain penambahan matriks, misalnya, adalah aplikasi yang lebih sedikit terikat CPU atau lebih banyak memori daripada multiplikasi matriks karena hanya membutuhkan n^2
FLOP dengan data yang sama.
Pada gambar berikut, FLOP yang diperoleh dengan algoritma naif untuk penambahan matriks dan perkalian matriks dalam Intel i5-9300H, ditunjukkan:
Perhatikan bahwa seperti yang diharapkan kinerja perkalian matriks lebih besar dari penambahan matriks. Hasil ini dapat direproduksi dengan menjalankan test/gemm
dan test/matadd
tersedia di repositori ini .
Saya sarankan juga untuk melihat video yang diberikan oleh J. Dongarra tentang efek ini.
Proses I / O Bound: - Jika sebagian besar masa hidup suatu proses dihabiskan dalam kondisi i / o, maka prosesnya adalah proses terikat a / o. Contoh: -kalkulator, penjelajah internet
Proses Terikat CPU: - Jika sebagian besar umur proses dihabiskan dalam cpu, maka itu adalah proses cpu terikat.