다중 프로세스/'스레드' BASH 스크립트가 되는 간단한 BASH 스크립트?

다중 프로세스/'스레드' BASH 스크립트가 되는 간단한 BASH 스크립트?

Mac Pro 2010/Mojarve OS에서 실행하는 다음 작업 BASH 스크립트가 있습니다.

#!/bin/bash

c=0
cnt=0

# count up wav files
cnt=$(find /hummdinger/LoCI/LoCI_orig/VO/WAV_Processed/ -name "*.wav" | wc -l)
echo "there are $cnt .wav voice samples."

# go through and run rhubarb on them
for f in $(find /hummdinger/LoCI/LoCI_orig/VO/WAV_Processed/ -name "*.wav")
do
    c=$((c+1))
    echo "$c of $cnt";
    f=$(basename "$f" .wav)
    /hummdinger/LoCI/LoCI_orig/TSV/rhubarb-lip-sync-1.10.0-osx/rhubarb /hummdinger/LoCI/LoCI_orig/VO/WAV_Processed/"$f".wav -o /hummdinger/LoCI/LoCI_orig/LOCI_GAME_FILES/Compiled/Windows/sync/"$f".tsv
done;

WAV 파일 목록을 가져와 각각을 살펴보고 파일을 스캔한 다음 출력을 생성하고 생성된 TSV 파일을 다른 곳에 저장합니다. '대황'의 핵심은 녹음(WAV 파일)에서 립싱크 정보를 생성하는 것입니다. 등등 등등 어쩌구 저쩌구.

이 스크립트의 한 가지 문제점은 약 3,000개의 wav 파일을 실행하는 데 ~10-12시간이 걸린다는 것입니다. 내 형편없는 비 ECC 램, 한 번은 완전히 손상되었고 다시는 사용하지 않겠다고 맹세한 Mac Mini 2018에서는 약 1시간이 걸렸습니다.시간.

하지만 이것은 Mac Pro입니다. 즉, 오래되었지만(2010년) 매우 안정적이며 12x Xeon을 갖추고 있습니다. 이것은 강도가 매우 낮은 작업이므로 단일 프로세서로 만들면 추가 혜택을 놓치게 됩니다. 저는 이 스크립트를 10-15-30개의 스레드로 작업하고 싶습니다. 이렇게 하면 속도가 빨라지고 한 시간 이내에 완료될 수 있기를 바랍니다. 대부분의 날은 아닙니다.

내 생각은 다음과 같습니다. WAV 디렉터리를 (total_files/15) 그룹으로 나누고 이 목록을 file1-15.txt에 넣은 다음 각 항목을 다시 읽고 15개의 개별 스레드에서 처리합니다. 하지만 제가 얻은 것은 여기까지입니다 :P

누구든지 이것을 다중 프로세스 스크립트로 만드는 데 도움을 줄 수 있습니까? 저는 아마추어이고 reddit의 도움을 받아 이 스크립트를 만들었습니다.

답변1

GNU Parallel을 사용하면 다음과 같은 작업을 수행할 수 있습니다.

rhubarb=/hummdinger/LoCI/LoCI_orig/TSV/rhubarb-lip-sync-1.10.0-osx/rhubarb 

find /hummdinger/LoCI/LoCI_orig/VO/WAV_Processed/ -name "*.wav" |
  parallel $rhubarb {} -o {.}.tsv

또는 (다른 디렉토리에 출력이 정말로 필요한 경우):

find /hummdinger/LoCI/LoCI_orig/VO/WAV_Processed/ -name "*.wav" |
  parallel $rhubarb {} -o /hummdinger/LoCI/LoCI_orig/LOCI_GAME_FILES/Compiled/Windows/sync/{/.}.tsv

답변2

인수를 반복하도록 스크립트를 작성하십시오. 예를 들어:

#!/bin/bash

rhubarb='/hummdinger/LoCI/LoCI_orig/TSV/rhubarb-lip-sync-1.10.0-osx/rhubarb'
outdir='/hummdinger/LoCI/LoCI_orig/LOCI_GAME_FILES/Compiled/Windows/sync/'

for fn in "$@"; do
    bn=$(basename "$fn" .wav)
    "$rhubarb" "$fn" -o "$outdir/$bn.tsv"
done

예를 들어 이것을 다른 이름 myscript1.sh으로 저장하고 chmod +x myscript1.sh.

이를 직접 실행할 수 있지만 각 파일을 순차적으로 처리합니다. 대신 GNU parallel또는 xargs -P. 예를 들어 처리할 파일 수를 보유한 코어 수로 나누는 다음과 같은 래퍼 스크립트를 사용합니다.

정확히 무엇을 하는지에 따라 rhubarb이는 CPU 바인딩보다 I/O 바인딩 작업에 더 가깝기 때문에 너무 많은 코어를 추가하는 것은 도움이 되지 않을 것입니다. 실제로 작업 속도가 느려질 수 있습니다. 디스크 I/O에 대한 경합이 너무 많습니다. 특히 SSD가 아닌 HDD에서 실행하는 경우 더욱 그렇습니다.

내가 사용하는 것보다 아래 스크립트에 cores=4또는 같은 것을 하드 코딩하고 싶을 수도 있습니다 . (나는 16개의 코어와 32개의 스레드가 있는 threadripper 1950x를 실행하고 있기 때문에 그렇게 썼습니다....그리고 원하지 않았습니다. 32개의 작업을 병렬로 실행하는 방법도 있습니다. 또한 에서 유용한 정보를 추출하는 방법의 예이기도 합니다 .cores=8lscpu | awk ...lscpu

또한 권장 사항: 드라이브가 두 개 이상인 경우 .wav 파일을 읽는 디렉터리가 한 드라이브에 있고 .tsv 파일을 쓰는 디렉터리가 다른 드라이브에 있도록 항목을 정렬해 보세요. 이렇게 하면 파일 읽기와 쓰기 사이의 I/O 경합이 제거됩니다. .tsv 파일이 크지 않으면 tmpfs 램디스크의 임시 디렉터리에 쓰고 스크립트 끝의 최종 위치로 이동합니다.

#!/bin/bash

wavdir="$1"

cores=$(lscpu | awk -F': +' '/^CPU\(s\):/ {cpus=$2};
                             /^Thread\(s\) per core:/ {tpc=$2};
                             END { print int(cpus / tpc) }')

count=$(find "$wavdir" -type f -name "*.wav" -print0 |
          perl -0ne '$c++;END{print $c}')

let files_per_thread=count/cores

find "$wavdir" -type f -name "*.wav" -print0 |
    xargs -0 -r -L "$files_per_thread" -P "$cores" /path/to/myscript1.sh

예를 들어 이것을 다른 이름 myscript2.sh으로 저장하고 chmod +x myscript2.sh.

이것은 명령줄이나 cron 등에서 실행하는 스크립트입니다. 차례로 xargs여러 인스턴스를 myscript1.sh병렬로 실행하는 데 사용됩니다.

다음과 같이 실행하세요:

./myscript2.sh /hummdinger/LoCI/LoCI_orig/VO/WAV_Processed/

그런데, 이는 파일 이름 사이의 구분 기호로 NUL을 사용하므로 모든 파일 이름에 사용하는 것이 안전합니다(개행 문자를 파일 이름 구분 기호로 사용하는 것은 파일 이름 내에서 유효한 문자이기 때문에 개행 문자를 사용하는 것은 안전하지 않습니다).

관련 정보