Menjalankan perintah secara paralel dengan batas jumlah perintah secara bersamaan

23

Berurutan: for i in {1..1000}; do do_something $i; done- terlalu lambat

Paralel: for i in {1..1000}; do do_something $i& done- terlalu banyak memuat

Bagaimana menjalankan perintah secara paralel, tetapi tidak lebih dari, misalnya, 20 instance per momen?

Sekarang biasanya menggunakan hack like for i in {1..1000}; do do_something $i& sleep 5; done, tetapi ini bukan solusi yang baik.

Pembaruan 2 : Mengonversi jawaban yang diterima menjadi skrip: http://vi-server.org/vi/parallel

#!/bin/bash

NUM=$1; shift

if [ -z "$NUM" ]; then
    echo "Usage: parallel <number_of_tasks> command"
    echo "    Sets environment variable i from 1 to number_of_tasks"
    echo "    Defaults to 20 processes at a time, use like \"MAKEOPTS='-j5' parallel ...\" to override."
    echo "Example: parallel 100 'echo \$i; sleep \`echo \$RANDOM/6553 | bc -l\`'"
    exit 1
fi

export CMD="$@";

true ${MAKEOPTS:="-j20"}

cat << EOF | make -f - -s $MAKEOPTS
PHONY=jobs
jobs=\$(shell echo {1..$NUM})

all: \${jobs}

\${jobs}:
        i=\$@ sh -c "\$\$CMD"
EOF

Perhatikan bahwa Anda harus mengganti 8 spasi dengan 2 tab sebelum "i =" untuk membuatnya berfungsi.

Vi.
sumber

Jawaban:

15

GNU Parallel dibuat untuk ini.

seq 1 1000 | parallel -j20 do_something

Ia bahkan dapat menjalankan pekerjaan pada komputer jarak jauh. Berikut adalah contoh untuk meng-encode ulang MP3 ke OGG menggunakan server2 dan komputer lokal yang menjalankan 1 pekerjaan per inti CPU:

parallel --trc {.}.ogg -j+0 -S server2,: \
     'mpg321 -w - {} | oggenc -q0 - -o {.}.ogg' ::: *.mp3

Tonton video intro ke GNU Parallel di sini:

http://www.youtube.com/watch?v=OpaiGYxkSuQ

Ole Tange
sumber
Belum tahu tentang "moreutils" dan bahwa sudah ada alat untuk pekerjaan itu. Mencari dan membandingkan.
Vi.
1
Dalam parallelmoreutils bukan GNU Paralel dan cukup terbatas dalam opsinya. Perintah di atas tidak akan berjalan dengan paralel dari moreutils.
Ole Tange
1
Satu lagi pilihan: xargs --max-procs=20.
Vi.
4

Bukan solusi bash, tetapi Anda harus menggunakan Makefile, mungkin dengan -ltidak melebihi beberapa beban maksimum.

NJOBS=1000

.PHONY = jobs
jobs = $(shell echo {1..$(NJOBS)})

all: $(jobs)

$(jobs):
    do_something $@

Kemudian untuk memulai 20 pekerjaan sekaligus lakukan

$ make -j20

atau untuk memulai pekerjaan sebanyak mungkin tanpa melebihi beban 5

$ make -j -l5
Benjamin Bannier
sumber
Sepertinya solusi non-hacky untuk saat ini.
Vi.
2
echo -e 'PHONY=jobs\njobs=$(shell echo {1..100000})\n\nall: ${jobs}\n\n${jobs}:\n\t\techo $@; sleep `echo $$RANDOM/6553 | bc -l`' | make -f - -j20Sekarang terlihat lebih retas lagi.
Vi.
@vi: oh my ....
Benjamin Bannier
Konversi solusi Anda menjadi skrip. Sekarang bisa digunakan dengan mudah.
Vi.
2

memposting skrip dalam pertanyaan dengan pemformatan:

#!/bin/bash

NUM=$1; shift

if [ -z "$NUM" ]; then
    echo "Usage: parallel <number_of_tasks> command"
    echo "    Sets environment variable i from 1 to number_of_tasks"
    echo "    Defaults to 20 processes at a time, use like \"MAKEOPTS='-j5' parallel ...\" to override."
    echo "Example: parallel 100 'echo \$i; sleep \`echo \$RANDOM/6553 | bc -l\`'"
    exit 1
fi

export CMD="$@";

true ${MAKEOPTS:="-j20"}

cat << EOF | make -f - -s $MAKEOPTS
PHONY=jobs
jobs=\$(shell echo {1..$NUM})

all: \${jobs}

\${jobs}:
        i=\$@ sh -c "\$\$CMD"
EOF

Perhatikan bahwa Anda harus mengganti 8 spasi dengan 2 tab sebelum "i =".

warren
sumber
1

Satu ide sederhana:

Periksa i modulo 20 dan jalankan perintah wait shell sebelum do_something.

harrymc
sumber
Entah akan menunggu semua tugas saat ini untuk menyelesaikan (membuat sags dalam jumlah plot tugas) atau menunggu satu tugas khusus yang dapat terhenti untuk waktu yang lebih lama (lagi-lagi membuat sags dalam kasus ini)
Vi.
@ Vi: Shell wait adalah untuk semua tugas latar belakang milik shell ini.
harrymc
1

Anda bisa menggunakan psuntuk menghitung berapa banyak proses yang telah Anda jalankan, dan setiap kali ini turun di bawah ambang tertentu Anda memulai proses lain.

Kode palsu:

i = 1
MAX_PROCESSES=20
NUM_TASKS=1000
do
  get num_processes using ps
  if num_processes < MAX_PROCESSES
    start process $i
    $i = $i + 1
  endif
  sleep 1 # add this to prevent thrashing with ps
until $i > NUM_TASKS
Paul R
sumber
1
for i in {1..1000}; do 
     (echo $i ; sleep `expr $RANDOM % 5` ) &
     while [ `jobs | wc -l` -ge 20 ] ; do 
         sleep 1 
     done
done
msw
sumber
Mungkin while [ `jobs | wc -l` -ge 20]; do?
Vi.
tentu, tetapi dalam sampel saya, saya kemudian harus menghitung njobsdua kali, dan kinerja cukup penting dalam skrip shell yang menjalankan tugas tidur;)
msw
Maksud saya versi Anda tidak berfungsi seperti yang diharapkan. Saya mengubah sleep 1ke sleep 0.1dan mulai rata-rata njobs ke 40-50 bukannya 20. Jika ada lebih dari 20 pekerjaan kita perlu menunggu pekerjaan selesai, bukan hanya menunggu 1 detik.
Vi.
0

Anda bisa melakukannya seperti ini.

threads=20
tempfifo=$PMS_HOME/$$.fifo

trap "exec 1000>&-;exec 1000<&-;exit 0" 2
mkfifo $tempfifo
exec 1000<>$tempfifo
rm -rf $tempfifo

for ((i=1; i<=$threads; i++))
do
    echo >&1000
done

for ((j=1; j<=1000; j++))
do
    read -u1000
    {
        echo $j
        echo >&1000
    } &
done

wait
echo "done!!!!!!!!!!"

menggunakan pipa bernama, setiap kali, ia menjalankan 20 sub shell secara paralel.

Semoga bisa membantu :)

ouyangyewei
sumber