Misalkan saya memiliki array numpy memori yang besar, saya memiliki fungsi func
yang mengambil array raksasa ini sebagai input (bersama dengan beberapa parameter lain). func
dengan parameter yang berbeda dapat dijalankan secara paralel. Sebagai contoh:
def func(arr, param):
# do stuff to arr, param
# build array arr
pool = Pool(processes = 6)
results = [pool.apply_async(func, [arr, param]) for param in all_params]
output = [res.get() for res in results]
Jika saya menggunakan perpustakaan multiprosesing, maka array raksasa itu akan disalin beberapa kali ke dalam proses yang berbeda.
Apakah ada cara untuk membiarkan proses yang berbeda berbagi larik yang sama? Objek array ini hanya-baca dan tidak akan pernah diubah.
Apa yang lebih rumit, jika arr bukanlah sebuah array, tetapi sebuah objek python yang berubah-ubah, apakah ada cara untuk membagikannya?
[DIEDIT]
Saya membaca jawabannya tetapi saya masih agak bingung. Karena fork () adalah copy-on-write, kita tidak boleh meminta biaya tambahan saat melakukan proses baru di pustaka multiprosesing python. Tetapi kode berikut menunjukkan ada overhead yang sangat besar:
from multiprocessing import Pool, Manager
import numpy as np;
import time
def f(arr):
return len(arr)
t = time.time()
arr = np.arange(10000000)
print "construct array = ", time.time() - t;
pool = Pool(processes = 6)
t = time.time()
res = pool.apply_async(f, [arr,])
res.get()
print "multiprocessing overhead = ", time.time() - t;
output (dan omong-omong, biaya meningkat seiring dengan peningkatan ukuran array, jadi saya curiga masih ada overhead yang terkait dengan penyalinan memori):
construct array = 0.0178790092468
multiprocessing overhead = 0.252444982529
Mengapa ada overhead yang begitu besar, jika kita tidak menyalin array? Dan bagian apa dari memori bersama yang menyelamatkan saya?
Jawaban:
Jika Anda menggunakan sistem operasi yang menggunakan
fork()
semantik salin-saat-tulis (seperti unix umum lainnya), maka selama Anda tidak pernah mengubah struktur data Anda, itu akan tersedia untuk semua proses turunan tanpa menggunakan memori tambahan. Anda tidak perlu melakukan sesuatu yang khusus (kecuali pastikan Anda benar-benar tidak mengubah objek).Hal paling efisien yang dapat Anda lakukan untuk masalah Anda adalah mengemas array Anda ke dalam struktur array yang efisien (menggunakan
numpy
atauarray
), menempatkannya di memori bersama, membungkusnyamultiprocessing.Array
, dan meneruskannya ke fungsi Anda. Jawaban ini menunjukkan bagaimana melakukan itu .Jika Anda menginginkan objek bersama yang dapat ditulisi , maka Anda perlu membungkusnya dengan semacam sinkronisasi atau penguncian.
multiprocessing
menyediakan dua metode untuk melakukan ini : satu menggunakan memori bersama (cocok untuk nilai sederhana, array, atau ctypes) atauManager
proxy, di mana satu proses menyimpan memori dan manajer mengatur akses ke sana dari proses lain (bahkan melalui jaringan).The
Manager
pendekatan dapat digunakan dengan sewenang-wenang Python objek, tetapi akan lebih lambat dari setara memori bersama menggunakan karena benda perlu serial / deserialized dan dikirim antara proses.Ada banyak pustaka dan pendekatan pemrosesan paralel yang tersedia dengan Python .
multiprocessing
adalah perpustakaan yang sangat baik dan menyeluruh, tetapi jika Anda memiliki kebutuhan khusus mungkin salah satu pendekatan lain mungkin lebih baik.sumber
apply_async
harus mereferensikan objek bersama dalam cakupan secara langsung, bukan melalui argumennya.Saya mengalami masalah yang sama dan menulis sedikit kelas utilitas memori bersama untuk mengatasinya.
Saya menggunakan
multiprocessing.RawArray
(lockfree), dan juga akses ke array tidak disinkronkan sama sekali (lockfree), berhati-hatilah untuk tidak menembak kaki Anda sendiri.Dengan solusi ini saya mendapatkan percepatan dengan faktor kira-kira 3 pada quad-core i7.
Berikut kodenya: Jangan ragu untuk menggunakan dan meningkatkannya, dan laporkan kembali setiap bug.
sumber
Ini adalah kasus penggunaan yang dimaksudkan untuk Ray , yang merupakan pustaka untuk Python yang paralel dan terdistribusi. Di bawah tenda, itu membuat serial objek menggunakan tata letak data Apache Arrow (yang merupakan format nol-salinan) dan menyimpannya di penyimpanan objek memori bersama sehingga mereka dapat diakses oleh banyak proses tanpa membuat salinan.
Kode tersebut akan terlihat seperti berikut.
Jika Anda tidak memanggil
ray.put
maka array akan tetap disimpan dalam memori bersama, tetapi itu akan dilakukan sekali per pemanggilanfunc
, yang bukan itu yang Anda inginkan.Perhatikan bahwa ini akan bekerja tidak hanya untuk array tetapi juga untuk objek yang berisi array , misalnya, kamus yang memetakan int ke array seperti di bawah ini.
Anda dapat membandingkan kinerja serialisasi di Ray versus acar dengan menjalankan berikut ini di IPython.
Serialisasi dengan Ray hanya sedikit lebih cepat daripada acar, tetapi deserialisasi 1000x lebih cepat karena penggunaan memori bersama (angka ini tentu saja akan bergantung pada objeknya).
Lihat dokumentasi Ray . Anda dapat membaca lebih lanjut tentang serialisasi cepat menggunakan Ray dan Panah . Perhatikan Saya salah satu pengembang Ray.
sumber
Seperti yang disebutkan Robert Nishihara, Apache Arrow membuat ini mudah, khususnya dengan penyimpanan objek dalam memori Plasma, yang merupakan dasar dari Ray.
Saya membuat plasma otak khusus untuk alasan ini - memuat dan memuat ulang benda-benda besar dengan cepat di aplikasi Flask. Ini adalah namespace objek memori bersama untuk objek Apache Arrow-serializable, termasuk
pickle
bytestrings d yang dihasilkan olehpickle.dumps(...)
.Perbedaan utama dengan Apache Ray dan Plasma adalah ia melacak ID objek untuk Anda. Setiap proses atau utas atau program yang berjalan secara lokal dapat berbagi nilai variabel dengan memanggil nama dari
Brain
objek apa pun .sumber