पायथन में समानांतर प्रोग्रामिंग कैसे करें?


141

C ++ के लिए, हम OpenMP का उपयोग समानांतर प्रोग्रामिंग करने के लिए कर सकते हैं; हालाँकि, OpenMP पायथन के लिए काम नहीं करेगा। यदि मैं अपने अजगर कार्यक्रम के कुछ हिस्सों को समानांतर करना चाहता हूं तो मुझे क्या करना चाहिए?

कोड की संरचना इस प्रकार मानी जा सकती है:

solve1(A)
solve2(B)

कहाँ solve1और solve2दो स्वतंत्र कार्य हैं। चल रहे समय को कम करने के लिए अनुक्रम में इस तरह के कोड को समानांतर में कैसे चलाया जाए? उम्मीद है कि कोई मेरी मदद करे। अग्रिम में ही बहुत शुक्रिया। कोड है:

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

जहां सेटिनर और सेटबोर्ड दो स्वतंत्र कार्य हैं। यही वह जगह है जहाँ मैं समानांतर होना चाहता हूँ ...


31
मल्टीप्रोसेसिंग पर एक नजर । नोट: पायथन के धागे सीपीयू-बाउंड कार्यों के लिए उपयुक्त नहीं हैं, केवल आई / ओ-बाउंड के लिए।
9000

4
सीपीयू बनाम I / O आश्रित कार्यों के लिए @ 9000 +100 इंटर्नशिप।
हाइपरबोरस

@ 9000 वास्तव में जहां तक ​​मुझे पता है सीपीयू-बाउंड कार्य के लिए धागे उपयुक्त नहीं हैं! प्रक्रियाएं वास्तविक सीपीयू-बाउंड कार्यों को करते समय जाने का तरीका है।
उमर अल-इत्तिवी

6
@OmarIthawi: क्यों, अगर आपके पास कई सीपीयू कोर (हमेशा की तरह) हैं तो धागे ठीक काम करते हैं। फिर आपकी प्रक्रिया इन सभी कोर को लोड करने वाले कई थ्रेड्स को समानांतर रूप से चला सकती है और उनके बीच सामान्य डेटा साझा कर सकती है (अर्थात, स्पष्ट साझा मेमोरी क्षेत्र या अंतर-प्रक्रिया संदेश के बिना)।
9000

1
@ user2134774: ठीक है, हाँ, मेरी दूसरी टिप्पणी थोड़ी समझ में आती है। संभवतः GIL को जारी करने वाले एकमात्र C एक्सटेंशन से लाभ हो सकता है; जैसे कि NumPy और Pandas के कुछ भाग। अन्य मामलों में, यह गलत है (लेकिन मैं इसे अभी संपादित नहीं कर सकता)।
9000

जवाबों:


162

आप मल्टीप्रोसेसिंग मॉड्यूल का उपयोग कर सकते हैं । इस मामले के लिए मैं एक प्रोसेसिंग पूल का उपयोग कर सकता हूं:

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)

यह प्रक्रियाएं पैदा करेगा जो आपके लिए सामान्य कार्य कर सकते हैं। चूंकि हम पास नहीं हुए थे processes, इसलिए यह आपकी मशीन पर प्रत्येक सीपीयू कोर के लिए एक प्रक्रिया शुरू करेगा। प्रत्येक CPU कोर एक प्रक्रिया को एक साथ निष्पादित कर सकता है।

यदि आप किसी एकल कार्य के लिए एक सूची बनाना चाहते हैं, तो आप ऐसा करेंगे:

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

धागे का उपयोग न करें क्योंकि GIL अजगर वस्तुओं पर किसी भी संचालन को बंद कर देता है।


1
यह pool.mapभी शब्दकोष के रूप में शब्दकोशों को स्वीकार करता है? या केवल सरल सूचियाँ?
बंड्र

बस सूची मुझे लगता है। लेकिन आप बस डिक्टेट में पास कर सकते हैं। () जो कि मुख्य मूल्य ट्यूपल्स की सूची होगी
मैट विलियमसन

दुर्भाग्यवश यह एक 'अनहैस्टेबल टाइप:' लिस्ट 'एरर
द बंड्र

मेरी अंतिम टिप्पणी के अलावा: `तानाशाही ()` काम। त्रुटि उठती है, क्योंकि मुझे प्रक्रिया-फ़ंक्शनल चर चर की हैंडलिंग को बदलना पड़ा। दुर्भाग्य से त्रुटि-संदेश बहुत उपयोगी नहीं था ... इसलिए: आपके संकेत के लिए धन्यवाद। :-)
द बंड्र

2
यहाँ क्या है?
गामा

26

यह बहुत ही शान से रे के साथ किया जा सकता है ।

अपने उदाहरण को समानांतर करने के लिए, आपको अपने कार्यों को @ray.remoteडेकोरेटर के साथ परिभाषित करने की आवश्यकता होगी , और फिर उन्हें लागू करना होगा .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])

मल्टीप्रोसेसिंग मॉड्यूल पर इसके कई फायदे हैं ।

  1. एक ही कोड एक मल्टीकोर मशीन के साथ-साथ मशीनों के एक क्लस्टर पर चलेगा।
  2. प्रक्रियाओं साझा स्मृति और शून्य-प्रतिलिपि क्रमांकन के माध्यम से कुशलतापूर्वक डेटा साझा करते हैं
  3. त्रुटि संदेशों को अच्छी तरह से प्रचारित किया जाता है।
  4. ये फंक्शन कॉल्स एक साथ रची जा सकती हैं, जैसे,

    @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. दूरस्थ रूप से कार्य करने के अलावा, अभिनेताओं के रूप में कक्षाओं को दूरस्थ रूप से त्वरित किया जा सकता है ।

ध्यान दें कि रे एक ढांचा है जिसे मैं विकसित करने में मदद कर रहा हूं।


मुझे एक त्रुटि मिलती है जो कहती है "ऐसा संस्करण नहीं मिल सकता है जो आवश्यकता किरण को संतुष्ट करता हो (संस्करणों से
:)

2
आमतौर पर इस तरह की त्रुटि का मतलब है कि आपको अपग्रेड करने की आवश्यकता है pip। मैं कोशिश कर सुझाव देंगे pip install --upgrade pip। यदि आपको sudoबिल्कुल उपयोग करने की आवश्यकता है तो यह संभव है pipकि आप जिस संस्करण को स्थापित करने के लिए उपयोग कर रहे हैं rayवह वही नहीं है जो अपग्रेड हो रहा है। आप के साथ जाँच कर सकते हैं pip --version। इसके अलावा, विंडोज वर्तमान में समर्थित नहीं है, यदि आप विंडोज पर हैं तो शायद यही समस्या है।
रॉबर्ट निशिहारा

1
बस एक नोट यह मुख्य रूप से कई मशीनों पर समवर्ती नौकरियों को वितरित करने के लिए है।
मैट विलियम्सन

2
यह वास्तव में सिंगल-मशीन केस और क्लस्टर सेटिंग दोनों के लिए अनुकूलित है। बहुत सारे डिजाइन निर्णय (जैसे, साझा की गई मेमोरी, शून्य-प्रतिलिपि क्रमांकन) अच्छी तरह से एकल मशीनों का समर्थन करने पर लक्षित होते हैं।
रॉबर्ट निशिहारा

2
यह बहुत अच्छा होगा यदि डॉक्स ने इंगित किया कि अधिक। डॉक्स पर पढ़ने से मुझे समझ में आया कि यह वास्तव में सिंगल मशीन के मामले में नहीं था।
स्लेज

4

CPython ग्लोबल इंटरप्रेटर लॉक का उपयोग करता है जो C ++ की तुलना में समानांतर प्रोग्रामिंग को थोड़ा और दिलचस्प बनाता है

इस विषय में चुनौती के कई उपयोगी उदाहरण और वर्णन हैं:

पायथन ग्लोबल इंटरप्रेटर लॉक (जीआईएल) लिनक्स पर कार्यपत्रक का उपयोग करते हुए मल्टी-कोर सिस्टम पर वर्कअराउंड?


13
आप वास्तव में समवर्ती "दिलचस्प" कोड चलाने की असमर्थता कहते हैं? : - /
मैनुएलशीन 3

4

समाधान, जैसा कि दूसरों ने कहा है, कई प्रक्रियाओं का उपयोग करना है। हालांकि, कौन सा ढांचा अधिक उपयुक्त है, कई कारकों पर निर्भर करता है। पहले से ही उल्लेख किए गए लोगों के अलावा, वहाँ भी है चार्म 4py और mpi4py (मैं चार्म 4py का डेवलपर हूं)।

वर्कर पूल एब्स्ट्रैक्शन का उपयोग करने की तुलना में उपरोक्त उदाहरण को लागू करने के लिए एक अधिक कुशल तरीका है। मुख्य लूप G1000 पुनरावृत्तियों में से प्रत्येक में श्रमिकों के लिए एक ही पैरामीटर (पूर्ण ग्राफ़ सहित ) भेजता है । चूंकि कम से कम एक कार्यकर्ता एक अलग प्रक्रिया पर निवास करेगा, इसमें अन्य प्रक्रिया में तर्कों को कॉपी करना और भेजना शामिल है। यह वस्तुओं के आकार के आधार पर बहुत महंगा हो सकता है। इसके बजाय, यह समझ में आता है कि श्रमिकों के पास राज्य है और बस अद्यतन जानकारी भेजें।

उदाहरण के लिए, Charm4py में इसे इस तरह किया जा सकता है:

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
        ...

ध्यान दें कि इस उदाहरण के लिए हमें वास्तव में केवल एक कार्यकर्ता की आवश्यकता है। मुख्य लूप एक कार्य को निष्पादित कर सकता है, और कार्यकर्ता दूसरे को निष्पादित कर सकता है। लेकिन मेरा कोड कुछ चीजों को समझने में मदद करता है:

  1. कार्यकर्ता ए 0 प्रक्रिया में चलता है (मुख्य लूप के समान)। जबकि result_a.get()परिणाम पर प्रतीक्षा अवरुद्ध है, कार्यकर्ता ए उसी प्रक्रिया में गणना करता है।
  2. कार्यकर्ता ए के संदर्भ में तर्क स्वचालित रूप से पारित हो जाते हैं, क्योंकि यह एक ही प्रक्रिया में है (इसमें कोई नकल शामिल नहीं है)।

2

कुछ मामलों में, नंबा का उपयोग करके स्वचालित रूप से छोरों को समानांतर करना संभव है , हालांकि यह केवल पायथन के एक छोटे उपसमूह के साथ काम करता है:

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

दुर्भाग्य से, ऐसा लगता है कि Numba केवल Numpy सरणियों के साथ काम करता है, लेकिन अन्य पायथन वस्तुओं के साथ नहीं। सिद्धांत रूप में, पायथन को C ++ में संकलित करना संभव हो सकता है और फिर Intel C ++ कंपाइलर का उपयोग करके इसे स्वचालित रूप से समानांतर किया जा सकता है , हालांकि मैंने अभी तक इसकी कोशिश नहीं की है।


2

joblibसमानांतर गणना और मल्टीप्रोसेसिंग करने के लिए आप लाइब्रेरी का उपयोग कर सकते हैं ।

from joblib import Parallel, delayed

आप बस एक फ़ंक्शन बना सकते हैं fooजिसे आप समानांतर में चलाना चाहते हैं और कोड लागू समानांतर प्रसंस्करण के निम्नलिखित टुकड़े पर आधारित है:

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

पुस्तकालय num_coresसे कहाँ प्राप्त किया जा सकता है multiprocessing:

import multiprocessing

num_cores = multiprocessing.cpu_count()

यदि आपके पास एक से अधिक इनपुट तर्क के साथ एक फ़ंक्शन है, और आप केवल एक सूची द्वारा तर्कों में से एक पर पुनरावृति करना चाहते हैं, तो आप partialफ़ंक्शन functoolsका अनुसरण पुस्तकालय से निम्नानुसार कर सकते हैं :

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)

आप यहां पर कुछ उदाहरणों के साथ अजगर और आर मल्टीप्रोसेसिंग की पूरी व्याख्या पा सकते हैं ।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.