Contenu | Rechercher | Menus

Annonce

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

À propos de l'équipe du forum.

#1 Le 30/01/2009, à 22:13

Totor

Tirer partie de ses coeurs

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 !.


-- Lucid Lynx --

Hors ligne

#2 Le 01/02/2009, à 15:59

Totor

Re : Tirer partie de ses coeurs

Bonjour,

Ayant trouvé la réponse à ma question initiale qui était : Comment faire tourner une tâche sur un seul processeur, j'ai modifié et amélioré le script.

Pour information, il existe un outil pour changer l'affinité d'un processus avec un processeur : schedtool

#!/bin/bash

#*************************************************************************************************************************
# Nom : paralleliseur.sh												 *
# Objet : Parallélise en traitement en fonction du nombre de processeur/coeur						 *
# La tâche à parraléliser est en position 1 des arguments du script.							 *
# Les arguments > 1 seront successivement passés aux tâches exécutées. Mais un argument ne sera utilisé qu'une seule fois*
# Le principe d'utilisation est à l'identique de xargs à l'exception près : les arguments ne peuvent être lus de l'entrée*
# standard.														 *
# Ce script est utile lorsq'il est question de traitements lourds à exécuter successivement et lorsque la machine possède*
# au moins 2 processeurs/coeurs.											 *
#*************************************************************************************************************************
# Version	Date		Objet											 *
#	1.0	30/01/2009	Création										 *
#	1.1	01/02/2009	Utilisation de l'utilitaire schedtool, permettant de positionner les affinités		 *
#*************************************************************************************************************************
# Dépendances :														 *
#	- schedtool (http://freequaos.host.sk/schedtool/). (V.1.3.0)							 *
#*************************************************************************************************************************
set -mb

#*************************************************************************************************************************
# Nom : lanceur													 	 *
# Objet : exécute la tâche donnée avec l'argument suivant								 *
#*************************************************************************************************************************
# Version	Date		Objet											 *
#	1.0	01/02/2009	Création										 *
#*************************************************************************************************************************
# paramètres :														 *
#*************************************************************************************************************************
function lanceur
{
  schedtool -a ${no_proc} -e "${tache}" "${args[${num_args}]}" &
  # sauvegarde du PID dans le tableau à l'indice du No du processeur avec lequel l'affinité est positionnée
  lstPID[${no_proc}]=$!
  # incrémentation du No d'argument
  num_args=$((num_args+1))
}

#*************************************************************************************************************************
# Nom : fin_process													 *
# Objet : fonction exécutée lorsqu'un process est terminé dans l'environnement d'exécution en cours.			 *
#*************************************************************************************************************************
# Version	Date		Objet											 *
#	1.0	01/02/2009	Création										 *
#*************************************************************************************************************************
# paramètres :														 *
#*************************************************************************************************************************
function fin_process
{
  if [[ ${num_args} -le ${#args[@]} && ${commence} -eq 1 ]]; then
    # recherche du processeur avec lequel le processus avait une affinité. Si non trouvé, on affecte à tous les processeurs
    no_proc="0xf"
    for((num_proc=0;num_proc<${nb_proc};num_proc++))
    do
      if [ -z "$(ps -o state --pid ${lstPID[${num_proc}]} --no-headers)" ]; then
	# ce process n'existe plus, avec l'indice no_proc, on en déduit le processeur avec lequel il avait une affinité 
         no_proc=${num_proc}
      fi
    done
    
    # on relance avec l'argument suivant
    lanceur
  fi
}

# précautions d'usage
[ $# -lt 2 ] && { echo "Nombre d'argument incorrecte : $(basename "$0") <tâche> <arg1> <arg2> ... <argn>"; exit 1; }

# recherche du nombre de coeurs/processeurs
nb_proc=$(grep -c 'processor[[:blank:]]*:.*' /proc/cpuinfo)

# sauvegarde des paramétres
for ((index=2;index<=$#;index++))
do
  args[$((index-1))]=${!index}
done

export tache="$1"
export num_args=1
export args
export lstPID=
export commence=0

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
    # calcul du No du processeur avec lequel l'affinité sera positionnée
    no_proc=$((num_proc-1))
    # on indique que l'on a commencé à lancer des jobs
    commence=1
    # exécution de la tâche
    lanceur
  fi
done

# on attend la fin de tous les processus lancés
wait

Dernière modification par Totor (Le 01/02/2009, à 16:37)


-- Lucid Lynx --

Hors ligne

#3 Le 02/02/2009, à 21:26

Totor

Re : Tirer partie de ses coeurs

Bon, en fait, la version initiale du script est à mettre à la poubelle hmm
En effet, l'option -P de xargs est là pour ça yikes
...mais l'affinité n'est pas gérer ....


-- Lucid Lynx --

Hors ligne