खोज…


pmap

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

addprocs(3)
sqrts = pmap(sqrt, 1:10)

यदि आप कई तर्क देते हैं, तो आप pmap को कई वैक्टर की आपूर्ति कर सकते हैं

dots = pmap(dot, 1:10, 11:20)

जैसा कि @parallel साथ है, हालांकि, अगर pmap को दिया गया pmap बेस जूलिया में नहीं है (यानी यह यूज़र-डिफ़ाइंड या पैकेज में डिफाइन है) तो आपको यह सुनिश्चित करना होगा कि फंक्शन पहले सभी वर्कर्स के लिए उपलब्ध है:

@everywhere begin
    function rand_det(n)
        det(rand(n,n))
    end
end

determinants = pmap(rand_det, 1:10)

यह भी देखें इस अतः क्यू एंड ए।

@parallel

@ समानांतर का उपयोग लूप को समानांतर बनाने के लिए किया जा सकता है, लूप के चरणों को विभिन्न श्रमिकों पर विभाजित किया जा सकता है। एक बहुत ही सरल उदाहरण के रूप में:

addprocs(3)

a = collect(1:10)

for idx = 1:10
    println(a[idx])
end

थोड़ा और जटिल उदाहरण के लिए, विचार करें:

@time begin
    @sync begin
        @parallel for idx in 1:length(a)
            sleep(a[idx])
        end
    end
end
27.023411 seconds (13.48 k allocations: 762.532 KB)
julia> sum(a)
55

इस प्रकार, हम देखते हैं कि अगर हमने @parallel बिना इस लूप को @parallel किया होता, तो इसे निष्पादित करने के लिए 27 के बजाय 55 सेकंड लगते।

हम @parallel मैक्रो के लिए एक कमी ऑपरेटर भी आपूर्ति कर सकते हैं। मान लीजिए कि हमारे पास एक सरणी है, हम सरणी के प्रत्येक कॉलम को योग करना चाहते हैं और फिर इन योगों को एक दूसरे से गुणा करें:

A = rand(100,100);

@parallel (*) for idx = 1:size(A,1)
    sum(A[:,idx])
end

अप्रत्याशित व्यवहार से बचने के लिए @parallel का उपयोग करते समय कई महत्वपूर्ण बातों को ध्यान में रखना चाहिए।

पहला: यदि आप अपने फंक्शंस में ऐसे किसी फ़ंक्शंस का उपयोग करना चाहते हैं जो बेस जूलिया में नहीं हैं (जैसे या तो फ़ंक्शंस जो आप अपनी स्क्रिप्ट में परिभाषित करते हैं या जिसे आप पैकेजों से आयात करते हैं), तो आपको उन फ़ंक्शंस को श्रमिकों के लिए सुलभ बनाना होगा। इस प्रकार, उदाहरण के लिए, निम्नलिखित काम नहीं करेगा:

myprint(x) = println(x)
for idx = 1:10
    myprint(a[idx])
end

इसके बजाय, हमें उपयोग करने की आवश्यकता होगी:

@everywhere begin
    function myprint(x) 
        println(x)
    end
end

@parallel for idx in 1:length(a)
    myprint(a[idx])
end

दूसरा हालांकि प्रत्येक कार्यकर्ता नियंत्रक के दायरे में वस्तुओं का उपयोग करने में सक्षम होगा, वे उन्हें संशोधित करने में सक्षम नहीं होंगे। इस प्रकार

a = collect(1:10)
@parallel for idx = 1:length(a)
   a[idx] += 1
end

julia> a'
1x10 Array{Int64,2}:
 1  2  3  4  5  6  7  8  9  10

जबकि, अगर हमने लूप को अंजाम दिया होता @ @ समानांतर के रूप में, तो यह सफलतापूर्वक सरणी a संशोधित करता।

इस पते पर, हम बजाय कर सकते हैं a एक SharedArray ताकि प्रत्येक कार्यकर्ता एक्सेस कर सकते हैं और इसे संशोधित प्रकार वस्तु:

a = convert(SharedArray{Float64,1}, collect(1:10))
@parallel for idx = 1:length(a)
    a[idx] += 1
end

julia> a'
1x10 Array{Float64,2}:
 2.0  3.0  4.0  5.0  6.0  7.0  8.0  9.0  10.0  11.0

@spawn और @spawnat

@spawn और @spawnat दो उपकरण हैं जो जूलिया श्रमिकों को काम सौंपने के लिए उपलब्ध @spawnat हैं। यहाँ एक उदाहरण है:

julia> @spawnat 2 println("hello world")
RemoteRef{Channel{Any}}(2,1,3)

julia>  From worker 2:  hello world

ये दोनों मैक्रोज़ एक कार्यकर्ता प्रक्रिया पर एक अभिव्यक्ति का मूल्यांकन करेंगे। दोनों के बीच एकमात्र अंतर यह है कि @spawnat आपको यह चुनने की अनुमति देता है कि @spawnat सा कार्यकर्ता अभिव्यक्ति का मूल्यांकन करेगा (उदाहरण के लिए कार्यकर्ता 2 से ऊपर निर्दिष्ट किया गया है) जबकि @spawn साथ एक कार्यकर्ता उपलब्धता के आधार पर स्वचालित रूप से चुना जाएगा।

उपरोक्त उदाहरण में, हमारे पास केवल कार्यकर्ता 2 था जो प्रिंटलाइन फ़ंक्शन को निष्पादित करता है। इसमें से लौटने या पुनः प्राप्त करने के लिए कोई दिलचस्पी नहीं थी। अक्सर, हालांकि, जो अभिव्यक्ति हमने कार्यकर्ता को भेजी है, वह हमें प्राप्त करने की इच्छा के साथ कुछ देगी। उपरोक्त उदाहरण में सूचना, जब हमने @spawnat को @spawnat , इससे पहले कि हम कार्यकर्ता 2 से प्रिंटआउट लें, हमने निम्नलिखित देखा:

RemoteRef{Channel{Any}}(2,1,3)

यह इंगित करता है कि @spawnat मैक्रो RemoteRef प्रकार की वस्तु लौटाएगा। बदले में इस ऑब्जेक्ट में हमारी अभिव्यक्ति से रिटर्न मान शामिल होंगे जो कार्यकर्ता को भेजे जाते हैं। यदि हम उन मानों को पुनः प्राप्त करना चाहते हैं, तो हम पहले RemoteRef असाइन कर सकते हैं कि @spawnat किसी ऑब्जेक्ट पर लौटता है और फिर, और fetch() जो fetch() RemoteRef टाइप वस्तु पर संचालित होने वाले फ़ंक्शन का उपयोग करता है, एक मूल्यांकन से संग्रहीत परिणामों को प्राप्त करने के लिए। एक कर्मचारी।

julia> result = @spawnat 2 2 + 5
RemoteRef{Channel{Any}}(2,1,26)

julia> fetch(result)
7

@spawn प्रभावी ढंग से उपयोग करने में सक्षम होने के कारण यह उन अभिव्यक्तियों के पीछे की प्रकृति को समझ रहा है जो इसे संचालित करती हैं। श्रमिकों को कमांड भेजने के लिए @spawn का उपयोग करना सीधे टाइपिंग की तुलना में थोड़ा अधिक जटिल है कि आप क्या टाइप करेंगे यदि आप श्रमिकों में से एक पर "दुभाषिया" चला रहे थे या उन पर मूल रूप से कोड निष्पादित कर रहे थे। उदाहरण के लिए, मान लीजिए कि हमने किसी वर्कर पर वैरिएबल को मान देने के लिए @spawnat का उपयोग करना चाहा है। हम कोशिश कर सकते हैं:

@spawnat 2 a = 5
RemoteRef{Channel{Any}}(2,1,2)

काम किया? ठीक है, मुद्रित करने के लिए होने कार्यकर्ता 2 कोशिश से देखते हैं a

julia> @spawnat 2 println(a)
RemoteRef{Channel{Any}}(2,1,4)

julia> 

कुछ नहीं हुआ। क्यों? हम ऊपर के रूप में fetch() का उपयोग करके इसकी अधिक जांच कर सकते हैं। fetch() बहुत आसान हो सकता है क्योंकि यह न केवल सफल परिणाम प्राप्त करेगा, बल्कि त्रुटि संदेश भी देगा। इसके बिना, हम यह भी नहीं जानते होंगे कि कुछ गलत हो गया है।

julia> result = @spawnat 2 println(a)
RemoteRef{Channel{Any}}(2,1,5)

julia> fetch(result)
ERROR: On worker 2:
UndefVarError: a not defined

त्रुटि संदेश का कहना है कि a कार्यकर्ता पर परिभाषित नहीं है 2. लेकिन ऐसा क्यों है? कारण यह है कि हमें अपने असाइनमेंट ऑपरेशन को एक अभिव्यक्ति में लपेटने की आवश्यकता है जो हम कार्यकर्ता का मूल्यांकन करने के लिए बताने के लिए @spawn का उपयोग करते हैं। नीचे एक उदाहरण है, निम्नलिखित स्पष्टीकरण के साथ:

julia> @spawnat 2 eval(:(a = 2))
RemoteRef{Channel{Any}}(2,1,7)

julia> @spawnat 2 println(a)
RemoteRef{Channel{Any}}(2,1,8)

julia>  From worker 2:  2

:() सिंटैक्स वह है जो जूलिया अभिव्यक्ति को नामित करने के लिए उपयोग करता है। हम जूलिया में eval() फ़ंक्शन का उपयोग करते हैं, जो एक अभिव्यक्ति का मूल्यांकन करता है, और हम @spawnat मैक्रो का उपयोग करने के लिए निर्देश देते हैं कि अभिव्यक्ति का मूल्यांकन कार्यकर्ता 2 पर किया जाए।

हम भी उसी परिणाम को प्राप्त कर सकते हैं:

julia> @spawnat(2, eval(parse("c = 5")))
RemoteRef{Channel{Any}}(2,1,9)

julia> @spawnat 2 println(c)
RemoteRef{Channel{Any}}(2,1,10)

julia>  From worker 2:  5

यह उदाहरण दो अतिरिक्त धारणाओं को प्रदर्शित करता है। सबसे पहले, हम देखते हैं कि हम स्ट्रिंग पर बुलाए गए parse() फ़ंक्शन का उपयोग करके एक अभिव्यक्ति भी बना सकते हैं। दूसरे, हम देखते हैं कि हम उन स्थितियों में जहाँ यह हमारे वाक्यविन्यास को अधिक स्पष्ट और प्रबंधनीय बना सकते हैं, @spawnat कॉल करते समय कोष्ठक का उपयोग कर सकते हैं।

जब @ अपरेल बनाम pmap का उपयोग करना है

जूलिया प्रलेखन सलाह देता है कि

pmap () उस मामले के लिए डिज़ाइन किया गया है जहां प्रत्येक फ़ंक्शन कॉल बड़ी मात्रा में काम करता है। इसके विपरीत, @ समानांतर उन स्थितियों को संभाल सकता है जहां प्रत्येक पुनरावृत्ति छोटी होती है, शायद केवल दो संख्याओं को जोड़ते हैं।

इसके अनेक कारण हैं। सबसे पहले, pmap श्रमिकों पर रोजगार शुरू करने की लागत को अधिक pmap । इस प्रकार, यदि नौकरियां बहुत छोटी हैं, तो ये स्टार्टअप लागत अक्षम हो सकती हैं। इसके विपरीत, हालांकि, pmap श्रमिकों के बीच नौकरी आवंटित करने का "होशियार" काम करता है। विशेष रूप से, यह नौकरियों की एक कतार बनाता है और जब भी वह श्रमिक उपलब्ध हो जाता है, तो प्रत्येक श्रमिक को एक नई नौकरी भेजता है। इसके विपरीत @parallel , श्रमिकों के बीच किए जाने वाले सभी कार्यों को @parallel है जब इसे बुलाया जाता है। जैसे, यदि कुछ कार्यकर्ता दूसरों की तुलना में अपनी नौकरी में अधिक समय लेते हैं, तो आप एक ऐसी स्थिति के साथ समाप्त हो सकते हैं जहां आपके अधिकांश कार्यकर्ता समाप्त हो चुके हैं और निष्क्रिय हैं, जबकि कुछ अपनी नौकरी को समाप्त करने के लिए समय की एक विषम राशि के लिए सक्रिय रहते हैं। हालांकि, ऐसी स्थिति बहुत कम और सरल नौकरियों के साथ होने की संभावना कम है।

निम्नलिखित यह दर्शाता है: मान लें कि हमारे पास दो कार्यकर्ता हैं, जिनमें से एक धीमा है और दूसरा जो दोगुना है। आदर्श रूप में, हम तेज़ कामगार को दो बार देना चाहेंगे, जितना धीमे काम करने वाले को। (या, हम तेज़ और धीमी नौकरी कर सकते थे, लेकिन मूलधन वही है)। pmap यह पूरा करेगा, लेकिन @parallel नहीं होगा।

प्रत्येक परीक्षण के लिए, हम निम्नलिखित को प्रारंभिक करते हैं:

addprocs(2)

@everywhere begin
    function parallel_func(idx)
        workernum = myid() - 1 
        sleep(workernum)
        println("job $idx")
    end
end

अब, @parallel परीक्षण के लिए, हम निम्नलिखित चलाते हैं:

@parallel for idx = 1:12
    parallel_func(idx)
end

और वापस प्रिंट उत्पादन प्राप्त करें:

julia>     From worker 2:    job 1
    From worker 3:    job 7
    From worker 2:    job 2
    From worker 2:    job 3
    From worker 3:    job 8
    From worker 2:    job 4
    From worker 2:    job 5
    From worker 3:    job 9
    From worker 2:    job 6
    From worker 3:    job 10
    From worker 3:    job 11
    From worker 3:    job 12

यह लगभग मीठा है। श्रमिकों ने समान रूप से काम को "साझा" किया है। ध्यान दें कि प्रत्येक श्रमिक ने 6 कार्य पूरे किए हैं, भले ही कार्यकर्ता 2 श्रमिक के रूप में दोगुना हो। 3. यह स्पर्श हो सकता है, लेकिन यह अक्षम है।

pmap परीक्षण के लिए, मैं निम्नलिखित चलाता हूं:

pmap(parallel_func, 1:12)

और उत्पादन प्राप्त करें:

From worker 2:    job 1
From worker 3:    job 2
From worker 2:    job 3
From worker 2:    job 5
From worker 3:    job 4
From worker 2:    job 6
From worker 2:    job 8
From worker 3:    job 7
From worker 2:    job 9
From worker 2:    job 11
From worker 3:    job 10
From worker 2:    job 12

अब, ध्यान दें कि कार्यकर्ता 2 ने 8 नौकरियां और कार्यकर्ता 3 ने प्रदर्शन किया है 4. यह उनकी गति के अनुपात में है, और हम इष्टतम दक्षता के लिए क्या चाहते हैं। pmap एक हार्ड टास्क मास्टर है - प्रत्येक को उनकी क्षमता के अनुसार।

@async और @sync

के तहत प्रलेखन के अनुसार, ?@async , " @async एक कार्य में एक अभिव्यक्ति लपेटता है।" इसका मतलब यह है कि जो कुछ भी इसके दायरे में आता है, जूलिया इस कार्य को शुरू कर देगी, लेकिन फिर स्क्रिप्ट में आगे आने वाले कार्य को पूरा करने की प्रतीक्षा किए बिना आगे बढ़ें। इस प्रकार, उदाहरण के लिए, मैक्रो के बिना आपको मिलेगा:

julia> @time sleep(2)
  2.005766 seconds (13 allocations: 624 bytes)

लेकिन मैक्रो के साथ, आपको मिलता है:

julia> @time @async sleep(2)
  0.000021 seconds (7 allocations: 657 bytes)
Task (waiting) @0x0000000112a65ba0

julia> 

जूलिया इस प्रकार स्क्रिप्ट को आगे बढ़ने के लिए अनुमति देता है (और @time मैक्रो को पूरी तरह से निष्पादित करने के लिए) कार्य की प्रतीक्षा किए बिना (इस मामले में, दो सेकंड के लिए सोते हुए) पूरा होने के लिए।

इसके विपरीत @sync मैक्रो, "प्रतीक्षा करें जब तक @async , @spawn , @spawnat और @parallel सभी गतिशील रूप से संलग्न उपयोग पूर्ण नहीं हो जाते।" (के तहत प्रलेखन के अनुसार ?@sync )। इस प्रकार, हम देखते हैं:

julia> @time @sync @async sleep(2)
  2.002899 seconds (47 allocations: 2.986 KB)
Task (done) @0x0000000112bd2e00

इस सरल उदाहरण में, @async और @sync एक साथ उदाहरण को शामिल करने का कोई मतलब नहीं है। लेकिन, जहाँ @sync उपयोगी हो सकता है, जहाँ आपके पास कई कार्यों के लिए @async लागू होता है, जिसे आप एक बार पूरा होने की प्रतीक्षा किए बिना सभी को एक बार शुरू करने की अनुमति देना चाहते हैं।

उदाहरण के लिए, मान लें कि हमारे पास कई कार्यकर्ता हैं और हम उनमें से प्रत्येक को एक साथ एक काम पर शुरू करना चाहते हैं और फिर उन कार्यों से परिणाम प्राप्त करेंगे। एक प्रारंभिक (लेकिन गलत) प्रयास हो सकता है:

addprocs(2)
@time begin
    a = cell(nworkers())
    for (idx, pid) in enumerate(workers())
        a[idx] = remotecall_fetch(pid, sleep, 2)
    end
end
## 4.011576 seconds (177 allocations: 9.734 KB)

यहाँ समस्या यह है कि लूप प्रत्येक remotecall_fetch() ऑपरेशन को समाप्त करने के लिए इंतजार कर रहा है, यानी अगले remotecall_fetch() ऑपरेशन को शुरू करने से पहले अपने काम को पूरा करने के लिए प्रत्येक प्रक्रिया के लिए (2 सेकंड के लिए सो रही है remotecall_fetch() । व्यावहारिक स्थिति के संदर्भ में, हमें यहाँ समानता का लाभ नहीं मिल रहा है, क्योंकि हमारी प्रक्रियाएँ अपना काम नहीं कर रही हैं (यानी सो रही हैं)।

हालाँकि, हम इसे @async और @sync मैक्रोज़ के संयोजन का उपयोग करके ठीक कर सकते हैं:

@time begin
    a = cell(nworkers())
    @sync for (idx, pid) in enumerate(workers())
        @async a[idx] = remotecall_fetch(pid, sleep, 2)
    end
end
## 2.009416 seconds (274 allocations: 25.592 KB)

अब, यदि हम लूप के प्रत्येक चरण को एक अलग ऑपरेशन के रूप में गिनते हैं, तो हम देखते हैं कि @async मैक्रो द्वारा पहले दो अलग-अलग ऑपरेशन हैं। मैक्रो इनमें से प्रत्येक को शुरू करने की अनुमति देता है, और प्रत्येक खत्म होने से पहले कोड जारी रखने के लिए (इस मामले में लूप के अगले चरण के लिए)। लेकिन, @sync मैक्रो का उपयोग, जिसका दायरा पूरे लूप को @sync करता है, का अर्थ है कि हम स्क्रिप्ट को उस लूप से आगे बढ़ने की अनुमति नहीं देंगे, जब तक कि @async द्वारा पहले किए गए सभी ऑपरेशन पूरे नहीं हो जाते।

इन उदाहरणों को और अधिक स्पष्ट रूप से समझा जाना संभव है, ऊपर के उदाहरण को और अधिक देखते हुए कि यह कुछ संशोधनों के तहत कैसे बदलता है। उदाहरण के लिए, मान लें कि हमारे पास @async बिना सिर्फ @sync :

@time begin
    a = cell(nworkers())
    for (idx, pid) in enumerate(workers())
        println("sending work to $pid")
        @async a[idx] = remotecall_fetch(pid, sleep, 2)
    end
end
## 0.001429 seconds (27 allocations: 2.234 KB)

यहां, @async मैक्रो हमें प्रत्येक remotecall_fetch() ऑपरेशन को पूरा करने से पहले ही हमारे लूप में जारी रखने की अनुमति देता है। लेकिन, बेहतर या बदतर के लिए, हमारे पास कोई भी @sync मैक्रो नहीं है, ताकि कोड को इस लूप से पिछले तक रोका जा सके जब तक कि सभी remotecall_fetch() ऑपरेशंस खत्म न हो जाएं।

फिर भी, प्रत्येक remotecall_fetch() ऑपरेशन अभी भी समानांतर में चल रहा है, यहां तक कि एक बार जब हम चलते हैं। हम यह देख सकते हैं क्योंकि यदि हम दो सेकंड तक प्रतीक्षा करते हैं, तो सरणी a, जिसमें परिणाम होंगे, शामिल होंगे:

sleep(2)
julia> a
2-element Array{Any,1}:
 nothing
 nothing

("कुछ भी नहीं" तत्व नींद समारोह के परिणामों के एक सफल लाने का परिणाम है, जो किसी भी मूल्य को वापस नहीं करता है)

हम यह भी देख सकते हैं कि दो remotecall_fetch() परिचालन अनिवार्य रूप से एक ही समय में शुरू होते हैं क्योंकि print कमांड जो उन्हें पूर्ववर्ती करते हैं वे भी तेजी से उत्तराधिकार में निष्पादित होते हैं (इन कमांड से आउटपुट यहां नहीं दिखाया गया है)। अगले उदाहरण के साथ इसका विरोध करें जहां print कमांड एक दूसरे से 2 सेकंड के अंतराल पर निष्पादित होते हैं:

यदि हम पूरे लूप पर (केवल इसके आंतरिक चरण के बजाय) पर @async मैक्रो @async , तो फिर से remotecall_fetch() संचालन की प्रतीक्षा किए बिना हमारी स्क्रिप्ट तुरंत जारी रहेगी। अब, हालांकि, हम केवल स्क्रिप्ट को लूप को एक पूरे के रूप में जारी रखने की अनुमति देते हैं। हम पिछले एक समाप्त होने से पहले लूप के प्रत्येक व्यक्तिगत चरण को शुरू करने की अनुमति नहीं देते हैं। जैसे, उपरोक्त उदाहरण के विपरीत, लूप के बाद स्क्रिप्ट के आगे #undef दो सेकंड बाद, results सरणी में अभी भी #undef रूप में एक तत्व है जो दर्शाता है कि दूसरा remotecall_fetch() ऑपरेशन अभी भी पूरा नहीं हुआ है।

@time begin
    a = cell(nworkers())
    @async for (idx, pid) in enumerate(workers())
        println("sending work to $pid")
        a[idx] = remotecall_fetch(pid, sleep, 2)
    end
end
# 0.001279 seconds (328 allocations: 21.354 KB)
# Task (waiting) @0x0000000115ec9120
## This also allows us to continue to

sleep(2)

a
2-element Array{Any,1}:
    nothing
 #undef    

और, आश्चर्य की बात नहीं, अगर हम @sync और @async को एक-दूसरे के ठीक बगल में रखते हैं, तो हम पाते हैं कि प्रत्येक remotecall_fetch() क्रमिक रूप से (एक साथ बजाय remotecall_fetch() चलता है, लेकिन हम प्रत्येक समाप्त होने तक कोड में जारी नहीं रखते हैं। दूसरे शब्दों में, यह अनिवार्य रूप से समतुल्य होगा यदि हमारे पास न तो मैक्रो था, जैसे कि sleep(2) अनिवार्य रूप से @sync @async sleep(2) लिए अनिवार्य रूप से व्यवहार करती है

@time begin
    a = cell(nworkers())
    @sync @async for (idx, pid) in enumerate(workers())
        a[idx] = remotecall_fetch(pid, sleep, 2)
    end
end
# 4.019500 seconds (4.20 k allocations: 216.964 KB)
# Task (done) @0x0000000115e52a10

यह भी ध्यान दें कि @async मैक्रो के दायरे में अधिक जटिल ऑपरेशन करना संभव है। प्रलेखन @async के दायरे में एक संपूर्ण लूप युक्त एक उदाहरण देता है।

याद रखें कि सिंक मैक्रोज़ के लिए मदद बताती है कि यह "प्रतीक्षा करें जब तक कि @async , @spawn , @spawnat और @parallel सभी गतिशील रूप से संलग्न उपयोग पूरे नहीं हो जाते।" "पूर्ण" के रूप में जो मायने रखता है उसके प्रयोजनों के लिए यह मायने रखता है कि आप @sync और @async मैक्रोज़ के दायरे में कार्यों को कैसे परिभाषित करते हैं। नीचे दिए गए उदाहरणों पर विचार करें, जो ऊपर दिए गए उदाहरणों में से एक पर थोड़ा बदलाव है:

@time begin
    a = cell(nworkers())
    @sync for (idx, pid) in enumerate(workers())
        @async a[idx] = remotecall(pid, sleep, 2)
    end
end
## 0.172479 seconds (93.42 k allocations: 3.900 MB)

julia> a
2-element Array{Any,1}:
 RemoteRef{Channel{Any}}(2,1,3)
 RemoteRef{Channel{Any}}(3,1,4)

पहले के उदाहरण को निष्पादित करने में लगभग 2 सेकंड का समय लगा, यह दर्शाता है कि दो कार्यों को समानांतर में चलाया गया था और यह कि स्क्रिप्ट प्रत्येक को आगे बढ़ने से पहले अपने कार्यों को पूरा करने की प्रतीक्षा कर रही है। हालाँकि, इस उदाहरण का मूल्यांकन बहुत कम है। कारण यह है कि @sync के उद्देश्यों के लिए एक बार यह काम करने के लिए कर्मचारी को भेजने के बाद remotecall() ऑपरेशन "समाप्त" हो गया है। (ध्यान दें कि परिणामी सरणी, a, यहाँ, बस में RemoteRef ऑब्जेक्ट प्रकार शामिल हैं, जो केवल यह संकेत देते हैं कि किसी विशेष प्रक्रिया के साथ कुछ चल रहा है जो कि भविष्य में किसी बिंदु पर सिद्धांत रूप में लाया जा सकता है)। इसके विपरीत, remotecall_fetch() ऑपरेशन में केवल "समाप्त" होता है जब उसे कार्यकर्ता से संदेश मिलता है कि उसका कार्य पूरा हो गया है।

इस प्रकार, यदि आप यह सुनिश्चित करने के तरीकों की तलाश कर रहे हैं कि आपकी स्क्रिप्ट में आगे बढ़ने से पहले श्रमिकों के साथ कुछ संचालन पूरा हो गया है (उदाहरण के लिए इस पोस्ट में चर्चा की गई है) तो यह ध्यान से सोचना आवश्यक है कि "पूर्ण" के रूप में क्या मायने रखता है और आप कैसे हैं उपाय करें और फिर उसे अपनी लिपि में परिचालित करें।

वर्कर्स को जोड़ना

जब आप पहली बार जूलिया शुरू करते हैं, तो डिफ़ॉल्ट रूप से, केवल एक ही प्रक्रिया चल रही होगी और काम देने के लिए उपलब्ध होगी। आप इसका उपयोग करके सत्यापित कर सकते हैं:

julia> nprocs()
1

समानांतर प्रसंस्करण का लाभ उठाने के लिए, आपको पहले अतिरिक्त श्रमिकों को जोड़ना होगा जो तब काम करने के लिए उपलब्ध होंगे जो आप उन्हें असाइन करते हैं। आप इसका उपयोग अपनी स्क्रिप्ट (या दुभाषिया से) के भीतर कर सकते हैं: addprocs(n) जहां n उन प्रक्रियाओं की संख्या है जिनका आप उपयोग करना चाहते हैं।

वैकल्पिक रूप से, जब आप कमांड लाइन से जूलिया का उपयोग शुरू करते हैं तो आप प्रक्रियाएँ जोड़ सकते हैं:

$ julia -p n

जहाँ n कितनी अतिरिक्त प्रक्रियाएँ आप जोड़ना चाहते हैं इस प्रकार, अगर हम जूलिया के साथ शुरू करते हैं

$ julia -p 2

जूलिया शुरू होने पर हमें मिलेगा:

julia> nprocs()
3


Modified text is an extract of the original Stack Overflow Documentation
के तहत लाइसेंस प्राप्त है CC BY-SA 3.0
से संबद्ध नहीं है Stack Overflow