
Eu tenho o seguinte script BASH funcional, que executo em um sistema operacional 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;
Ele pega uma lista de arquivos WAV, examina cada um, verifica o arquivo e, em seguida, produz uma saída e armazena os arquivos TSV gerados em outro lugar. O objetivo do 'ruibarbo' é produzir informações de sincronização labial de uma gravação (os arquivos WAV). etc. etc., blá, blá.
O único problema com este script é que leva cerca de 10 a 12 HORAS para executar cerca de 3.000 arquivos wav. No meu RAM pior, não ECC, Mac Mini 2018, uma vez-corrompeu-todo-e-fiz-um-juro-de-nunca-usar-de-novo, demorou cerca de3horas.
Mas este é um Mac Pro, ou seja, embora seja antigo (2010), é muito confiável e possui 12x Xeons. Este é um trabalho de intensidade bastante baixa, então estou perdendo aquele suco extra ao torná-lo um processador único. Eu só quero que esse script funcione com 10-15-30 threads e espero que isso acelere e seja concluído em uma hora ou menos; não a maior parte do dia.
Meus pensamentos são: divida o diretório de WAVs em grupos de (total_files/15), coloque essas listagens em file1-15.txt, depois leia cada uma delas e processe-as em 15 threads separados. Mas isso foi tudo que consegui :P
Alguém pode ajudar a tornar este script de processo múltiplo? Sou amador e fiz esse script com ajuda do reddit.
Responder1
Com o GNU Parallel você pode fazer algo assim:
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
Ou (se você realmente precisar da saída em um diretório diferente):
find /hummdinger/LoCI/LoCI_orig/VO/WAV_Processed/ -name "*.wav" |
parallel $rhubarb {} -o /hummdinger/LoCI/LoCI_orig/LOCI_GAME_FILES/Compiled/Windows/sync/{/.}.tsv
Responder2
Escreva seu script para que ele repita seus argumentos. Por exemplo:
#!/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
Salve isso como, por exemplo, myscript1.sh
e torne-o executável com chmod +x myscript1.sh
.
Você pode executar isso diretamente, mas processará cada arquivo sequencialmente. Em vez disso, você deseja executá-lo com GNU parallel
ou xargs -P
. Por exemplo, com um script wrapper como o seguinte, que divide o número de arquivos a serem processados pelo número de núcleos que você possui.
Observe que, dependendo exatamente do que rhubarb
acontece, é provável que seja mais uma tarefa vinculada à E/S do que à CPU, portanto, adicionar muitos núcleos não ajudará - na verdade, provavelmente tornará as coisas mais lentas, pois haverá haverá muita disputa para E/S de disco... especialmente se você estiver executando isso em um HDD em vez de um SSD.
Você pode querer codificar algo como cores=4
ou cores=8
no script abaixo, em vez de usar lscpu | awk ...
como eu fiz (escrevi assim porque estou executando um threadripper 1950x com 16 núcleos e 32 threads... e não queria para executar 32 jobs em paralelo e também como um exemplo de como você pode extrair informações úteis de lscpu
).
Também recomendado: se você tiver mais de uma unidade, tente organizar as coisas de forma que o diretório de onde você lê os arquivos .wav esteja em uma unidade e o diretório em que você grava os arquivos .tsv esteja em outra. Isso eliminará a contenção de E/S entre a leitura e a gravação dos arquivos. Se os arquivos .tsv não forem grandes, grave-os em um diretório temporário em um ramdisk tmpfs e mova-os para seu local final no final do script.
#!/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
salve isso como, por exemplo, myscript2.sh
e torne-o executável com chmod +x myscript2.sh
.
Este é o script que você executa na linha de comando ou cron, ou qualquer outra coisa. Ele, por sua vez, usa xargs
para executar várias instâncias myscript1.sh
em paralelo.
Execute como:
./myscript2.sh /hummdinger/LoCI/LoCI_orig/VO/WAV_Processed/
BTW, isso usa NUL como separador entre nomes de arquivos, portanto é seguro usar com qualquer nome de arquivo (usar nova linha como separador de nome de arquivo não é seguro porque nova linha é um caractere válido dentro de um nome de arquivo).