multiprocessing.Pool: Apa perbedaan antara map_async dan imap?

184

Saya mencoba mempelajari cara menggunakan multiprocessingpaket Python , tetapi saya tidak mengerti perbedaan antara map_asyncdan imap. Saya perhatikan bahwa keduanya map_asyncdan imapdijalankan secara tidak sinkron. Jadi kapan saya harus menggunakan yang satu? Dan bagaimana saya harus mengambil hasil yang dikembalikan map_async?

Haruskah saya menggunakan sesuatu seperti ini?

def test():
    result = pool.map_async()
    pool.close()
    pool.join()
    return result.get()

result=test()
for i in result:
    print i
ruang angkasa
sumber

Jawaban:

492

Ada dua perbedaan utama antara imap/ imap_unordereddan map/ map_async:

  1. Cara mereka mengonsumsi iterable yang Anda berikan kepada mereka.
  2. Cara mereka mengembalikan hasilnya kepada Anda.

map mengkonsumsi iterable Anda dengan mengonversikan iterable ke daftar (dengan asumsi itu bukan daftar), memecahnya menjadi potongan-potongan, dan mengirimkan potongan-potongan itu ke proses pekerja di Pool . Memecah iterable menjadi chunks berkinerja lebih baik daripada melewatkan setiap item di iterable antara proses satu item pada suatu waktu - terutama jika iterable besar. Namun, mengubah iterable menjadi daftar untuk memotongnya dapat memiliki biaya memori yang sangat tinggi, karena seluruh daftar harus disimpan dalam memori.

imaptidak mengubah iterable yang Anda berikan menjadi daftar, juga tidak memecahnya menjadi potongan (secara default). Ini akan beralih pada elemen yang dapat diubah pada satu waktu, dan mengirimkannya masing-masing ke proses pekerja. Ini berarti Anda tidak mengambil memori hit dari mengubah seluruh iterable ke daftar, tetapi juga berarti kinerjanya lebih lambat untuk iterables besar, karena kurangnya chunking. Ini dapat dikurangi dengan memberikan chunksizeargumen yang lebih besar dari standar 1, namun.

Perbedaan utama lainnya antara imap/ imap_unordereddan map/ map_async, adalah bahwa dengan imap/ imap_unordered, Anda dapat mulai menerima hasil dari pekerja segera setelah mereka siap, daripada harus menunggu semuanya selesai. Dengan map_async, sebuah AsyncResultdikembalikan segera, tetapi Anda tidak dapat benar-benar mengambil hasil dari objek itu sampai semuanya diproses, pada titik mana ia mengembalikan daftar yang mapsama ( mapbenar-benar diimplementasikan secara internal seperti map_async(...).get()). Tidak ada cara untuk mendapatkan hasil parsial; Anda memiliki seluruh hasil, atau tidak sama sekali.

imapdan imap_unorderedkeduanya mengembalikan iterables segera. Dengan imap, hasilnya akan dihasilkan dari iterable segera setelah mereka siap, sambil tetap mempertahankan urutan input iterable. Dengan imap_unordered, hasil akan dihasilkan segera setelah siap, terlepas dari urutan input yang dapat diubah. Jadi, katakan Anda memiliki ini:

import multiprocessing
import time

def func(x):
    time.sleep(x)
    return x + 2

if __name__ == "__main__":    
    p = multiprocessing.Pool()
    start = time.time()
    for x in p.imap(func, [1,5,3]):
        print("{} (Time elapsed: {}s)".format(x, int(time.time() - start)))

Ini akan menampilkan:

3 (Time elapsed: 1s)
7 (Time elapsed: 5s)
5 (Time elapsed: 5s)

Jika Anda menggunakan p.imap_unorderedalih-alih p.imap, Anda akan melihat:

3 (Time elapsed: 1s)
5 (Time elapsed: 3s)
7 (Time elapsed: 5s)

Jika Anda menggunakan p.mapatau p.map_async().get(), Anda akan melihat:

3 (Time elapsed: 5s)
7 (Time elapsed: 5s)
5 (Time elapsed: 5s)

Jadi, alasan utama untuk menggunakan imap/ imap_unorderedlebih map_asyncadalah:

  1. Iterable Anda cukup besar sehingga mengubahnya menjadi daftar akan menyebabkan Anda kehabisan / menggunakan terlalu banyak memori.
  2. Anda ingin dapat mulai memproses hasil sebelum semuanya selesai.
dano
sumber
1
bagaimana dengan apply dan apply_async?
Harsh Daftary
10
@HarshDaftary applymengirim satu tugas ke proses pekerja, dan kemudian memblokir sampai selesai. apply_asyncmengirimkan satu tugas ke proses kerja, dan kemudian segera mengembalikan AsyncResultobjek, yang dapat digunakan untuk menunggu tugas selesai dan mengambil hasilnya. applydiimplementasikan dengan hanya meneleponapply_async(...).get()
dano
51
Itulah jenis deskripsi yang seharusnya ada dalam Pooldokumentasi resmi dan bukan yang membosankan .
mnt
@ano Saya ingin menjalankan fungsi di latar belakang tetapi saya memiliki beberapa keterbatasan sumber daya dan tidak dapat menjalankan fungsi sebanyak yang saya inginkan dan ingin mengantri eksekusi tambahan fungsi. Apakah Anda punya ide tentang bagaimana saya harus melakukan itu? Saya punya pertanyaan di sini . Bisakah Anda melihat pertanyaan saya dan melihat apakah Anda dapat memberi saya beberapa petunjuk (atau bahkan lebih baik, jawaban) tentang bagaimana saya harus melakukan itu?
Amir
1
@BallpointBen Ini akan beralih ke bagian pekerjaan berikutnya segera setelah selesai. Pemesanan ditangani kembali dalam proses induk.
dano