고유한 입력 파일부터 고유한 출력 파일까지 병렬 처리 사용

고유한 입력 파일부터 고유한 출력 파일까지 병렬 처리 사용

입력 파일로 가득 찬 디렉토리(각 파일에 많은 입력 행이 포함되어 있음)가 있는 쉘 스크립트 문제가 있습니다. 이를 개별적으로 처리하여 각 출력을 고유한 파일로 리디렉션해야 합니다(일명 file_1.input을 file_1에서 캡처해야 함). .output 등).

준비, 프로세서를 압도하지 않도록 일종의 타이머/계산 기술을 수행하면서 디렉터리의 각 파일을 반복하고 명령을 실행합니다(각 프로세스에 일정한 런타임이 있다고 가정). 그러나 이것이 항상 그런 것은 아니라는 것을 알고 있으므로 "병렬"과 같은 솔루션을 사용하는 것이 사용자 정의 코드를 작성하지 않고 쉘 스크립트 멀티스레딩을 얻는 가장 좋은 방법인 것 같습니다.

각 파일을 병렬로 처리하고 코어를 효율적으로 관리할 수 있는 몇 가지 방법을 생각해 보았지만 모두 엉망인 것 같습니다. 나는 매우 간단하다고 생각하는 사용 사례를 가지고 있으므로 가능한 한 깔끔하게 유지하고 싶습니다(병렬 예제의 어떤 것도 나에게는 문제가 되지 않는 것 같습니다.

어떤 도움이라도 대단히 감사하겠습니다!

입력 디렉터리의 예:

> ls -l input_files/
total 13355
location1.txt
location2.txt
location3.txt
location4.txt
location5.txt

스크립트:

> cat proces_script.sh
#!/bin/sh

customScript -c 33 -I -file [inputFile] -a -v 55 > [outputFile]

고쳐 쓰다: 아래 Ole의 답변을 읽은 후 누락된 부분을 모아서 병렬 구현을 할 수 있었습니다. 그의 답변은 훌륭했지만, 제가 추가로 조사한 내용과 메모는 다음과 같습니다.

전체 프로세스를 실행하는 대신 내 환경에서 솔루션을 시연하기 위해 개념 증명 명령으로 시작했습니다. 내 두 가지 구현(및 설명)을 참조하세요.

find /home/me/input_files -type f -name *.txt | parallel cat /home/me/input_files/{} '>' /home/me/output_files/{.}.out

find(문제를 일으킬 수 있는 ls 아님)를 사용하여 입력 파일 디렉터리에서 적용 가능한 모든 파일을 찾은 다음 해당 내용을 별도의 디렉터리와 파일로 리디렉션합니다. 위의 문제는 읽고 리디렉션하는 것입니다(실제 스크립트는 간단함). 따라서 스크립트를 cat으로 바꾸는 것이 좋은 개념 증명입니다.

parallel cat '>' /home/me/output_files/{.}.out :::  /home/me/input_files/*

두 번째 솔루션은 병렬 입력 변수 패러다임을 사용하여 파일을 읽지만 초보자에게는 더 혼란스럽습니다. 나에게는 find a와 파이프라인을 사용하는 것이 내 요구에 잘 부합했습니다.

답변1

GNU Parallel은 다음과 같은 작업을 위해 설계되었습니다.

parallel customScript -c 33 -I -file {} -a -v 55 '>' {.}.output ::: *.input

또는:

ls | parallel customScript -c 33 -I -file {} -a -v 55 '>' {.}.output

각 CPU 코어는 하나의 작업을 실행합니다.

다음과 같이 간단하게 GNU Parallel을 설치할 수 있습니다.

wget https://git.savannah.gnu.org/cgit/parallel.git/plain/src/parallel
chmod 755 parallel
cp parallel sem

자세히 알아보려면 GNU Parallel 소개 비디오를 시청하세요. https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

답변2

이를 수행하는 표준 방법은 대기열을 설정하고 대기열에서 콘텐츠를 가져와 처리하는 방법을 아는 작업자를 원하는 만큼 생성하는 것입니다. fifo(명명된 파이프라고도 함)를 사용하여 이러한 프로세스 간에 통신할 수 있습니다.

다음은 이 개념을 보여주는 간단한 예입니다.

간단한 대기열 스크립트:

#!/bin/sh
mkfifo /tmp/location-queue
for i in inputfiles/*; do
  echo $i > /tmp/location-queue
done
rm /tmp/location-queue

작업자도 있습니다.

#!/bin/sh
while read file < /tmp/location-queue; do
  process_file "$file"
done

process_file이는 작업자 스레드 어딘가에 정의될 수 있으며 필요한 모든 작업을 수행합니다.

이 두 부분이 있으면 대기열 프로세스와 작업자 프로세스를 시작하는 간단한 모니터를 가질 수 있습니다.

모니터링 스크립트:

#!/bin/sh
queue.sh &
num_workers="$1"
i=0
while [ $i < $num_workers ]; do
  worker.sh &
  echo $! >> /tmp/worker.pids
  i=$((i+1))
done
monitor_workers

거기 있어요. 이렇게 하면 모니터에서 fifo를 설정하고 대기열 및 작업자 스레드에 대한 경로를 전달하여 이들이 연결되어 fifo의 특정 위치에 고정되지 않도록 하는 것이 좋습니다. 나는 당신이 그것을 읽을 때 당신이 무엇을하고 있는지 명확하게 알 수 있도록 내 대답에서 특별히 이런 방식으로 설정했습니다.

답변3

다른 예시:

ls *.txt | parallel 'sort {} > {.}.sorted.txt'

다른 예제는 불필요하게 복잡하다는 것을 알았지만 대부분의 경우 위의 예제가 여러분이 찾고 있던 것일 수 있습니다.

답변4

다음은 현재 디렉터리에 있는 대규모 파일 세트에 대한 동일한 명령입니다.

#!/bin/sh
trap 'worker=`expr $worker - 1`' USR1  # free up a worker
worker=0  # current worker
num_workers=10  # maximum number of workers
for file in *.txt; do
    if [ $worker -lt $num_workers ]; then
        {   customScript -c 33 -I -file $file -a -v 55 > `basename $file .txt`.outtxt 
            kill -USR1 $$ 2>/dev/null  # signal parent that we're free
        } &
        echo $worker/$num_worker $! $file  # feedback to caller
        worker=`expr $worker + 1`
    else
        wait # for a worker to finish
    fi
done

이는 customScripttxt파일에서 실행되어 출력을 outtxt파일에 저장합니다. 필요에 따라 변경합니다. 이를 작동시키는 핵심은 SIGUSR1을 사용하여 신호 처리이므로 하위 프로세스가 완료되었음을 상위 프로세스에 알릴 수 있습니다. 스크립트의 대부분 명령문이 쉘 스크립트에 SIGCHLD 신호를 생성하므로 SIGCHLD를 사용해도 아무런 효과가 없습니다. 귀하의 명령을 로 바꾸려고했는데 sleep 1프로그램은 0.28s의 사용자 CPU와 0.14s의 시스템 CPU를 사용했습니다. 이는 약 400개의 파일만 포함했습니다.

관련 정보