간단한 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 파일을 다른 곳에 저장합니다. "rhubarb"의 목적은 녹음(WAV 파일)에서 립싱크 정보를 생성하는 것입니다. 어쩌고 저쩌고 저쩌고.

이 스크립트의 한 가지 문제점은 약 3,000개의 wav 파일을 실행하는 데 약 10-12시간이 걸린다는 것입니다. 내 형편없는 ECC RAM이 아닌 Mac Mini 2018에서 한 번에 고장이 나서 다시는 사용하지 않겠다고 다짐했는데 대략 10분 정도 걸립니다.시간.

하지만 이것은 Mac Pro입니다. 즉, 오래된 제품(2010년)임에도 매우 안정적이고 12개의 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로 인해 속도가 느려질 수 있습니다. O 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을 사용하므로 모든 파일 이름에 사용하는 것이 안전합니다(줄 바꿈은 파일 이름에서 유효한 문자이므로 줄 바꿈을 파일 이름 구분 기호로 사용하는 것은 안전하지 않습니다).

관련 정보