Python'da bir for döngüsü paralelleştirme


35

Python'da Matlab'ın parforuna benzeyen herhangi bir araç var mı? Bu konuyu buldum ama dört yaşında. Belki de buradaki birinin daha yeni deneyime sahip olabileceğini düşündüm.

Paralelleştirmek istediğim türden bir örnek:

X = np.random.normal(size=(10, 3))
F = np.zeros((10, ))
for i in range(10):
    F[i] = my_function(X[i,:])

nerede my_functionbir alan ndarraybüyüklüğü (1,3)ve bir skalar döndürür.

En azından eşzamanlı olarak birden fazla çekirdek kullanmak istiyorum --- parfor gibi. Başka bir deyişle, 8 ila 16 çekirdekli bir paylaşılan bellek sistemi varsayalım.



Teşekkürler, @ doug-lipinski. Bu örnekleri, googling yaparken bulduğum diğerleri gibi, yineleme endeksine dayanan önemsiz hesaplamalar var. Ve her zaman kodun "inanılmaz derecede kolay" olduğunu iddia ediyorlar. Örneğim, for-loop'un dışındaki dizileri (belleği tahsis eder) tanımlar. Başka bir yolla yapıyorum; Ben de aynen Matlab’da yapıyorum. Bu örnekleri yırttıran zor kısım, belirli bir dizinin bir kısmını döngü içindeki fonksiyona almaktır.
Paul G. Constantine,

Yanıtlar:


19

Joblib ne istersen onu yapar. Temel kullanım şekli:

from joblib import Parallel, delayed

def myfun(arg):
     do_stuff
     return result

results = Parallel(n_jobs=-1, verbose=verbosity_level, backend="threading")(
             map(delayed(myfun), arg_instances))

burada arg_instanceskendisi için değerler listesi myfunparalel olarak hesaplanır. Ana kısıtlama, myfunüst seviye bir fonksiyon olması gerektiğidir. backendParametresi ya olabilir "threading"ya da "multiprocessing".

Paralelleştirilen işleve ek ortak parametreler iletebilirsiniz. 'Nin organı myfunaynı zamanda çocuklar için mevcut olacak değerlere sahip olan küresel değişkenleri de ifade edebilir.

Args ve sonuçlar, iş parçacığı arka ucunda hemen hemen her şey olabilir, ancak sonuçların, çok işlemli arka ucuyla seri hale getirilmesi gerekir.


Dask da benzer işlevler sunar. Temel verilerle çalışıyorsanız veya daha karmaşık hesaplamaları paralel hale getirmeye çalışıyorsanız, tercih edilebilir.


Çok işlemeli dahil bataryayı kullanmak için sıfır değer eklendi. Joblib'in kaputun altında kullandığına bahse girerim.
Xavier Combelle

1
Bu joblib sihirli olmadığı belirtilmelidir zorundadır threadinggelen arka uç uğrar GiL darboğaz ve multiprocessingarka uç nedeniyle tüm parametreleri ve dönüş değerleri dizgeleleştirme büyük yükü getiriyor. Python'da paralel işlemenin düşük düzey detayları için bu cevaba bakınız .
Jakub Klinkovsk

Joblib'in for-loop işleminden daha hızlı olacağı bir fonksiyon karmaşıklığı ve yineleme sayısı kombinasyonu bulamıyorum. Benim için, eğer n_jobs = 1 ise aynı hıza sahip ve diğer tüm durumlarda çok daha yavaş
Aleksejs Fomins

@AleksejsFomins İş parçacığı temelli paralellik, GIL'yi serbest bırakmayan kod için yardımcı olmaz, ancak önemli bir sayı, özellikle de veri bilimi veya sayısal kütüphaneler yapar. Aksi takdirde, karşılıklı işlemeye ihtiyacınız varsa, Jobli her ikisini de destekler. Çok işlemcili modül artık mapdoğrudan kullanabileceğinizde paraleldir . Ayrıca eğer mkl derlenmiş numpy kullanırsanız, hiçbir şey yapmadan vectorized işlemleri otomatik olarak paralelleştirir. Ananconda’daki numpy varsayılan olarak mkl’dir. Ancak evrensel bir çözüm yok. Joblib'in yaygara çok düşük ve 2015'te daha az seçenek vardı.
Daniel Mahler

Tavsiyeniz için teşekkürler. Önceden çoklu işlemeyi denediğimi ve hatta birkaç mesaj yazdığımı hatırlıyorum, çünkü beklediğim gibi ölçeklenmedi. Belki başka bir görünüm
vermeliyim

9

Aradığın şey bir for döngüsünü otomatik olarak paralelleştirebilen Numba . Onların itibaren belgelerinde

from numba import jit, prange

@jit
def parallel_sum(A):
    sum = 0.0
    for i in prange(A.shape[0]):
        sum += A[i]

    return sum

8

Bir şey special varsayarak olmadan my_functionseçeceği multiprocessing.Pool().map()böyle basit döngüler koşutlama için iyi bir tahmindir. joblib, dask, mpiHesaplamalar veya numbayararsız bağımlılıkları böyle durumlarda kullanmak için herhangi bir avantaj getiren ve eklemeyin görünüyor diğer yanıtlar önerilen gibi (Özetle onlar tarafından olduğu düşünülen). Başka bir cevapta önerildiği gibi iş parçacığını kullanmak iyi bir çözüm değildir, çünkü kodunuzun GIL etkileşimi konusunda samimi olmanız gerekir veya kodunuz çoğunlukla girdi / çıktı yapmalıdır.

Bu, numbasıralı saf python kodunu hızlandırmak için iyi bir fikir olabilir, ancak bunun sorunun kapsamı dışında olduğunu düşünüyorum.

import multiprocessing
import numpy as np

if __name__ == "__main__":
   #the previous line is necessary under windows to not execute 
   # main module on each child under windows

   X = np.random.normal(size=(10, 3))
   F = np.zeros((10, ))

   pool = multiprocessing.Pool(processes=16)
   # if number of processes is not specified, it uses the number of core
   F[:] = pool.map(my_function, (X[i,:] for i in range(10)) )

Ancak bazı uyarılar var (ancak çoğu uygulamayı etkilememelidir):

  • Pencerelerin altında çatal desteği yoktur, bu nedenle her çocuğun başlangıcında ana modüle sahip bir tercüman başlatılır, bu nedenle bir ek yükü olabilir (reklamın nedeni if __name__ == "__main__"
  • My_function işlevinin argümanları ve sonuçları toplanır ve kaldırılır, çok büyük bir yük olabilir, bunu azaltmak için bu cevaba bakın https://stackoverflow.com/a/37072511/128629 . Ayrıca seçilemeyen nesneleri kullanılamaz duruma getirir.
  • my_functionDurumlar süreçler arasında paylaşılmadığından küresel değişkenlerle iletişim kurmak gibi paylaşılan devletlere bağlı olmamalıdır. saf fonksiyonlar (matematiksel anlamda fonksiyonlar) durumları paylaşmayan fonksiyonlara örnektir

6

Parfor hakkındaki izlenimim, MATLAB'ın uygulama ayrıntılarını kapsıyor olmasıdır, bu nedenle hem paylaşılan bellek paralelliği (istediğiniz şey) hem de dağıtılmış bellek paralelliği (eğer bir MATLAB dağıtılmış hesaplama sunucusu kullanıyorsanız ) kullanıyor olabilir.

Eğer paylaşılan bellek paralelliği istiyorsanız ve bir çeşit görev paralel döngüsünü uyguluyorsanız, çok işlemli standart kütüphane paketi muhtemelen Doug'ın yazısında belirtildiği gibi, belki de joblib gibi güzel bir ön uçla istediğiniz şeydir . Standart kütüphane kaybolmayacak ve korunuyor, bu yüzden düşük riskli.

Parallel Python ve IPython'un paralel yetenekleri gibi başka seçenekler de var . Paralel Python'a kısa bir bakış, bana, kütüphanenin dağıtılmış durumun ayrıntılarını içerdiği için parfor ruhuna daha yakın olduğunu düşünmeme neden oluyor, ancak bunu yapmanın bedeli ekosistemini benimsemeniz gerektiği. IPython kullanmanın maliyeti benzerdir; IPython'u bir şey yapmanın yolunu benimsemeniz gerekir;

Dağıtılmış hafızaya önem veriyorsanız , mpi4py'yi tavsiye ederim . Lisandro Dalcin harika bir iş çıkarıyor ve mets4py PETSc Python ambalajlarında kullanılıyor, bu yüzden yakın zamanda biteceğini sanmıyorum. Çok işlemciliğe benzer şekilde, paralelliğe parforma kıyasla düşük (er) düzeyli bir arayüzdür, fakat bir süre daha sürmesi muhtemeldir.


Teşekkürler, @Geoff. Bu kütüphanelerle çalışma tecrüben var mı? Belki de paylaşılan bir bellek / multicore işlemcide mpi4py kullanmayı deneyeceğim.
Paul G. Constantine,

@PaulGConstantine mpi4py'yi başarıyla kullandım; MPI ile aşina iseniz, oldukça acısızdır. Çok işlemeyi kullanmadım, ancak kendileri için iyi çalıştığını söyleyen meslektaşlarıma önerdim. IPython'u da kullandım, ancak paralellik özellikleri değil, bu yüzden ne kadar iyi çalıştığını konuşamam.
Geoff Oxberry

1
Aron'un Supercomputing'de PyHPC kursu için hazırladığı güzel bir mpi4py dersi var: github.com/pyHPC/pyhpc-tutorial
Matt Knepley

4

Paralel "jenerik" python fonksiyonlarında çalıştırılabilecek bir "kara kutu" aracı aramadan my_function()önce, elle nasıl paralelleştirilebileceğini analiz etmeyi öneririm .

İlk olarak, my_function(v)baştaki python fordöngüsünün yürütme süresini karşılaştırın : [C] Python fordöngüleri oldukça yavaştır, bu nedenle harcanan zaman my_function()ihmal edilebilir.

>>> timeit.timeit('pass', number=1000000)
0.01692986488342285
>>> timeit.timeit('for i in range(10): pass', number=1000000)
0.47521495819091797
>>> timeit.timeit('for i in xrange(10): pass', number=1000000)
0.42337894439697266

İkincisi, basit bir vektör uygulamasının olup my_function(v)olmadığını, döngü gerektirmediğini kontrol edin :F[:] = my_vector_function(X)

(Bu iki ilk nokta oldukça önemsizdir, burada tamlıktan bahsettiysem beni affet.)

Üçüncü ve en önemli nokta, en azından CPython uygulamaları için, my_functionzamanının çoğunu küresel tercüman kilidinin içinde mi yoksa GIL dışında mı geçirdiğini kontrol etmektir . GIL dışında zaman harcanırsa, standart kütüphane modülü kullanılmalıdır. ( İşte bir örnek). BTW, biri yalnızca GIL'yi serbest bırakmak için bir C uzantısı olarak yazmayı düşünebilirdi .threadingmy_function()

Son olarak, my_function()GIL'yi serbest bırakmazsa, multiprocessingmodül kullanılabilir .

Kaynaklar: Python, Eşzamanlı Yürütme ile ilgili dokümanlar ve paralel işlemede numpy / scipy girişini yapar .


2

Julia'yı deneyebilirsin. Python'a oldukça yakın ve çok sayıda MATLAB yapısı var. Buradaki çeviri:

F = @parallel (vcat) for i in 1:10
    my_function(randn(3))
end

Bu da rastgele sayıları paralel olarak yapar ve sonuçta azaltma sırasında sonuçları birleştirir. Bu çok işlemciliği kullanır (bu nedenle addprocs(N), kullanmadan önce işlem eklemek için yapmanız gerekir ve bu aynı zamanda bu blog gönderisinde gösterildiği gibi bir HPC'deki birden fazla düğümde de çalışır ).

Bunun pmapyerine kullanabilirsiniz :

F = pmap((i)->my_function(randn(3)),1:10)

İş parçacığı paralelliği istiyorsanız, kullanabilirsiniz Threads.@threads(algoritmayı iş parçacığı güvenli hale getirdiğinizden emin olun). Julia'yı açmadan önce, JULIA_NUM_THREADS ortam değişkenini ayarlayın, sonra:

Ftmp = [Float64[] for i in Threads.nthreads()]
Threads.@threads for i in 1:10
    push!(Ftmp[Threads.threadid()],my_function(randn(3)))
end
F = vcat(Ftmp...)

Burada her bir iş parçacığı için ayrı bir dizi yapıyorum, böylece diziye eklerken çakışmazlar, ardından dizileri bir araya getirirler. Diş açma oldukça yenidir, bu yüzden şu anda iş parçacığının sadece düz kullanımı vardır, ancak iş parçacıklı kullanımda olduğu gibi dişli azaltmalarının ve haritaların ekleneceğinden eminim.


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.