लूप के लिए एक बैश समानांतर


109

मैं निम्नलिखित स्क्रिप्ट को समानांतर करने की कोशिश कर रहा हूं, विशेष रूप से तीन फॉर लूप उदाहरणों में से प्रत्येक, जीएनयू समानांतर का उपयोग कर रहा है, लेकिन करने में सक्षम नहीं है। फोर लूप के भीतर निहित 4 कमांड श्रृंखला में चलते हैं, प्रत्येक लूप को लगभग 10 मिनट लगते हैं।

#!/bin/bash

kar='KAR5'
runList='run2 run3 run4'
mkdir normFunc
for run in $runList
do 
  fsl5.0-flirt -in $kar"deformed.nii.gz" -ref normtemp.nii.gz -omat $run".norm1.mat" -bins 256 -cost corratio -searchrx -90 90 -searchry -90 90 -searchrz -90 90 -dof 12 
  fsl5.0-flirt -in $run".poststats.nii.gz" -ref $kar"deformed.nii.gz" -omat $run".norm2.mat" -bins 256 -cost corratio -searchrx -90 90 -searchry -90 90 -searchrz -90 90 -dof 12 
  fsl5.0-convert_xfm -concat $run".norm1.mat" -omat $run".norm.mat" $run".norm2.mat"
  fsl5.0-flirt -in $run".poststats.nii.gz" -ref normtemp.nii.gz -out $PWD/normFunc/$run".norm.nii.gz" -applyxfm -init $run".norm.mat" -interp trilinear

  rm -f *.mat
done

जवाबों:


94

आप उन्हें केवल कांटा (उर्फ पृष्ठभूमि) क्यों नहीं बनाते हैं?

foo () {
    local run=$1
    fsl5.0-flirt -in $kar"deformed.nii.gz" -ref normtemp.nii.gz -omat $run".norm1.mat" -bins 256 -cost corratio -searchrx -90 90 -searchry -90 90 -searchrz -90 90 -dof 12 
    fsl5.0-flirt -in $run".poststats.nii.gz" -ref $kar"deformed.nii.gz" -omat $run".norm2.mat" -bins 256 -cost corratio -searchrx -90 90 -searchry -90 90 -searchrz -90 90 -dof 12 
    fsl5.0-convert_xfm -concat $run".norm1.mat" -omat $run".norm.mat" $run".norm2.mat"
    fsl5.0-flirt -in $run".poststats.nii.gz" -ref normtemp.nii.gz -out $PWD/normFunc/$run".norm.nii.gz" -applyxfm -init $run".norm.mat" -interp trilinear
}

for run in $runList; do foo "$run" & done

यदि यह स्पष्ट नहीं है, तो महत्वपूर्ण हिस्सा यहाँ है:

for run in $runList; do foo "$run" & done
                                   ^

पृष्ठभूमि में एक कांटा खोल में निष्पादित होने के कारण समारोह। वह समानांतर है।


6
उसने जादू की तरह काम किया। धन्यवाद। इस तरह के एक सरल कार्यान्वयन (मुझे अब इतना बेवकूफ लगता है!)।
रावनूर एस गिल

8
यदि मेरे पास समानांतर में चलाने के लिए 8 फाइलें हैं, लेकिन केवल 4 कोर हैं, तो क्या ऐसी सेटिंग में एकीकृत किया जा सकता है या जॉब शेड्यूलर की आवश्यकता होगी?
रावनूर एस गिल

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

17
आप भी waitअंत में एक कमांड जोड़ना चाह सकते हैं ताकि मास्टर स्क्रिप्ट तब तक बाहर न निकले जब तक कि सभी बैकग्राउंड जॉब न कर दें।
psusi

1
मैं समवर्ती प्रक्रियाओं की संख्या को सीमित करने के लिए भी उपयोगी होगा: मेरी प्रक्रियाएं प्रत्येक 25% कोर के समय का लगभग 25 मिनट के लिए उपयोग करती हैं। यह 16 कोर के साथ एक साझा सर्वर पर है, जहां कई लोग नौकरी चला रहे हैं। मुझे स्क्रिप्ट की 23 प्रतियां चलाने की आवश्यकता है। यदि मैं उन सभी को समवर्ती रूप से चलाता हूं, तो मैं सर्वर को स्वैप करता हूं, और इसे एक या दो घंटे के लिए बाकी सभी के लिए बेकार कर देता हूं (लोड 30 तक चला जाता है, बाकी सब कुछ धीमा हो जाता है)। मुझे लगता है कि यह किया जा सकता है nice, लेकिन तब मुझे नहीं पता कि क्या यह कभी खत्म होगा ..
naught101

150

नमूना कार्य

task(){
   sleep 0.5; echo "$1";
}

अनुक्रमिक रन

for thing in a b c d e f g; do 
   task "$thing"
done

समानांतर रन

for thing in a b c d e f g; do 
  task "$thing" &
done

एन-प्रक्रिया बैचों में समानांतर रन

N=4
(
for thing in a b c d e f g; do 
   ((i=i%N)); ((i++==0)) && wait
   task "$thing" & 
done
)

एफआईएफओ को सेमाफोर के रूप में उपयोग करना और यह सुनिश्चित करने के लिए उनका उपयोग करना भी संभव है कि नई प्रक्रियाओं को जितनी जल्दी हो सके उतारा जाए और एन प्रक्रियाओं से अधिक कोई भी एक ही समय में न चले। लेकिन इसके लिए अधिक कोड की आवश्यकता होती है।

एक एफआईएफओ-आधारित सेमाफोर के साथ एन प्रक्रियाएं:

open_sem(){
    mkfifo pipe-$$
    exec 3<>pipe-$$
    rm pipe-$$
    local i=$1
    for((;i>0;i--)); do
        printf %s 000 >&3
    done
}
run_with_lock(){
    local x
    read -u 3 -n 3 x && ((0==x)) || exit $x
    (
     ( "$@"; )
    printf '%.3d' $? >&3
    )&
}

N=4
open_sem $N
for thing in {a..g}; do
    run_with_lock task $thing
done 

4
इसके साथ लाइन waitमूल रूप से सभी प्रक्रियाओं को चलाने देती है, जब तक कि यह nthप्रक्रिया हिट नहीं हो जाती है, तब तक अन्य सभी के चलने का इंतजार करती है, क्या यह सही है?
n

यदि iशून्य है, तो कॉल प्रतीक्षा करें। iशून्य परीक्षण के बाद वृद्धि ।
पीएसकोकिक

2
@ naught101 हां। waitw / no arg सभी बच्चों की प्रतीक्षा करता है। जो इसे थोड़ा बेकार बनाता है। पाइप-आधारित-सेमाफोर दृष्टिकोण आपको अधिक धाराप्रवाह संगति देता है (मैं एक कस्टम शेल आधारित बिल्ड सिस्टम के साथ -nt/ -otअब थोड़ी देर के लिए सफलतापूर्वक जाँच कर रहा
हूं

1
@ BeowulfNode42 आपको बाहर निकलने की जरूरत नहीं है। जब तक कार्य की प्रक्रिया से बाहर / क्रैश हो जाता है तब तक कार्य की वापसी की स्थिति सेमाफ़ोर की स्थिरता को नुकसान नहीं पहुंचाएगी क्योंकि स्थिति (या उस गति के साथ कुछ) को फिर से लिखा जाता है।
PSkocik

1
FYI करें mkfifo pipe-$$कमांड को वर्तमान निर्देशिका के लिए उपयुक्त लिखने की आवश्यकता है। इसलिए मैं पूर्ण पथ निर्दिष्ट करना पसंद करता हूं जैसे कि /tmp/pipe-$$यह सबसे अधिक संभावना है कि वर्तमान उपयोगकर्ता जो भी वर्तमान निर्देशिका है उस पर भरोसा करने के बजाय वर्तमान उपयोगकर्ता के लिए उपलब्ध लेखन लिख सकता है। हाँ सभी 3 घटनाओं को प्रतिस्थापित करें pipe-$$
बियोवुल्फ़न्यूड 42

65
for stuff in things
do
( something
  with
  stuff ) &
done
wait # for all the something with stuff

क्या यह वास्तव में काम करता है आपके आदेशों पर निर्भर करता है; मैं उनसे परिचित नहीं हूँ। rm *.matथोड़ा संघर्ष होने का खतरा लग रहा है अगर यह समानांतर में चलाता है ...


2
यह पूरी तरह से चलाता है। आप सही हैं कि मुझे किसी rm *.matचीज़ में बदलाव करना होगा, rm $run".mat"ताकि एक प्रक्रिया को दूसरे के साथ हस्तक्षेप किए बिना काम कर सकें। आपका धन्यवाद
रावनूर एस गिल

@RavnoorSGill स्टैक एक्सचेंज में आपका स्वागत है! यदि इस उत्तर ने आपकी समस्या को हल कर दिया है, तो कृपया इसे चिह्नित करें जैसे कि इसके आगे चेक मार्क को टिक करके स्वीकार किया गया है।
गिल्स

7
+1 के लिए wait, जिसे मैं भूल गया।
गोल्डीलॉक्स

5
अगर वहाँ 'चीजों' के टन कर रहे हैं, यह प्रक्रियाओं के टन शुरू नहीं होगा? बेहतर होगा कि केवल एक ही संख्या में प्रक्रियाओं को एक साथ शुरू किया जाए, है ना?
डेविड डोरिया

1
बहुत उपयोगी टिप! इस मामले में थ्रेड्स की संख्या कैसे सेट करें?
दादोंग झांग

30
for stuff in things
do
sem -j+0 ( something
  with
  stuff )
done
sem --wait

यह उपमाओं का उपयोग करेगा, उपलब्ध पुनरावृत्तियों की संख्या के रूप में कई पुनरावृत्तियों को समाहित करेगा (-j +0 का अर्थ है कि आप N + 0 नौकरियों को समानांतर करेंगे , जहां N उपलब्ध कोर की संख्या है )।

सेमी - प्रतीक्षा करने के लिए कहता है जब तक कि लूप के लिए सभी पुनरावृत्तियों ने कोड की क्रमिक लाइनों को निष्पादित करने से पहले निष्पादन को समाप्त कर दिया हो।

नोट: आपको GNU समानांतर परियोजना (sudo apt-get install समानांतर) से "समानांतर" की आवश्यकता होगी ।


1
क्या 60 के पार जाना संभव है? मेरा एक त्रुटि कह रही है कि पर्याप्त फ़ाइल वर्णनकर्ता नहीं है।
चॉवी

यदि यह किसी के लिए ब्रेसिज़ की वजह से एक वाक्यविन्यास त्रुटि भी फेंक रहा है, तो मोरिट्ज़ज़फर द्वारा उत्तर पर एक नज़र डालें।
निकोलाई

10

एक बहुत आसान तरीका है जो मैं अक्सर उपयोग करता हूं:

cat "args" | xargs -P $NUM_PARALLEL command

यह कमांड चलाएगा, "आर्ग्स" फ़ाइल की प्रत्येक पंक्ति में, समानांतर में, एक ही समय में अधिकांश $ NUM_PARALLEL पर चल रहा है।

यदि आप विभिन्न स्थानों में इनपुट तर्कों को स्थानापन्न करने की आवश्यकता है, तो आप xargs के लिए -I विकल्प देख सकते हैं।


6

ऐसा लगता है कि fsl नौकरियां प्रत्येक अभिभावक पर निर्भर करती हैं, इसलिए 4 नौकरियों को समानांतर में नहीं चलाया जा सकता है। रन, हालांकि, समानांतर में चलाया जा सकता है।

एक फंक्शन रनिंग बैश फंक्शन करें और उस फंक्शन को समानांतर में चलाएं:

#!/bin/bash

myfunc() {
    run=$1
    kar='KAR5'
    mkdir normFunc
    fsl5.0-flirt -in $kar"deformed.nii.gz" -ref normtemp.nii.gz -omat $run".norm1.mat" -bins 256 -cost corratio -searchrx -90 90 -searchry -90 90 -searchrz -90 90 -dof 12 
    fsl5.0-flirt -in $run".poststats.nii.gz" -ref $kar"deformed.nii.gz" -omat $run".norm2.mat" -bins 256 -cost corratio -searchrx -90 90 -searchry -90 90 -searchrz -90 90 -dof 12 
    fsl5.0-convert_xfm -concat $run".norm1.mat" -omat $run".norm.mat" $run".norm2.mat"
    fsl5.0-flirt -in $run".poststats.nii.gz" -ref normtemp.nii.gz -out $PWD/normFunc/$run".norm.nii.gz" -applyxfm -init $run".norm.mat" -interp trilinear
}

export -f myfunc
parallel myfunc ::: run2 run3 run4

अधिक जानने के लिए परिचय वीडियो देखें: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1 और ट्यूटोरियल के माध्यम से चलने के लिए एक घंटे खर्च करते हैं http://www.gnu.org/software/parallel/parallel_tutorial.html आपका आदेश रेखा आपको इसके लिए प्यार करेगी।


यदि आप एक गैर-बैश शेल का उपयोग कर रहे हैं, तो आपको export SHELL=/bin/bashसमानांतर चलने से पहले भी आवश्यकता होगी । अन्यथा आपको एक त्रुटि मिलेगी जैसे:Unknown command 'myfunc arg'
एंड्रयूहार्इ

1
@AndrewHarvey: यह नहीं है कि शबंग किस लिए है?
n

5

अधिकतम एन-प्रक्रिया समवर्ती में समानांतर निष्पादन

#!/bin/bash

N=4

for i in {a..z}; do
    (
        # .. do your stuff here
        echo "starting task $i.."
        sleep $(( (RANDOM % 3) + 1))
    ) &

    # allow only to execute $N jobs in parallel
    if [[ $(jobs -r -p | wc -l) -gt $N ]]; then
        # wait only for first job
        wait -n
    fi

done

# wait for pending jobs
wait

echo "all done"

3

मुझे वास्तव में @lev से उत्तर पसंद है क्योंकि यह बहुत सरल तरीके से अधिकतम प्रक्रियाओं पर नियंत्रण प्रदान करता है। हालाँकि जैसा कि मैनुअल में बताया गया है , सेम ब्रैकेट्स के साथ काम नहीं करता है।

for stuff in things
do
sem -j +0 "something; \
  with; \
  stuff"
done
sem --wait

नौकरी करता है।

-j + N CPU कोर की संख्या में N जोड़ें। समानांतर में इस कई नौकरियों के लिए भागो। गणना के लिए गहन जॉब -j +0 उपयोगी है क्योंकि यह नंबर-ऑफ-सीपीयू-कोर जॉब्स को एक साथ चलाएगा।

-j -N घटाव N को CPU कोर की संख्या से। समानांतर में इस कई नौकरियों के लिए भागो। यदि मूल्यांकित संख्या 1 से कम है तो 1 का उपयोग किया जाएगा। यह भी देखें --use-cpus-बजाय-कोर।


1

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

यह अच्छी तरह से काम करता है अगर कार्यों में लगभग समान समय लगता है। नुकसान यह है कि, अगर श्रमिकों में से एक को अपना काम करने में लंबा समय लगता है, तो पहले से ही समाप्त हो चुके अन्य लोगों को मदद नहीं मिलेगी।

एन श्रमिकों के बीच काम का विभाजन (1 प्रति कोर)

# array of assets, assuming at least 1 item exists
listAssets=( {a..z} ) # example: a b c d .. z
# listAssets=( ~/"path with spaces/"*.txt ) # could be file paths

# replace with your task
task() { # $1 = idWorker, $2 = asset
  echo "Worker $1: Asset '$2' START!"
  # simulating a task that randomly takes 3-6 seconds
  sleep $(( ($RANDOM % 4) + 3 ))
  echo "    Worker $1: Asset '$2' OK!"
}

nVirtualCores=$(nproc --all)
nWorkers=$(( $nVirtualCores * 1 )) # I want 1 process per core

worker() { # $1 = idWorker
  echo "Worker $1 GO!"
  idAsset=0
  for asset in "${listAssets[@]}"; do
    # split assets among workers (using modulo); each worker will go through
    # the list and select the asset only if it belongs to that worker
    (( idAsset % nWorkers == $1 )) && task $1 "$asset"
    (( idAsset++ ))
  done
  echo "    Worker $1 ALL DONE!"
}

for (( idWorker=0; idWorker<nWorkers; idWorker++ )); do
  # start workers in parallel, use 1 process for each
  worker $idWorker &
done
wait # until all workers are done

0

मैं मुसीबत के @PSkocikसमाधान के साथ था । मेरे सिस्टम में एक पैकेज के रूप में जीएनयू समानांतर उपलब्ध नहीं है और semजब मैंने इसे मैन्युअल रूप से बनाया और चलाया तो एक अपवाद को फेंक दिया। मैंने तब फीफो सेमाफोर उदाहरण की कोशिश की, जो संचार के संबंध में कुछ अन्य त्रुटियों को भी फेंक देता है।

@eyeApps सुझाव दिया xargs, लेकिन मुझे नहीं पता था कि यह मेरे जटिल उपयोग के मामले में कैसे काम करेगा (उदाहरणों का स्वागत होगा)।

यहाँ समानांतर नौकरियों के लिए मेरा समाधान है जो Nएक समय में नौकरियों के लिए प्रक्रिया के अनुसार कॉन्फ़िगर किया गया है _jobs_set_max_parallel:

_lib_jobs.sh:

function _jobs_get_count_e {
   jobs -r | wc -l | tr -d " "
}

function _jobs_set_max_parallel {
   g_jobs_max_jobs=$1
}

function _jobs_get_max_parallel_e {
   [[ $g_jobs_max_jobs ]] && {
      echo $g_jobs_max_jobs

      echo 0
   }

   echo 1
}

function _jobs_is_parallel_available_r() {
   (( $(_jobs_get_count_e) < $g_jobs_max_jobs )) &&
      return 0

   return 1
}

function _jobs_wait_parallel() {
   # Sleep between available jobs
   while true; do
      _jobs_is_parallel_available_r &&
         break

      sleep 0.1s
   done
}

function _jobs_wait() {
   wait
}

उदाहरण का उपयोग:

#!/bin/bash

source "_lib_jobs.sh"

_jobs_set_max_parallel 3

# Run 10 jobs in parallel with varying amounts of work
for a in {1..10}; do
   _jobs_wait_parallel

   # Sleep between 1-2 seconds to simulate busy work
   sleep_delay=$(echo "scale=1; $(shuf -i 10-20 -n 1)/10" | bc -l)

   ( ### ASYNC
   echo $a
   sleep ${sleep_delay}s
   ) &
done

# Visualize jobs
while true; do
   n_jobs=$(_jobs_get_count_e)

   [[ $n_jobs = 0 ]] &&
      break

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