Saya ingin memparalelkan program Python saya sehingga dapat menggunakan banyak prosesor pada mesin yang menjalankannya. Paralelisasi saya sangat sederhana, karena semua "utas" paralel dari program ini independen dan menulis outputnya ke file terpisah. Saya tidak memerlukan utas untuk bertukar informasi tetapi sangat penting bahwa saya tahu kapan utas selesai karena beberapa langkah pipa saya bergantung pada outputnya.
Portabilitas itu penting, karena saya ingin ini berjalan pada versi Python apa pun di Mac, Linux, dan Windows. Dengan batasan ini, modul Python mana yang paling tepat untuk mengimplementasikan ini? Saya mencoba untuk memutuskan antara utas, subproses, dan multiproses, yang semuanya tampaknya menyediakan fungsionalitas terkait.
Ada pemikiran tentang ini? Saya ingin solusi paling sederhana yang portabel.
sumber
Jawaban:
multiprocessing
adalah jenis modul pisau Swiss-army yang bagus. Ini lebih umum daripada utas, karena Anda bahkan dapat melakukan komputasi jarak jauh. Oleh karena itu, ini adalah modul yang saya sarankan Anda gunakan.The
subprocess
Modul juga akan memungkinkan Anda untuk memulai beberapa proses, tapi saya menemukan untuk menjadi kurang nyaman digunakan daripada modul multiprocessing baru.Utas terkenal halus, dan, dengan CPython, Anda sering dibatasi pada satu inti, dengan mereka (meskipun, seperti disebutkan di salah satu komentar, Kunci Penerjemah Global (GIL) dapat dirilis dalam kode C yang dipanggil dari kode Python) .
Saya yakin bahwa sebagian besar fungsi dari tiga modul yang Anda kutip dapat digunakan dengan cara yang tidak bergantung platform. Di sisi portabilitas, perhatikan bahwa
multiprocessing
hanya tersedia dalam standar sejak Python 2.6 (versi untuk beberapa versi Python yang lebih lama memang ada). Tapi itu modul yang bagus!sumber
Bagi saya ini sebenarnya cukup sederhana:
The subproses opsi:
subprocess
adalah untuk menjalankan executable lain --- pada dasarnya ini adalah pembungkusos.fork()
danos.execve()
dengan beberapa dukungan untuk pipa saluran opsional (menyiapkan PIPE ke dan dari subproses. Jelas Anda dapat mekanisme komunikasi antar proses (IPC) lainnya, seperti soket, atau Posix atau Memori bersama SysV. Tetapi Anda akan dibatasi pada antarmuka dan saluran IPC apa pun yang didukung oleh program yang Anda panggil.Umumnya, seseorang menggunakan sembarang
subprocess
- cukup memanggil beberapa utilitas eksternal dan membaca kembali outputnya atau menunggu penyelesaiannya (mungkin membaca hasilnya dari file sementara, atau setelah diposting ke beberapa database).Namun seseorang dapat menelurkan ratusan subproses dan melakukan polling. Classh utilitas favorit pribadi saya melakukan hal itu. Kerugian terbesar dari
subprocess
modul ini adalah dukungan I / O umumnya memblokir. Ada draf PEP-3145 untuk memperbaikinya di beberapa versi Python 3.x yang akan datang dan asyncproc alternatif (Peringatan yang mengarahkan hak ke unduhan, bukan ke dokumentasi atau README). Saya juga menemukan bahwa relatif mudah untuk hanya mengimporfcntl
dan memanipulasiPopen
deskriptor file PIPE Anda secara langsung --- meskipun saya tidak tahu apakah ini portabel untuk platform non-UNIX.(Pembaruan: 7 Agustus 2019: Dukungan Python 3 untuk subproses ayncio : Subproses asyncio )
subprocess
hampir tidak memiliki dukungan penanganan peristiwa ... meskipun Anda dapat menggunakansignal
modul dan sinyal UNIX / Linux jadul biasa --- mematikan proses Anda dengan lembut, seolah-olah.The multiprocessing opsi:
multiprocessing
adalah untuk menjalankan fungsi dalam kode (Python) yang ada dengan dukungan untuk komunikasi yang lebih fleksibel di antara rangkaian proses ini. Secara khusus, yang terbaik adalah membangunmultiprocessing
IPC Anda di sekitar objek modulQueue
jika memungkinkan, tetapi Anda juga dapat menggunakanEvent
objek dan berbagai fitur lainnya (beberapa di antaranya, mungkin, dibangun di sekitarmmap
dukungan pada platform di mana dukungan itu cukup).multiprocessing
Modul Python dimaksudkan untuk menyediakan antarmuka dan fitur yang sangat mirip denganthreading
sementara memungkinkan CPython untuk menskalakan pemrosesan Anda di antara banyak CPU / core meskipun ada GIL (Global Interpreter Lock). Ini memanfaatkan semua penguncian SMP dan upaya koherensi yang dilakukan oleh pengembang kernel OS Anda.The threading opsi:
threading
adalah untuk rentang aplikasi yang cukup sempit yang terikat I / O (tidak perlu diskalakan di beberapa inti CPU) dan yang diuntungkan dari latensi yang sangat rendah dan overhead switching dari peralihan thread (dengan memori inti bersama) vs. proses / pengalihan konteks. Di Linux ini hampir merupakan himpunan kosong (waktu peralihan proses Linux sangat dekat dengan sakelar utasnya).threading
menderita dua kelemahan utama dengan Python .Satu, tentu saja, adalah implementasi spesifik --- kebanyakan mempengaruhi CPython. Itulah GIL. Untuk sebagian besar, sebagian besar program CPython tidak akan mendapatkan keuntungan dari ketersediaan lebih dari dua CPU (inti) dan seringkali kinerja akan menderita karena pertentangan penguncian GIL.
Masalah yang lebih besar yang tidak spesifik untuk implementasi, adalah bahwa utas berbagi memori yang sama, penangan sinyal, deskriptor file, dan sumber daya OS tertentu lainnya. Oleh karena itu, programmer harus sangat berhati-hati tentang penguncian objek, penanganan pengecualian, dan aspek lain dari kode mereka yang tidak kentara dan dapat mematikan, menghentikan, atau menghentikan keseluruhan proses (rangkaian utas).
Sebagai perbandingan,
multiprocessing
model memberikan setiap proses memorinya sendiri, deskriptor file, dll. Sebuah error atau pengecualian yang tidak tertangani di salah satu dari mereka hanya akan mematikan sumber daya itu dan menangani hilangnya proses anak atau saudara secara kuat bisa jauh lebih mudah daripada debugging, mengisolasi dan memperbaiki atau mengatasi masalah serupa di utas.threading
dengan sistem Python utama, seperti NumPy , mungkin menderita lebih sedikit dari pertentangan GIL daripada kebanyakan kode Python Anda sendiri. Itu karena mereka telah direkayasa secara khusus untuk melakukannya; bagian asli / biner dari NumPy, misalnya, akan merilis GIL saat itu aman).The bengkok opsi:
Perlu juga dicatat bahwa Twisted menawarkan alternatif lain yang elegan dan sangat menantang untuk dipahami . Pada dasarnya, dengan risiko terlalu menyederhanakan ke titik di mana penggemar Twisted dapat menyerbu rumah saya dengan garpu rumput dan obor, Twisted menyediakan multi-tasking koperasi yang digerakkan oleh acara dalam proses (tunggal) apa pun.
Untuk memahami bagaimana hal ini dimungkinkan, seseorang harus membaca tentang fitur-fitur
select()
(yang dapat dibangun di sekitar select () atau poll () atau panggilan sistem OS serupa). Pada dasarnya itu semua didorong oleh kemampuan untuk membuat permintaan OS untuk tidur sambil menunggu aktivitas apa pun di daftar deskriptor file atau beberapa waktu tunggu.Kebangkitan dari masing-masing panggilan ini ke
select()
adalah sebuah peristiwa --- baik yang melibatkan input yang tersedia (dapat dibaca) pada sejumlah soket atau deskriptor file, atau ruang buffer menjadi tersedia pada beberapa deskriptor atau soket lain (dapat ditulis), beberapa kondisi luar biasa (TCP out-of-band PUSH'd packets, misalnya), atau TIMEOUT.Jadi, model pemrograman Twisted dibangun untuk menangani kejadian ini kemudian melakukan perulangan pada pengendali "utama" yang dihasilkan, memungkinkannya untuk mengirimkan kejadian ke penangan Anda.
Saya pribadi menganggap nama, Twisted sebagai menggugah model pemrograman ... karena pendekatan Anda terhadap masalah harus, dalam arti tertentu, "dipelintir" dari dalam ke luar. Daripada menganggap program Anda sebagai rangkaian operasi pada data masukan dan keluaran atau hasil, Anda menulis program Anda sebagai layanan atau daemon dan menentukan bagaimana reaksinya terhadap berbagai peristiwa. (Sebenarnya inti "putaran utama" dari program Twisted adalah (biasanya? Selalu?) A
reactor()
).The tantangan besar untuk menggunakan twisted melibatkan memutar pikiran Anda sekitar acara didorong Model dan juga menghindari penggunaan setiap perpustakaan kelas atau toolkit yang tidak ditulis untuk bekerja sama dalam kerangka twisted. Inilah sebabnya mengapa Twisted menyediakan modulnya sendiri untuk penanganan protokol SSH, untuk kutukan, dan subproses / fungsi Popennya sendiri, dan banyak modul dan penangan protokol lainnya yang, pada awalnya, akan terlihat menduplikasi hal-hal di pustaka standar Python.
Saya pikir itu berguna untuk memahami Twisted pada tingkat konseptual bahkan jika Anda tidak pernah berniat menggunakannya. Ini dapat memberikan wawasan tentang kinerja, perselisihan, dan penanganan acara dalam penanganan threading, multiprosesing dan bahkan subproses Anda serta setiap pemrosesan terdistribusi yang Anda lakukan.
( Catatan: Versi lebih baru dari Python 3.x termasuk fitur asyncio (asynchronous I / O) seperti async def , dekorator @ async.coroutine , dan kata kunci await , dan hasil dari dukungan di masa mendatang . Semua ini secara kasar mirip dengan Memutar dari perspektif proses (multitasking koperasi)). (Untuk status dukungan Twisted saat ini untuk Python 3, lihat: https://twistedmatrix.com/documents/current/core/howto/python3.html )
The didistribusikan opsi:
Namun bidang pemrosesan lain yang belum Anda tanyakan, tetapi yang patut dipertimbangkan, adalah pemrosesan terdistribusi . Ada banyak alat dan kerangka kerja Python untuk pemrosesan terdistribusi dan komputasi paralel. Secara pribadi saya pikir yang paling mudah digunakan adalah yang paling tidak sering dianggap ada di ruang itu.
Membangun pemrosesan terdistribusi di sekitar Redis hampir tidak mudah . Seluruh penyimpanan kunci dapat digunakan untuk menyimpan unit dan hasil kerja, Redis LIST dapat digunakan sebagai
Queue()
objek sejenis, dan dukungan PUB / SUB dapat digunakan untukEvent
penanganan serupa. Anda dapat melakukan hashing pada kunci dan nilai penggunaan Anda, yang direplikasi di seluruh cluster instans Redis yang longgar, untuk menyimpan topologi dan pemetaan token hash guna memberikan hashing dan fail-over yang konsisten untuk penskalaan di luar kapasitas instans tunggal mana pun untuk mengoordinasikan pekerja Anda dan data marshaling (acar, JSON, BSON, atau YAML) di antaranya.Tentu saja saat Anda mulai membangun skala yang lebih besar dan solusi yang lebih canggih di sekitar Redis, Anda menerapkan ulang banyak fitur yang telah diselesaikan menggunakan, Celery , Apache Spark dan Hadoop , Zookeeper , dlld , Cassandra , dan seterusnya. Semuanya memiliki modul untuk akses Python ke layanan mereka.
[Pembaruan: Beberapa sumber daya untuk dipertimbangkan jika Anda mempertimbangkan Python untuk komputasi intensif di seluruh sistem terdistribusi: IPython Parallel dan PySpark . Meskipun ini adalah sistem komputasi terdistribusi untuk tujuan umum, sistem ini sangat mudah diakses dan ilmu data subsistem dan analitik yang populer].
Kesimpulan
Di sana Anda memiliki keseluruhan alternatif pemrosesan untuk Python, dari utas tunggal, dengan panggilan sinkron sederhana ke sub-proses, kumpulan subproses yang disurvei, utas dan multiprosesing, multi-tasking koperasi yang digerakkan oleh peristiwa, dan keluar ke pemrosesan terdistribusi.
sumber
Dalam kasus serupa, saya memilih proses terpisah dan sedikit komunikasi yang diperlukan melalui soket jaringan. Ini sangat portabel dan cukup mudah dilakukan dengan python, tetapi mungkin tidak lebih sederhana (dalam kasus saya, saya juga memiliki kendala lain: komunikasi dengan proses lain yang ditulis dalam C ++).
Dalam kasus Anda, saya mungkin akan menggunakan multiproses, karena utas python, setidaknya saat menggunakan CPython, bukan utas asli. Ya, mereka adalah utas sistem asli tetapi modul C yang dipanggil dari Python mungkin atau mungkin tidak melepaskan GIL dan mengizinkan utas lain menjalankannya saat memanggil kode pemblokiran.
sumber
Untuk menggunakan banyak prosesor di CPython, satu - satunya pilihan Anda adalah
multiprocessing
modul. CPython menyimpan kunci pada internalnya ( GIL ) yang mencegah utas pada cpu lain untuk bekerja secara paralel. Themultiprocessing
Modul menciptakan proses baru (sepertisubprocess
) dan mengelola komunikasi di antara mereka.sumber
Keluar dan biarkan unix keluar untuk melakukan pekerjaan Anda:
gunakan iterpipes untuk membungkus subproses dan kemudian:
Dari situs Ted Ziuba
INPUTS_FROM_YOU | xargs -n1 -0 -P NUM ./process #NUM proses paralel
ATAU
Gnu Parallel juga akan melayani
Anda bergaul dengan GIL sementara Anda mengirim anak laki-laki ruang belakang untuk melakukan pekerjaan multicore Anda.
sumber