簡單的 BASH 腳本變成多進程/「執行緒」BASH 腳本?

簡單的 BASH 腳本變成多進程/「執行緒」BASH 腳本?

我有以下有效的 BASH 腳本,我在 Mac Pro 2010/Mojarve 作業系統上執行該腳本:

#!/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 內存、一次性全部損壞並且我發誓再也不會使用它的 Mac Mini 2018 上,大約需要3小時。

但這是一台 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

您可以直接運行它,但它將按順序處理每個文件。相反,您想使用 GNUparallelxargs -P.例如,使用如下所示的包裝器腳本,它將要處理的檔案數除以您擁有的核心數。

請注意,根據具體rhubarb執行的操作,這可能更多是 I/O 密集型任務,而不是 CPU 密集型任務,因此添加太多核心不會有幫助 - 事實上,它可能會減慢速度,因為磁碟I/O 爭用過多...特別是如果您在HDD 而不是SSD 上運行它。

您可能想要在下面的腳本中硬編碼類似cores=4或 的內容,而不是像我那樣使用(我這樣寫是因為我正在運行具有16 個核心和32 個線程的threadripper 1950x......並且我不想並行運行 32 個作業,並作為如何從中提取有用資訊的範例)。cores=8lscpu | awk ...lscpu

另建議:如果您有多個驅動器,請嘗試進行安排,以便您從中讀取 .wav 檔案的目錄位於一個驅動器上,而將 .tsv 檔案寫入其中的目錄位於另一個驅動器上。這將消除讀取和寫入檔案之間的 I/O 爭用。如果 .tsv 檔案不大,請將它們寫入 tmpfs ramdisk 上的臨時目錄,並將它們移至腳本末尾的最終位置。

#!/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 作為檔案名稱之間的分隔符,因此可以安全地與任何檔案名稱一起使用(使用換行符作為檔案名稱分隔符不安全,因為換行符是檔案名稱中的有效字元)。

相關內容