Python'da paralel programlama nasıl yapılır?


141

C ++ için, paralel programlama yapmak için OpenMP'yi kullanabiliriz; ancak, OpenMP Python için çalışmaz. Python programımın bazı kısımlarına paralel yapmak istersem ne yapmalıyım?

Kodun yapısı şu şekilde düşünülebilir:

solve1(A)
solve2(B)

Nerede solve1ve solve2iki bağımsız işlevi vardır. Çalışma süresini azaltmak için bu tür kodları sıra yerine paralel olarak nasıl çalıştırırım? Umarım birisi bana yardım edebilir. Şimdiden çok teşekkürler. Kod:

def solve(Q, G, n):
    i = 0
    tol = 10 ** -4

    while i < 1000:
        inneropt, partition, x = setinner(Q, G, n)
        outeropt = setouter(Q, G, n)

        if (outeropt - inneropt) / (1 + abs(outeropt) + abs(inneropt)) < tol:
            break

        node1 = partition[0]
        node2 = partition[1]

        G = updateGraph(G, node1, node2)

        if i == 999:
            print "Maximum iteration reaches"
    print inneropt

Setinner ve setouter'ın iki bağımsız fonksiyon olduğu yerler. Paralellemek istediğim yer burası ...


31
Çok işlemciliğe bir göz atın . Not: Python'un iş parçacıkları CPU'ya bağlı görevler için uygun değildir, yalnızca G / Ç bağlantılı için uygundur.
9000

4
@ 9000 +100 internets, CPU ve I / O bağımlı görevlerden bahsediyor.
Hyperboreus

@ 9000 İş parçacıkları bildiğim kadarıyla CPU bağlantılı görevler için hiç uygun değil! İşlemler, gerçek CPU'ya bağlı görevleri yaparken gitmenin yoludur.
Omar Al-Ithawi

6
@OmarIthawi: Neden, birçok CPU çekirdeğiniz varsa (şimdi her zamanki gibi) iş parçacıkları iyi çalışıyor. Daha sonra işleminiz, tüm bu çekirdekleri paralel olarak yükleyen ve aralarındaki ortak verileri dolaylı olarak paylaşan (yani, açık bir paylaşılan bellek alanına veya süreçler arası mesajlaşmaya gerek olmadan) birkaç iş parçacığı çalıştırabilir .
9000

1
@ user2134774: Evet, ikinci yorumum çok az mantıklı. Muhtemelen GIL'i serbest bırakan tek C uzantıları bundan yararlanabilir; örneğin NumPy ve Pandaların parçaları bunu yapar. Diğer durumlarda, yanlış (ama şimdi düzenleyemiyorum).
9000

Yanıtlar:


162

Çok işlem modülünü kullanabilirsiniz . Bu durumda bir işleme havuzu kullanabilirsiniz:

from multiprocessing import Pool
pool = Pool()
result1 = pool.apply_async(solve1, [A])    # evaluate "solve1(A)" asynchronously
result2 = pool.apply_async(solve2, [B])    # evaluate "solve2(B)" asynchronously
answer1 = result1.get(timeout=10)
answer2 = result2.get(timeout=10)

Bu, sizin için genel işler yapabilen süreçleri ortaya çıkaracaktır. Biz geçemedi beri processes, bu makinedeki her işlemci çekirdeği için bir süreç doğacaktır. Her CPU çekirdeği aynı anda bir işlemi yürütebilir.

Bir listeyi tek bir işlevle eşlemek istiyorsanız bunu yaparsınız:

args = [A, B]
results = pool.map(solve1, args)

GIL , python nesneleri üzerindeki işlemleri kilitlediğinden , iş parçacığı kullanmayın .


1
yapar pool.mapda args olarak sözlükleri kabul? Yoksa sadece basit listeler?
The Bndr

Sadece bence listeler. Ama sadece anahtar değer tuples bir listesi olacak dict.items () iletebilirsiniz
Matt Williamson

Ne yazık ki bu `` paylaşılamaz bir tür '' ile bitiyor: 'list' 'hatası
Bndr

son yorumuma ek olarak: `dict.items ()` işe yarar. Hata artar, çünkü süreç-funktion değişken anlayışının işleyişini değiştirmek zorunda kaldım. Ne yazık ki hata mesajı çok yardımcı olmadı ... Yani: ipucunuz için teşekkür ederim. :-)
The Bndr

2
Burada zaman aşımı nedir?
gama

26

Bu Ray ile çok zarif bir şekilde yapılabilir .

Örneğinizi paralel hale getirmek için, işlevlerinizi @ray.remotedekoratör ile tanımlamanız ve ardından bunları çağırmanız gerekir .remote.

import ray

ray.init()

# Define the functions.

@ray.remote
def solve1(a):
    return 1

@ray.remote
def solve2(b):
    return 2

# Start two tasks in the background.
x_id = solve1.remote(0)
y_id = solve2.remote(1)

# Block until the tasks are done and get the results.
x, y = ray.get([x_id, y_id])

Bunun çok işlem modülüne göre bir takım avantajları vardır .

  1. Aynı kod, çok çekirdekli bir makinenin yanı sıra bir makine kümesinde de çalışacaktır.
  2. İşlemler, paylaşılan bellek ve sıfır kopya serileştirme yoluyla verileri verimli bir şekilde paylaşır .
  3. Hata mesajları düzgün bir şekilde yayılır.
  4. Bu işlev çağrıları birlikte oluşturulabilir, örn.

    @ray.remote
    def f(x):
        return x + 1
    
    x_id = f.remote(1)
    y_id = f.remote(x_id)
    z_id = f.remote(y_id)
    ray.get(z_id)  # returns 4
  5. İşlevleri uzaktan çağırmanın yanı sıra, sınıflar aktörler olarak uzaktan başlatılabilir .

Ray'in gelişmeye yardım ettiğim bir çerçeve olduğunu unutmayın .


Ben python paketi yüklemeye çalışırken "gereksinimi ışını karşılayan bir sürüm bulunamadı (sürümlerden: ray için eşleşen bir dağıtım bulunamadı" yazan bir hata alıyorum
alwaysaskingquestions

2
Genellikle bu tür bir hata yükseltme yapmanız gerektiği anlamına gelir pip. Denemenizi öneririm pip install --upgrade pip. Eğer kullanım gerekiyorsa sudohiç o sürümü olması mümkün pipyüklemek kullanmakta olduğunuz rayyükseltilmiş oluyor aynı değil. İle kontrol edebilirsiniz pip --version. Ayrıca, Windows şu anda desteklenmemektedir, bu nedenle Windows'taysanız muhtemelen sorun budur.
Robert Nishihara

1
Bunun, esas olarak, eşzamanlı işleri birden fazla makineye dağıtmak için olduğunu unutmayın.
Matt Williamson

2
Aslında hem tekli makine kasası hem de küme ayarı için optimize edilmiştir. Birçok tasarım kararı (örneğin, paylaşılan bellek, sıfır kopya serileştirme) tek makinelerin iyi desteklenmesini hedeflemektedir.
Robert Nishihara

2
Dokümanlar bunu daha fazla işaret ettiyse harika olurdu. Belgeleri tek bir makine kasası için tasarlanmadığını belgelere okudum.
Kızak


4

Diğerlerinin de söylediği gibi çözüm, birden çok işlem kullanmaktır. Bununla birlikte, hangi çerçevenin daha uygun olduğu birçok faktöre bağlıdır. Daha önce bahsedilenlere ek olarak, charm4py ve mpi4py da var (ben charm4py geliştiricisiyim).

Yukarıdaki örneği uygulamanın işçi havuzu soyutlamasını kullanmaktan daha etkili bir yolu vardır. Ana döngü G, 1000 yinelemenin her birinde çalışanlara aynı parametreleri (tam grafik dahil ) tekrar tekrar gönderir . En az bir işçi farklı bir süreçte ikamet edeceğinden, bu, argümanları kopyalayıp diğer süreçlere göndermeyi içerir. Bu, nesnelerin boyutuna bağlı olarak çok maliyetli olabilir. Bunun yerine, çalışanların devleti depolaması ve güncellenen bilgileri göndermesi mantıklıdır.

Örneğin, charm4py'de bu şu şekilde yapılabilir:

class Worker(Chare):

    def __init__(self, Q, G, n):
        self.G = G
        ...

    def setinner(self, node1, node2):
        self.updateGraph(node1, node2)
        ...


def solve(Q, G, n):
    # create 2 workers, each on a different process, passing the initial state
    worker_a = Chare(Worker, onPE=0, args=[Q, G, n])
    worker_b = Chare(Worker, onPE=1, args=[Q, G, n])
    while i < 1000:
        result_a = worker_a.setinner(node1, node2, ret=True)  # execute setinner on worker A
        result_b = worker_b.setouter(node1, node2, ret=True)  # execute setouter on worker B

        inneropt, partition, x = result_a.get()  # wait for result from worker A
        outeropt = result_b.get()  # wait for result from worker B
        ...

Bu örnek için gerçekten sadece bir çalışana ihtiyacımız olduğunu unutmayın. Ana döngü, işlevlerden birini yürütebilir ve çalışanın diğerini yürütmesini sağlayabilir. Ancak kodum birkaç şeyi göstermeye yardımcı olur:

  1. A İşçisi 0 işleminde çalışır (ana döngü ile aynı). İken result_a.get()sonucu bekleyen örtülmüşse, işçi bir aynı süreçte hesaplama yapar.
  2. Bağımsız değişkenler aynı işlemde olduğu için A işçisine atıfta bulunularak otomatik olarak iletilir (herhangi bir kopya yoktur).

2

Bazı durumlarda, Numba'yı kullanarak döngüler otomatik olarak paralelleştirilebilir , ancak yalnızca küçük bir Python alt kümesiyle çalışır:

from numba import njit, prange

@njit(parallel=True)
def prange_test(A):
    s = 0
    # Without "parallel=True" in the jit-decorator
    # the prange statement is equivalent to range
    for i in prange(A.shape[0]):
        s += A[i]
    return s

Ne yazık ki, Numba sadece Numpy dizileriyle çalışıyor, ancak diğer Python nesneleriyle çalışmıyor gibi görünüyor. Teoride, Python'u C ++ ' a derlemek ve daha sonra Intel C ++ derleyicisini kullanarak otomatik olarak paralelleştirmek de mümkün olabilir , ancak henüz denemedim.


2

joblibParalel hesaplama ve çoklu işlem yapmak için kitaplığı kullanabilirsiniz .

from joblib import Parallel, delayed

fooParalel olarak ve aşağıdaki kod gerçekleştirme paralel işleme parçasına dayalı olarak çalıştırmak istediğiniz bir işlev oluşturabilirsiniz:

output = Parallel(n_jobs=num_cores)(delayed(foo)(i) for i in input)

num_coresAşağıdaki gibi multiprocessingkütüphaneden nereden temin edilebilir :

import multiprocessing

num_cores = multiprocessing.cpu_count()

Birden fazla giriş bağımsız değişkenine sahip bir işleviniz varsa ve yalnızca bir listeden bağımsız değişkenlerden birini yinelemek istiyorsanız, partialişlevi functoolskitaplıktan aşağıdaki gibi kullanabilirsiniz :

from joblib import Parallel, delayed
import multiprocessing
from functools import partial
def foo(arg1, arg2, arg3, arg4):
    '''
    body of the function
    '''
    return output
input = [11,32,44,55,23,0,100,...] # arbitrary list
num_cores = multiprocessing.cpu_count()
foo_ = partial(foo, arg2=arg2, arg3=arg3, arg4=arg4)
# arg1 is being fetched from input list
output = Parallel(n_jobs=num_cores)(delayed(foo_)(i) for i in input)

Burada birkaç örnekle python ve R çoklu işleminin tam bir açıklamasını bulabilirsiniz .

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.