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

0/5 (11 avis)

Vue 6 057 fois - Téléchargée 1 685 fois

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

Ajouter un commentaire Commentaires
denisbertin Messages postés 248 Date d'inscription lundi 22 avril 2013 Statut Membre Dernière intervention 13 mai 2023 1
2 août 2013 à 15:51
Note de l'expert : Une petite précision vos différents threads ne doivent pas appeler des objet local à la procédure qui créer les threads mais des variables globales à l'unité ou bien encapsulé-les dans chacun des threads.
denisbertin Messages postés 248 Date d'inscription lundi 22 avril 2013 Statut Membre Dernière intervention 13 mai 2023 1
2 août 2013 à 01:47
Le nombre de coeur multiplie la fréquence du processeur : Vrai

Un processeur équipé d'un double coeur permet d'effectuer des programmes dont la fréquence total double. En effet, si pour une action donné, vous pouvez répartir le nombre d'opération sur chacun des coeurs Alors vous obtiendrais une fréquence cumulé égale à la somme des fréquences cumulées des deux coeurs.
denisbertin Messages postés 248 Date d'inscription lundi 22 avril 2013 Statut Membre Dernière intervention 13 mai 2023 1
2 août 2013 à 01:46
En effet dès que l'on utilise une boucle for i=indice to une_fin ou peut alors segmenter son code de cette façon pour répartir des calculs élémentaires sur chaque unité de traitement qui vont aussi agir simultanément et ainsi gagner autant de cycles machine que le temps total d'exécution qui cette fois ci est divisible pour le nombre d'unité de coeur de calcul de ton µ-micro-processeur.
denisbertin Messages postés 248 Date d'inscription lundi 22 avril 2013 Statut Membre Dernière intervention 13 mai 2023 1
2 août 2013 à 01:38
Comme tu peut le voir les cours d'automatisme servent à quelque chose en effet jusqu'a présent je voyait des thread (des tache en français) lancée en l'air sans qu'il y ai de synchronisation - ce qui m'a moi aussi laisser septique pendant un moment mais j'ai finit par réaliser ce code et de multiple autre dans mon logiciel afin d'utiliser plusieurs coeur de calcul simultanément et en parallèle pour qu'ils agissent de concert!...
Cirec Messages postés 3833 Date d'inscription vendredi 23 juillet 2004 Statut Modérateur Dernière intervention 18 septembre 2022 50
30 juil. 2013 à 18:35
Zip modifié

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.