Caculer une factorielle avec plusieurs coeur de calcul et afficher les temps d'exécutions

Description

Pour calculer une factorielle sur un ou plusieurs coeurs de calcul.
Ce module utilise un mutex pour synchroniser les différentes tache.
Affiche le temps d'execution et une boucle de temporisation permet de ralentir un peut le calcul.
Utilisa un thread qui attend que les N threads déclarés soient terminés.
N correspond au nombre de coeur de calcul présent sur le µ-microprocesseur.
Permet ainsi d'optimiser avec un traitement paralléle toutes les boucles demandant une forte charge de calcul.

Source / Exemple :


unit factorielle;

//Ecrit par denis bertin stéphane le 25 juillet 2013.
//Pour calculer une factorielle sur un ou plusieurs coeurs de calcul.
//Ce module utilise un mutex pour synchroniser les différentes tache.
//Affiche le temps d'execution et une boucle de temporisation permet de ralentir un peut le calcul.

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TAffichage_factorielle = class(TForm)
    Label1: TLabel;
    Edit_nombre: TEdit;
    Label2: TLabel;
    Edit_resultat: TEdit;
    Button1: TButton;
    Label3: TLabel;
    Edit_mono: TEdit;
    Edit_multi: TEdit;
    Label4: TLabel;
    result_multi_coeur: TLabel;
    Edit_result_multi_coeur: TEdit;
    Label_sur_x_coeur: TLabel;
    procedure Button1Click(Sender: TObject);
  private
    { Déclarations privées }
  public
    { Déclarations publiques }
  end;

var
  Affichage_factorielle: TAffichage_factorielle;

implementation

{$R *.dfm}

uses SyncObjs;

const Number_of_process_unit = 31;

var ThreadsRunning:integer;
var Lock:SyncObjs.TCriticalSection;
var Table_thread : array[0..Number_of_process_unit] of extended;

type

    // Objet de gestion des affinités processeur.
    TCPU = Class

    Protected
      FCPUMask  : Array[0..Number_of_process_unit] Of Boolean ;    // Mapping physique des processeurs autorisés pour le processus courant.
      FCPUMap   : Array[0..Number_of_process_unit] Of Cardinal ;   // Mapping logique des processeurs, contient un masque d'affinité.
      FCPUCount : Cardinal ;                   // Nombre de processeurs utilisables, conditionne la limite supérieure de CPUMap.

      // Accesseur "Count".
      Function GetCPUCount : Cardinal ;

    Public
      // Constructeur
      Constructor Create ;

      // Récupération du nombre de CPU / coeurs du système.
      // Les processeurs sont ensuite numérotés de 0 à (Count-1).
      Property Count : Cardinal Read GetCPUCount ;

      // Force le thread courant à basculer sur le processeur passé en paramètre.
      // Lève une exception en cas d'erreur.
      // Ne lève PAS d'erreur (mais ne fait rien non plus) en cas de numéro de CPU invalide.
      Procedure SwitchTo ( Const CPUIndex : Cardinal ) ;

    End Platform ;

type

  TSeulement_Wait = class(TThread)
    Constructor Create;
    protected procedure Execute; override;
    end;

type

  Thread_Factorielle = class(TThread)
    Constructor Create(un_index,a_int_begin,a_int_ending:integer);
    Procedure ThreadDone(Sender: TObject);
    protected procedure Execute; override; {Méthode recouverte en français}
    Public
      index,commencement,finissement:integer; {Denis B le 20.7.2013}
    end;

{ Cette classe TCPU importé depuis internet le saviez-vous ?}

constructor TCPU.Create;
begin
     // Initialisation des structures d'affinité processeur.
     FCPUCount:=0;
     FillChar(FCPUMask,SizeOf(FCPUMask),0);
     FillChar(FCPUMap,SizeOf(FCPUMap),0);
     // Calcul direct du nombre de CPU.
     GetCPUCount;
end;

Function TCPU.GetCPUCount : Cardinal ;
  Var I, ProcessMask, SystemMask : Cardinal ;
  Begin
     // Inutile de tout déclencher si on a déjà le nombre de CPU...
     If (FCPUCount=0) Then
        Begin
        // On va récupérer le nombre de CPU.
        FCPUCount:=0 ;
        // On compte les bits d'affinité.
        Win32Check(GetProcessAffinityMask(GetCurrentProcess,ProcessMask,SystemMask));
        // Bon, on a le masque : on va calculer sa taille.
        For I:=0 To pred(Number_of_process_unit) Do
            Begin
            // Explosage du masque binaire vers un tableau de booléens.
            FCPUMask[I]:=(ProcessMask And (1 Shl I))<>0 ;
            // On compte les CPU utilisables, et on crée la map d'assignation.
            // Le contenu de CPUMap, à l'indice donné, est donc le masque d'affinité à utiliser.
            If FCPUMask[I] Then
               Begin
               FCPUMap[FCPUCount]:=(1 Shl I) ;
               Inc(FCPUCount);
               End;
            End;
        End;
     // Le nombre de CPU est correct quel que soit le cas, on renvoie donc le résultat.
     Result:=FCPUCount ;
  End;

Procedure TCPU.SwitchTo ( Const CPUIndex : Cardinal ) ;
  Begin
     If (CPUIndex<FCPUCount) Then
        If (SetThreadAffinityMask(GetCurrentThread,FCPUMap[CPUIndex])=0) Then
           RaiseLastOSError ;
  End;

Constructor TSeulement_Wait.Create;
  begin
  inherited Create(False);
  self.Priority:=tpLowest;
  end; {TSeulement_Wait.Create}

//Utilisation d'un Mutex pour ne pas décrémenter le compteur deux fois en même temps.
Procedure TSeulement_Wait.Execute;
  var encore:boolean;
  begin
  encore:=True;
	while encore do
	  begin
		Lock.Enter;
		encore:=ThreadsRunning<>0;
		Lock.Leave;
    end; {While encore}
  end; {TSeulement_Wait.Execute}

Constructor Thread_Factorielle.Create(un_index,a_int_begin,a_int_ending:integer);
  begin
  inherited Create(False);
  self.FreeOnTerminate:=True;
  self.index:=un_index;
  self.commencement:=a_int_begin;
  self.finissement:=a_int_ending;
  end;

procedure Thread_Factorielle.Execute;
  var i,j:integer;
      s:extended;
  begin
  For i:=self.commencement to self.finissement do
    begin
    Table_thread[self.index]:=Table_thread[self.index]*i;
    //Boucle de temporisation
    for j:=1 to 20000 do s:=sin(j);
    end;
  end;

Procedure Thread_Factorielle.ThreadDone(Sender: TObject);
		begin
		Lock.Enter;
		Dec(ThreadsRunning); //Pour que la fin du wait attend que ThreadsRunning=0!
		Lock.Leave;
		end;

function Convertir_ce_numerique_en_string_separer_par_des_point(a_value:integer):string;
  var i,j:integer;
			s,ss:string;
	begin
	ss:='';
	s:=inttostr(a_value);
	j:=length(s);
	for i:=1 to length(s) do
		begin
		ss:=ss+char(s[i]);
		dec(j);
		if (j mod 3=0) and (j<>0) then
			ss:=ss+'.';
		end;
	Convertir_ce_numerique_en_string_separer_par_des_point:=ss;
  end;

procedure TAffichage_factorielle.Button1Click(Sender: TObject);
var i,j,k,jusqua,ithread:integer;
    n,s:Extended;
    time_begin:integer;
    time_fining:integer;
    uncpu:TCPU;
    nombre_de_coeur:integer;
    un_thread_wait:TSeulement_Wait;
    modulo,posit_one,posit_two:integer;

begin
jusqua:=strtoint(Edit_nombre.text);
time_begin:=gettickcount;
n:=1;
for i:=1 to jusqua do
  begin
  n:=n*i;
  //Boucle de temporisation
  for j:=1 to 20000 do s:=sin(j);
  end;
Edit_resultat.Text:=floattostr(n);
time_fining:=gettickcount;
Edit_mono.Text:=Convertir_ce_numerique_en_string_separer_par_des_point(time_fining-time_begin)+' Millisecondes';

uncpu:=TCPU.Create;
nombre_de_coeur:=uncpu.Count;
uncpu.Free;
Label_sur_x_coeur.Caption:='sur '+inttostr(nombre_de_coeur)+ ' coeur(s)';
if nombre_de_coeur>1 then
  begin
  time_begin:=gettickcount;
  for i:=1 to nombre_de_coeur do
    Table_thread[i]:=1;
  Lock:=TCriticalSection.Create;
  ThreadsRunning:=nombre_de_coeur;
  un_thread_wait:=TSeulement_Wait.Create;
  for ithread:=1 to nombre_de_coeur do
    begin
		modulo:=jusqua div nombre_de_coeur;
    if ithread=1 then
      posit_one:=1
    else
		  posit_one:=modulo*pred(ithread);
    if ithread=nombre_de_coeur then
      posit_two:=jusqua
    else
		  posit_two:=pred(modulo*ithread);
    with Thread_Factorielle.create(
      ithread,posit_one,posit_two) do
        OnTerminate := ThreadDone;
    end;
  un_thread_wait.waitfor;
  Lock.Free;
  n:=1;
  for i:=1 to nombre_de_coeur do
    n:=n*Table_thread[i];
  time_fining:=gettickcount;
  Edit_result_multi_coeur.text:=floattostr(n);
  Edit_multi.Text:=Convertir_ce_numerique_en_string_separer_par_des_point(time_fining-time_begin)+' Millisecondes';
  end
else
  Edit_multi.Text:='Monocoeur';
end; {TAffichage_factorielle.Button1Click}

end.

Conclusion :


Utilise un mutex pour synchroniser n tache selon le nombre de coeur.

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.