
Ich habe das folgende funktionierende BASH-Skript, das ich auf einem Mac Pro 2010/Mojarve OS ausführe:
#!/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;
Es nimmt eine Liste von WAV-Dateien, geht jede einzelne durch, scannt die Datei, erzeugt dann eine Ausgabe und speichert die generierten TSV-Dateien an einem anderen Ort. Der Sinn von „rhubarb“ besteht darin, aus einer Aufnahme (den WAV-Dateien) Lippensynchronisationsinformationen zu erzeugen. usw. usw. bla bla.
Das einzige Problem mit diesem Skript ist, dass es ~10-12 STUNDEN braucht, um über 3.000 WAV-Dateien auszuführen. Auf meinem schlechteren, nicht-ECC-RAM, der einmal alles beschädigt hat und auf dem ich geschworen habe, ihn nie wieder zu verwenden, Mac Mini 2018, dauerte es ungefähr3Std.
Aber das ist ein Mac Pro, das heißt, er ist zwar alt (2010), aber sehr zuverlässig und hat 12x Xeons. Das ist ziemlich wenig intensive Arbeit, also verpasse ich diese Extraleistung, wenn ich ihn zu einem Einzelprozessor mache. Ich möchte nur, dass dieses Skript mit 10-15-30 Threads funktioniert, und hoffentlich beschleunigt das die Arbeit und ich bin in einer Stunde oder weniger fertig, nicht in der Hälfte des Tages.
Meine Gedanken sind: das Verzeichnis der WAVs in Gruppen von (total_files/15) aufteilen, diese Listen in file1-15.txt ablegen, dann jede einzelne wieder lesen und in 15 separaten Threads verarbeiten. Aber weiter bin ich nicht gekommen :P
Kann mir jemand dabei helfen, daraus ein Mehrprozess-Skript zu machen? Ich bin ein Amateur und habe dieses Skript mit Hilfe von Reddit erstellt.
Antwort1
Mit GNU Parallel können Sie etwa Folgendes tun:
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
Oder (wenn Sie die Ausgabe wirklich in einem anderen Verzeichnis benötigen):
find /hummdinger/LoCI/LoCI_orig/VO/WAV_Processed/ -name "*.wav" |
parallel $rhubarb {} -o /hummdinger/LoCI/LoCI_orig/LOCI_GAME_FILES/Compiled/Windows/sync/{/.}.tsv
Antwort2
Schreiben Sie Ihr Skript so, dass es seine Argumente durchläuft. Beispiel:
#!/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
Speichern Sie dies zB als myscript1.sh
und machen Sie es mit ausführbar chmod +x myscript1.sh
.
Sie können dies direkt ausführen, es verarbeitet jedoch jede Datei nacheinander. Stattdessen sollten Sie es mit GNU parallel
oder ausführen xargs -P
. Beispielsweise mit einem Wrapper-Skript wie dem folgenden, das die Anzahl der zu verarbeitenden Dateien durch die Anzahl Ihrer Kerne teilt.
Beachten Sie, dass es sich hierbei, je nachdem, was genau rhubarb
passiert, wahrscheinlich eher um eine E/A-gebundene als eine CPU-gebundene Aufgabe handelt. Das Hinzufügen zu vieler Kerne hilft also nicht weiter. Tatsächlich wird es die Dinge wahrscheinlich verlangsamen, da es zu viele Konkurrenz bei den Festplatten-E/A gibt, insbesondere, wenn Sie dies auf einer Festplatte und nicht auf einer SSD ausführen.
cores=4
Vielleicht möchten Sie etwas wie oder cores=8
im folgenden Skript fest codieren, anstatt es so zu verwenden lscpu | awk ...
wie ich (ich habe es so geschrieben, weil ich einen Threadripper 1950x mit 16 Kernen und 32 Threads verwende … und ich wollte nicht 32 Jobs parallel ausführen. Und auch als Beispiel, wie Sie nützliche Informationen daraus extrahieren können lscpu
).
Ebenfalls empfohlen: Wenn Sie über mehr als ein Laufwerk verfügen, versuchen Sie, die Dinge so zu arrangieren, dass sich das Verzeichnis, aus dem Sie die WAV-Dateien lesen, auf einem Laufwerk befindet und das Verzeichnis, in das Sie die TSV-Dateien schreiben, auf einem anderen. Dadurch werden E/A-Konflikte zwischen dem Lesen und Schreiben der Dateien vermieden. Wenn die TSV-Dateien nicht sehr groß sind, schreiben Sie sie in ein temporäres Verzeichnis auf einer tmpfs-Ramdisk und verschieben Sie sie am Ende des Skripts an ihren endgültigen Speicherort.
#!/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
Speichern Sie dies zB als myscript2.sh
und machen Sie es mit ausführbar chmod +x myscript2.sh
.
Dies ist das Skript, das Sie über die Befehlszeile oder Cron oder was auch immer ausführen. Es wiederum wird verwendet, xargs
um mehrere Instanzen myscript1.sh
parallel auszuführen.
Führen Sie es wie folgt aus:
./myscript2.sh /hummdinger/LoCI/LoCI_orig/VO/WAV_Processed/
Übrigens wird hier NUL als Trennzeichen zwischen Dateinamen verwendet, sodass die Verwendung mit jedem Dateinamen sicher ist (die Verwendung von Newline als Dateinamentrennzeichen ist nicht sicher, da Newline ein gültiges Zeichen innerhalb eines Dateinamens ist).