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.

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


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

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


1

Satu ide sederhana:

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


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

1
for i in {1..1000}; do 
     (echo $i ; sleep `expr $RANDOM % 5` ) &
     while [ `jobs | wc -l` -ge 20 ] ; do 
         sleep 1 
     done
done

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 :)

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.