Pendekatan asinkron Microsoft adalah pengganti yang baik untuk tujuan paling umum dari pemrograman multithread: meningkatkan respons terhadap tugas-tugas IO.
Namun, penting untuk menyadari bahwa pendekatan asinkron tidak mampu meningkatkan kinerja sama sekali, atau meningkatkan daya tanggap sehubungan dengan tugas-tugas intensif CPU.
Multithreading untuk Responsif
Multithreading untuk responsif adalah cara tradisional untuk membuat program responsif selama tugas IO yang berat atau tugas komputasi yang berat. Anda menyimpan file di utas latar belakang, sehingga pengguna dapat melanjutkan pekerjaan mereka, tanpa harus menunggu hard drive menyelesaikan tugasnya. Utas IO sering memblokir menunggu sebagian penulisan selesai, sehingga sakelar konteks sering dilakukan.
Demikian pula, ketika melakukan perhitungan yang rumit, Anda ingin mengizinkan pengalihan konteks biasa sehingga UI dapat tetap responsif, dan pengguna tidak menganggap programnya macet.
Tujuannya di sini bukan, secara umum, untuk membuat banyak utas berjalan pada CPU yang berbeda. Alih-alih, kami hanya tertarik untuk membuat sakelar konteks terjadi antara tugas latar belakang yang telah lama berjalan dan UI, sehingga UI dapat memperbarui dan merespons pengguna saat tugas latar belakang sedang berjalan. Secara umum, UI tidak akan memakan banyak daya CPU, dan kerangka kerja threading atau OS biasanya akan memutuskan untuk menjalankannya pada CPU yang sama.
Kami benar-benar kehilangan kinerja keseluruhan karena biaya tambahan untuk pengalihan konteks, tetapi kami tidak peduli karena kinerja CPU bukanlah tujuan kami. Kami tahu bahwa kami biasanya memiliki daya CPU lebih dari yang kami butuhkan, dan tujuan kami sehubungan dengan multithreading adalah menyelesaikan tugas bagi pengguna tanpa membuang waktu pengguna.
Alternatif "Asinkron"
"Pendekatan asinkron" mengubah gambar ini dengan mengaktifkan sakelar konteks dalam satu utas. Ini menjamin bahwa semua tugas kami akan berjalan pada satu CPU, dan dapat memberikan beberapa peningkatan kinerja sederhana dalam hal pembuatan / pembersihan thread yang lebih sedikit dan lebih sedikit saklar konteks nyata antar thread.
Alih-alih membuat utas baru untuk menunggu penerimaan sumber daya jaringan (misalnya mengunduh gambar), async
metode digunakan, yang await
gambarnya tersedia, dan, sementara itu, menghasilkan metode panggilan.
Keuntungan utama di sini adalah Anda tidak perlu khawatir tentang masalah threading seperti menghindari kebuntuan, karena Anda tidak menggunakan kunci dan sinkronisasi sama sekali, dan ada sedikit pekerjaan untuk programmer mengatur utas latar belakang, dan mendapatkan kembali di utas UI saat hasilnya kembali untuk memperbarui UI dengan aman.
Saya belum melihat terlalu dalam ke detail teknis, tetapi kesan saya adalah bahwa mengelola unduhan dengan aktivitas CPU ringan sesekali menjadi tugas bukan untuk utas terpisah, melainkan sesuatu yang lebih seperti tugas pada antrian acara UI, dan ketika unduhan selesai, metode asinkron dilanjutkan dari antrian acara itu. Dengan kata lain, await
berarti sesuatu yang mirip dengan "memeriksa apakah hasil yang saya butuhkan tersedia, jika tidak, masukkan saya kembali dalam antrian tugas utas ini".
Perhatikan bahwa pendekatan ini tidak akan menyelesaikan masalah tugas intensif-CPU: tidak ada data untuk ditunggu, jadi kami tidak bisa mendapatkan sakelar konteks yang kami butuhkan tanpa membuat thread pekerja latar belakang yang sebenarnya. Tentu saja, mungkin masih nyaman untuk menggunakan metode asinkron untuk memulai utas latar belakang dan mengembalikan hasilnya, dalam program yang secara luas menggunakan pendekatan asinkron.
Multithreading untuk Kinerja
Karena Anda berbicara tentang "kinerja", saya juga ingin membahas bagaimana multithreading dapat digunakan untuk peningkatan kinerja, sesuatu yang sama sekali tidak mungkin dengan pendekatan asinkron berulir tunggal.
Ketika Anda benar-benar dalam situasi di mana Anda tidak memiliki kekuatan CPU yang cukup pada satu CPU, dan ingin menggunakan multithreading untuk kinerja, sebenarnya sering kali sulit dilakukan. Di sisi lain, jika satu CPU tidak cukup daya pemrosesan, itu juga sering merupakan satu-satunya solusi yang dapat memungkinkan program Anda untuk melakukan apa yang ingin Anda capai dalam jangka waktu yang masuk akal, yang membuat pekerjaan menjadi berharga.
Paralelisme Trivial
Tentu saja, kadang-kadang bisa mudah untuk mendapatkan speedup nyata dari multithreading.
Jika Anda memiliki sejumlah besar tugas intensif komputasi independen (yaitu, tugas yang input dan output datanya sangat kecil sehubungan dengan perhitungan yang harus dilakukan untuk menentukan hasilnya), maka Anda seringkali dapat memperoleh percepatan signifikan dengan membuat kumpulan utas (berukuran tepat berdasarkan jumlah CPU yang tersedia), dan memiliki master utas mendistribusikan pekerjaan dan mengumpulkan hasilnya.
Multithreading Praktis untuk Kinerja
Saya tidak ingin mengajukan diri terlalu banyak sebagai ahli, tetapi kesan saya adalah bahwa, secara umum, multithreading paling praktis untuk kinerja yang terjadi hari ini adalah mencari tempat dalam aplikasi yang memiliki paralelisme sepele, dan menggunakan banyak utas. untuk menuai manfaat.
Seperti halnya optimasi apa pun, biasanya lebih baik untuk mengoptimalkan setelah Anda membuat profil kinerja program Anda, dan mengidentifikasi hot spot: mudah untuk memperlambat program dengan memutuskan secara sewenang-wenang bahwa bagian ini harus berjalan di satu utas dan bagian lain, tanpa pertama-tama menentukan apakah kedua bagian mengambil sebagian besar waktu CPU.
Utas tambahan berarti lebih banyak biaya setup / teardown, dan lebih banyak konteks beralih atau lebih banyak biaya komunikasi antar-CPU. Jika itu tidak melakukan pekerjaan yang cukup untuk menebus biaya-biaya itu jika pada CPU yang terpisah, dan tidak perlu menjadi utas terpisah untuk alasan responsif, itu akan memperlambat segalanya tanpa manfaat.
Cari tugas-tugas yang memiliki sedikit saling ketergantungan, dan yang mengambil porsi signifikan dari runtime program Anda.
Jika mereka tidak memiliki saling ketergantungan, maka itu adalah kasus paralelisme sepele, Anda dapat dengan mudah mengatur masing-masing dengan utas dan menikmati manfaatnya.
Jika Anda dapat menemukan tugas dengan saling ketergantungan terbatas, sehingga penguncian dan sinkronisasi untuk bertukar informasi tidak memperlambatnya secara signifikan, maka multithreading dapat mempercepat, asalkan Anda berhati-hati untuk menghindari bahaya kebuntuan karena kesalahan logika saat menyinkronkan atau hasil yang salah karena tidak disinkronkan saat diperlukan.
Atau, beberapa aplikasi yang lebih umum untuk multithreading tidak (dalam arti) mencari speedup dari algoritma yang telah ditentukan, tetapi sebaliknya untuk anggaran yang lebih besar untuk algoritma yang mereka rencanakan untuk ditulis: jika Anda menulis mesin gim , dan AI Anda harus membuat keputusan dalam frame rate Anda, Anda sering dapat memberi AI Anda siklus siklus CPU lebih besar jika Anda bisa memberikannya CPU sendiri.
Namun, pastikan untuk membuat profil utas dan memastikan bahwa mereka melakukan cukup banyak pekerjaan untuk menebus biaya di beberapa titik.
Algoritma Paralel
Ada juga banyak masalah yang dapat dipercepat dengan menggunakan beberapa prosesor, tetapi terlalu monolitik untuk hanya membagi antara CPU.
Algoritma paralel harus dianalisis dengan cermat untuk runtime O-besar mereka sehubungan dengan algoritma non-paralel terbaik yang tersedia, karena sangat mudah untuk biaya komunikasi antar-CPU untuk menghilangkan manfaat dari menggunakan beberapa CPU. Secara umum, mereka harus menggunakan lebih sedikit komunikasi antar-CPU (dalam istilah O-besar) daripada mereka menggunakan perhitungan pada setiap CPU.
Saat ini, sebagian besar masih merupakan ruang untuk penelitian akademis, sebagian karena analisis kompleks yang diperlukan, sebagian karena paralelisme sepele cukup umum, sebagian karena kami belum memiliki begitu banyak inti CPU di komputer kami yang bermasalah yang tidak dapat diselesaikan dalam kerangka waktu yang masuk akal pada satu CPU dapat diselesaikan dalam kerangka waktu yang wajar menggunakan semua CPU kami.