A la recherche d'un moyen d'affecter (en bash) un processus à l'un des 4 coeurs de mon processeur (AMD Phenom 9950) pour ré-encoder plusieurs fichiers avi au format mp4 en même temps. J'ai "plus ou moins" trouvé la solution à la lecture de
cette page. Il me semble judicieux de la lire pour comprendre la suite.
"plus ou moins" : oui car il en découle qu'un processus n'est affecté qu'à un cœur. Non, car je ne peux pas choisir le coeur.
Toujours est-il que le principe de cet article m'a plu mais il m'a semblait perfectible. En effet, le principe de la meilleure solution proposée est de lancer 2 scripts de ré-encodage en parallèle (car 2 coeurs). Le premier réencodant les fichiers en position impaire passés en argument d'un script principale. Le 2nd script, réencodant les fichiers en position paire passés en argument du script principale.
A y réfléchir, le traitement des fichiers "paires" peut-être bien plus long que le réencodage des fichiers "impaires". Et vice-versa. En conséquence, l'un des 2 coeurs, sera plus utilisé que l'autre.
C'est pourquoi la solution est perfectible. Et pour cela il suffirait de lancer autant de processus de ré-encodage qu'il y a de coeurs et dès que l'un de ces processus est terminé, lancer un nouveau processus avec le fichier suivant passé en argument du script.
J'ai donc écris ce script "paralleliseur.sh" que je vous fais partager car je pense qu'il pourra vous être utile.
Attention, la moindre modification pour avoir un impacte important sur le rôle qu'il doit jouer !
#!/bin/bash
set -mb
export commence=0
function lanceur
{
"${1}" "${2}"
}
function fin_process
{
if [[ ${num_args} -le ${#args[*]} && ${commence} -eq 1 ]]; then
lanceur "${tache}" "${args[${num_args}]}" &
num_args=$((num_args+1))
fi
}
nb_proc=$(grep -c 'processor[[:blank:]]*:.*' /proc/cpuinfo)
export tache="$1"
export num_args=1
for ((index=2;index<=$#;index++))
do
args[$((index-1))]=${!index}
done
export args
trap "fin_process" SIGCHLD
# on lance autant de process qu'il y a de processeur/coeur
for(( num_proc=1 ; num_proc <= ${nb_proc} ; num_proc++))
do
if [ $num_args -le ${#args[@]} ]; then
commence=1
lanceur "${tache}" "${args[${num_args}]}" &
num_args=$((num_args+1))
fi
done
wait
Son utilisation :
$1 : Script à paralléliser
$2.....$i : liste des arguments à traiter
Pour example :
paralleliseur.sh reencoder.sh ~/divx/album1/saison1/*.avi
script "reencoder.sh" :
#!/bin/bash
source="$1"
debut="$(date +"%k:%M:%S")"
debut_s=$(date +%s)
ffmpeg -threads 4 -y -i "${source}" -r 29.97 -vcodec libxvid -s 640x480 -aspect 16:9 -maxrate 1500k -b 1250k -qmin 3 -qmax 5 -bufsize 4096 -mbd 2 -flags +4mv+trell -aic 2 -cmp 2 -subcmp 2 -g 300 -acodec libfaac -ar 48000 -ab 80k -ac 2 -s 320x240 "${source%.*}.mp4" </dev/null &>/dev/null
fin_s=$(date +%s)
fin="$(date +"%k:%M:%S")"
echo "traitement de ${source} en $((fin_s-debut_s)) secondes (${debut} - ${fin})."
Note : le gain n'est pas en temps de traitement unitaire... mais globale !.