Developpez.com

Club des développeurs et IT pro
Plus de 4 millions de visiteurs uniques par mois

Developpez.com - Delphi
X

Choisissez d'abord la catégorieensuite la rubrique :


Le Multi-Processeur avec Delphi

Configuration d'une application SMP sous Delphi.


I. Introduction
II. Les affinitées
II-A. Application a Delphi
III. L'HyperThreading
III-A. Le Load Imbalance
III-B. Gestion des Load Imbalance


I. Introduction

Pour les applications serveur acceptant plusieurs connexions clientes, il est souvent nécéssaire de multi-threader les processus appellés afin de ne pas avoir a gérer un sheduler ou un queue de connexion.

La mise en pratique des applications multi-threadées n'est pas compliquée outre-mesure et permet dans le cas d'une serveur Multi-Processeur d'améliorer les pérformances de l'application, a condition de pouvoir gérer tout ça correctement.

Une des principale amélioration est d'authorisée l'utilisation de tout/certains processeurs du serveur, voir de certains processeurs logiques si l'hyperthreading est utilisable en fonction du type de thread.


II. Les affinitées

On nomme affinitée processeur un masque binaire qui permet de connaitre le comportement d'une application et de ses threads vis-a-vis des processeurs de la machine.

Supposons un serveur possédant 4 processeurs (CPU1, CPU2, CPU3, CPU4) : Dans le cas ou une application créée 60 threads, comment vont se répartir les threads de l'application (chaque thread s'éxecutant séparement) ? Et bien tout est question d'affinitée processeur. L'affinitée de l'application ou d'un processus se controle avec l'appel a la fonction SetProcessAffinityMask contenue dans kernel32.dll. Etudions d'un peu plus près cette fonction.
BOOL SetProcessAffinityMask(
HANDLE hProcess,
DWORD_PTR dwProcessAffinityMask);
Cette fonction permet d'affecter un affinityMask pour ses différents threads. Comme dis précédement, un AffinityMask est un masque binaire et il s'utilise de la manière suivante: Les processeur on une valeur en puissance de 2:

  • CPU1 = 1
  • CPU2 = 2
  • CPU3 = 4
  • CPU4 = 8
Pour l'exemple suivant, nous souhaitons travailler avec les processeurs CPU1 et CPU3, les autrse étant réservés par une autre application. La valeur du masque sera la somme des valeurs de CPU : Masque = CPU1(1)+CPU3(4) = 5;

La valeur de notre masque sera donc 5; De la même manière pour travailler avec seulement le processeur 4, le masque vaudra 8. Une fois cette valeur affectée, tout les threads créés utiliserons les processeurs disponibles. Mais de quelle manière seront ils répartis entre les processeurs si on demandre l'utilisation de plusieurs CPU ? Et bien c'est le kernel qui va décider de la répartition de la charge en fonction de la charge de chaque processeur. On appelle ça du LoadBalancing.

Il est toutefois possible d'affecter manuellement une affinitée à un thread a condition que celle-ci soit en accords avec l'affinitée de l'application principale. Par exemple, il n'est pas possible de demander l'affectation d'un thread au processeur CPU4 si seulement CPU1 et CPU3 sont définis lors de l'appel à SetProcessAffinityMask. Dans ce cas la fonction retournera False et une appel à GetLastError permettra de retrouver le problème.

L'affectation d'un thread a un AffinityMask se fait a travers l'appel a SetThreadAffinityMask. Il s'appelle de la même manière que la fonction précédente à la différence pret que c'est le Handle du thread qui doit être passé en paramètres.


II-A. Application a Delphi

procedure SetApplicationAffinity(han: THandle; Aff: Integer); type
TProcSetAppAffinityMask = function (hProcess:THandle; dwProcessAffinityMask:DWORD):BOOL; stdcall;
var
   h:HMODULE;
   p : Pointer;
begin

h:=LoadLibrary('kernel32'); if h>0 then try
   p:=GetProcAddress(h,'SetProcessAffinityMask');
   if p <> nil then
      if not TProcSetAppAffinityMask(p)(han,Aff) then begin
         Exit;
      end;
finally
       FreeLibrary(h);
end;
end;


procedure SetThreadAffinity(han: THandle; Aff: Integer); type
   TProcSetThreadAffinityMask = function (hProcess:THandle; dwProcessAffinityMask:DWORD):BOOL; stdcall;
var
   h:HMODULE;
   p : Pointer;
begin

h:=LoadLibrary('kernel32'); if h>0 then try
   p:=GetProcAddress(h,'SetThreadAffinityMask');
   if p<>nil then
      if not TProcSetThreadAffinityMask(p)(han,Aff) then begin
         Exit;
      end;
finally
       FreeLibrary(h);
end;
end;

III. L'HyperThreading

L'HyperThreading est une technologie qui permet une utilisation maximale des ressources systemes en créant des processeurs logiques qui partagent ces ressources. Vous l'avez surement déja vue ou utilisée, il sagit d'une simulation d'un Bi-Processeur avec un seul CPU possédant 2 Cores. Windows détecte donc 2 processeurs au démarage alors d'un seul est présent dans la machine (en réallité, il y a 1 seul processeur avec 2 noyeaux). On pourrais donc penser que les applications utilisées sur un processeur HyperThread seront plus rapides, le systeme étant capable de paralleliser les threads sur 2 CPU.

Si l'application n'est pas programmées otpimalement, il n'en est rien...


III-A. Le Load Imbalance

On appelle Load Imabalance le fait que la charge des travail ne soit pas répartie correctement entre les processeurs physiques et logiques aun moment donné. Par exemple, un processeur physique voit ses 2 processeurs logiques surchargés alors que l'autre processeurs reste en IDLE. Le systeme d'exploitation ne verra pas la différence étant donné que pour l'OS, l'Hyperthreading correspond a 2 processeurs distincts alors qu'en réalitée, c'est un et un seul CPU qui travaille.

Ce cas ne peux apparaitre que si il y a moins de Threads actifs que de processeurs logiques. Prenons un exemple: Une machine possède 2 processeurs physiques HyperThread (donc 4 processeurs en tout dont 2 logiques). Un serveur d'application créé 3 threads au lancement qui doivent calculer un chiffre d'affaire depuis une base de données. 2 threads vont s'executer sur un processeurs physique et 1 autre thread sur l'autre processeur physique. Le thread qui fonctionne seul sur 1 processeur a toutes les chances d'être plus performant car il n'a pas a partager les ressources d'un autre processeur (cache...). Nous somme donc un cas de Load Imbalance. Les performances des 2 threads seront dégradées par rapport a l'autre CPU du fait qu'elle devront partager les ressources du processeur pendant l'execution.

Dans le cas ci-dessus, il n'y a pas de configuration parfaite. Le Load Imbalance peut arriver a n'importe quel moment du fait que le Kernel de l'OS peut faire migrer des threads d'un CPU a l'autre en fonction de la charge.

Dans le cas de 2 processeurs distincts, le changement de CPU d'un thread a une très forte impacte sur les ressources systèmes du fait que les informations du cache ne sont pas les mêmes entre les processeurs.

Dans le cas de l'HyperThreading, les ressources étant partagées entre les threads, le changement de processeur n'a qu'une impacte minime sur les performances.


III-B. Gestion des Load Imbalance

Afin de gérer au mieux ces cas critiques (non, non, ce n'est pas l'OS qui vous rendra ce service...), il convient de programmer l'application de telle sorte qu'en fonction du type de Thread, l'AffinityMask de ce dernier sera géré par le programme. Prenons un programme qui possède 4 threads (notre programme de calcul de chiffre d'affaire). 2 threads (Thread A et Thread B) font les calculs de moyenne (Opération en float) et 2 autres threads (Thread C et Thread D) suppriment des enregistrements de la base de données (opération en entiers).

Afin d'optimiser au mieux l'application, il faut penser a éviter le partage des ressources CPU pour les threads qui font des calculs lourds (virgule flottante) et ne pas hésiter a séparer les threads qui ne font que des accès mémoires. Par exemple :

  • CPU1-->Thread A
  • CPU2-->Thread C
  • CPU3-->Thread B
  • CPU4-->Thread D
Permet que l'unitée de gestion en virgule flottante de chaque CPU soir dédiée a un thread pour cette application. On optimisera ainsi le partage des ressources processeurs et les performances de l'application.

Pour avoir plus d'informations, je ne peux que vous conseiller d'aller jeter un oeil sur le site d'en Intel afin de récupérer les informations sur la technologie HyperThreading.



Valid XHTML 1.1!Valid CSS!

Ce document est issu de http://www.developpez.com et reste la propriété exclusive de son auteur. La copie, modification et/ou distribution par quelque moyen que ce soit est soumise à l'obtention préalable de l'autorisation de l'auteur.
Responsables bénévoles de la rubrique Delphi : Gilles Vasseur - Alcatîz -