[bash] 최대 프로세스 수로 Bash 스크립트 병렬화

Bash에 루프가 있다고 가정 해 보겠습니다.

for foo in `some-command`
do
   do-something $foo
done

do-somethingCPU에 묶여 있고 멋진 4 코어 프로세서가 있습니다. do-something한 번에 최대 4 개까지 실행하고 싶습니다 .

순진한 접근 방식은 다음과 같습니다.

for foo in `some-command`
do
   do-something $foo &
done

이 실행됩니다 모두 do-something 한 번에들하지만, 주로 몇 가지 단점이있다 할이-뭔가도 수행 할 몇 가지 중요한 I / O 할 수 있습니다 모든 조금 느려질 수 있습니다 한 번에 있습니다. 다른 문제는이 코드 블록이 즉시 반환되므로 모든 do-somethings가 완료 되면 다른 작업을 수행 할 방법이 없다는 것 입니다.

do-something한 번에 항상 X 가 실행 되도록이 루프를 어떻게 작성 하시겠습니까?



답변

원하는 작업에 따라 xargs도 도움이 될 수 있습니다 (여기 : pdf2ps로 문서 변환).

cpus=$( ls -d /sys/devices/system/cpu/cpu[[:digit:]]* | wc -w )

find . -name \*.pdf | xargs --max-args=1 --max-procs=$cpus  pdf2ps

문서에서 :

--max-procs=max-procs
-P max-procs
       Run up to max-procs processes at a time; the default is 1.
       If max-procs is 0, xargs will run as many processes as  possible  at  a
       time.  Use the -n option with -P; otherwise chances are that only one
       exec will be done.


답변

GNU Parallel http://www.gnu.org/software/parallel/ 을 사용하면 다음과 같이 작성할 수 있습니다.

some-command | parallel do-something

GNU Parallel은 원격 컴퓨터에서 작업 실행도 지원합니다. 이렇게하면 원격 컴퓨터의 CPU 코어 당 하나씩 실행됩니다. 코어 수가 다른 경우에도 마찬가지입니다.

some-command | parallel -S server1,server2 do-something

고급 예 : 여기 my_script가 실행될 파일 목록이 있습니다. 파일 확장자는 .jpeg 일 수 있습니다. my_script의 출력이 basename.out의 파일 옆에 배치되기를 원합니다 (예 : foo.jpeg-> foo.out). 컴퓨터에있는 각 코어에 대해 my_script를 한 번씩 실행하고 로컬 컴퓨터에서도 실행하려고합니다. 원격 컴퓨터의 경우 파일이 주어진 컴퓨터로 전송되기를 원합니다. my_script가 완료되면 foo.out을 다시 전송하고 원격 컴퓨터에서 foo.jpeg 및 foo.out을 제거하려고합니다.

cat list_of_files | \
parallel --trc {.}.out -S server1,server2,: \
"my_script {} > {.}.out"

GNU Parallel은 각 작업의 출력이 혼합되지 않도록하므로 출력을 다른 프로그램의 입력으로 사용할 수 있습니다.

some-command | parallel do-something | postprocess

더 많은 예를 보려면 동영상을 참조 하세요 . https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1


답변

maxjobs = 4
parallelize () {
        while [$ # -gt 0]; 하다
                jobcnt = (`작업 -p`)
                if [$ {# jobcnt [@]} -lt $ maxjobs]; 그때
                        뭔가 $ 1 &
                        시프트
                그밖에
                        수면 1
                fi
        끝난
        기다림
}

arg1 arg2 "3 번째 작업에 5 개의 args"arg4 ...


답변

다음은 .bashrc에 삽입 할 수 있고 매일 하나의 라이너에 사용할 수있는 대체 솔루션입니다.

function pwait() {
    while [ $(jobs -p | wc -l) -ge $1 ]; do
        sleep 1
    done
}

이를 사용하기 위해해야 ​​할 일은 &작업과 pwait 호출 뒤에 놓기 만하면됩니다. 매개 변수는 병렬 프로세스의 수를 제공합니다.

for i in *; do
    do_something $i &
    pwait 10
done

wait의 출력을 기다리는 대신 사용 하는 것이 더 jobs -p좋지만 주어진 작업이 모두 완료 될 때까지 기다리는 확실한 해결책은없는 것 같습니다.


답변

일반 bash 대신 Makefile을 사용한 다음 동시 작업 수를 지정하십시오. make -jX여기서 X는 한 번에 실행할 작업 수입니다.

또는 wait( ” man wait“)를 사용할 수 있습니다 . 여러 하위 프로세스를 시작하고 호출 wait합니다. 하위 프로세스가 완료되면 종료됩니다.

maxjobs = 10

foreach line in `cat file.txt` {
 jobsrunning = 0
 while jobsrunning < maxjobs {
  do job &
  jobsrunning += 1
 }
wait
}

job ( ){
...
}

작업 결과를 저장해야하는 경우 결과를 변수에 할당하십시오. wait변수에 포함 된 내용을 확인한 후 .


답변

루프를 다시 작성하는 대신 병렬화 유틸리티를 사용해보십시오. 나는 xjobs의 열렬한 팬입니다. 저는 항상 xjobs를 사용하여 네트워크를 통해 파일을 대량 복사합니다. 일반적으로 새 데이터베이스 서버를 설정할 때입니다.
http://www.maier-komor.de/xjobs.html


답변

make명령에 익숙한 경우 대부분의 경우 실행할 명령 목록을 makefile로 표현할 수 있습니다. 예를 들어 각각 * .output을 생성하는 * .input 파일에서 $ SOME_COMMAND를 실행해야하는 경우 makefile을 사용할 수 있습니다.

INPUT = a. 입력 b. 입력
OUTPUT = $ (INPUT : .input = .output)

%.출력 입력
    $ (SOME_COMMAND) $ <$ @

모두 : $ (OUTPUT)

그리고 그냥 실행

make -j <번호>

최대 NUMBER 개의 명령을 병렬로 실행합니다.