IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Contribuez Delphi Discussion :

Compteur de référence et pointeurs communs


Sujet :

Contribuez Delphi

  1. #1
    Membre éprouvé
    Avatar de Montor
    Homme Profil pro
    Autre
    Inscrit en
    Avril 2008
    Messages
    879
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Autre

    Informations professionnelles :
    Activité : Autre

    Informations forums :
    Inscription : Avril 2008
    Messages : 879
    Points : 963
    Points
    963
    Par défaut Compteur de référence et pointeurs communs
    Il arrive qu’on associe un TList, ou un TListView à une chaine string la méthode la plus simple pour associer :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    P:=nil;
    string(P):=S; 
    List.Add(P);
    Pour désassocier
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    P:=List[0];
    List[0]:=nil;
    string(P):='';
    voici quatre versions de fonctions pour le même but ,les trois premières fonctions sont un peu pénalisées dans les applications non multithreadées par le LOCK imposé par le compteur de références.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    {version 1}
    function DynAddRef_V1(const AInp:string):Pointer;
    asm
       JMP System.@LStrAddRef
    end;
     
    procedure DynRelease_V1(var P: Pointer);
    asm
      JMP System.@LStrClr
    end;
     
    {version2}
    function DynAddRef_V2(const AInp:string):Pointer;
    begin
      result:=nil;
      string(result):= AInp;
    end;
     
    procedure DynRelease_V2(P: Pointer);
    begin
      string(P):= '';
    end;
     
    {version 3}
    function DynAddRef_V3(AInp:string):Pointer;
    begin
      Result:=Pointer(AInp);
      Pointer(AInp):=nil;
    end;
     
    procedure DynRelease_V3(P: Pointer);
    var S:string;
    begin
       Pointer(S):=P;
    end;
     
    {version 4}
    function DynAddRef_V4(const AInp:string):Pointer;
    var
     P:PInteger;
    begin
      result:=Pointer(AInp);
      if Assigned(result) then
      begin
         P := Result;
         dec(P,2);
         if (P^ >= 0) then
         begin
             if  IsMultiThread then
              Interlockedincrement(P^)
             else
              inc(P^);
         end;
      end;
    end;
     
    procedure DynRelease_V4(var P: Pointer);
    var
      PStr:PInteger;
    begin
      PStr :=P;
      if Assigned(PStr) then
      begin
         P := nil;
         dec(PStr,2);
         if (PStr^ > 0)then
         begin
           if IsMultiThread then
              Interlockeddecrement(PStr^)
           else
              dec(PStr^);
           if PStr^ = 0 then
           begin
              Freemem(PStr);
           end;
         end;
      end;
    end;
    Utilisation :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
       {association}
       S:=StringOfchar('a',10);
       List.Add(DynAddRef_V4(S));
     
       {désassociation}
       P:=List[0];
       List[0]:=nil;
       DynRelease_V4(P);
    Résultat de tests pour différentes versions

    DynAddRef_V1/DynRelease_V1-> 1156 ms
    DynAddRef_V2/DynRelease_V2 -> 2000 ms
    DynAddRef_V3/DynRelease_V3 -> 1578 ms
    DynAddRef_V4/DynRelease_V4 -> 140 ms

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    procedure TForm1.Button1Click(Sender: TObject);
    var
     P:Pointer;
     i:integer;
     sc:string;
     G:cardinal;
    begin
        sc :=StringOfChar(#32,$80);
        G := GetTickCount;
        for i := 1 to 8000000-1 do
        begin
          P:= DynAddRef_V4(sc);
          DynRelease_V4(P);
        end;
        Showmessagefmt('RefCnt = %d : %d ms',[PInteger(Integer(sc)-8)^,GetTickCount - G]);
    end;
    Exemples sur le compteur de reférences:

    Exemple1
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    var
     S:string;  {variable sur quatre octets }
     P:Pointer; {variable sur quatre octets }
    begin
       {ne faites pas des tests avec des constants du genre S:="azertyu"     }
       {le compteur de références et toujours a -1 qui indique que la chaine }
       {ne sera jamais libérée }
     
       S:=StringOfChar('A',$20);
       { RefCount = 1}
     
       {Le Pointeur P doit contenir une valeur corrècte 'un pointeur sur une chaine'}
       { ou un nil, Delphi va essayer de decrementer l'ancienne réference ou la libérer}
       P:=nil;
     
       {Le Pointeur P vaut S et le compteur de références est incrementé}
       {affectation string les compteurs de références sur les deux coté}
       {sont controlés}
       string(P):=S;
       { RefCount = 2}
     
       List.Add(P);
       { end;}
       { Fin de procedure S n'est pas null le compilo va decrementer le compteur de références }
       { RefCount = 1 le compteur n'est pas null donc la chaine n'est pas libérée}
       { le pointeur sur la list est valide }
    end;
    Exemple2
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    var
     S:string;
     P:Pointer;
    begin
       S:=StringOfChar('A',$20);
       { RefCount = 1}
     
       {Le Pointeur P vaut S et le compteur de références n'est pas incrementé}
       {S est considéré comme Pointeur et pas comme string ,on a trompé le compilo +-}
       {affectation Pointeur}
       P:=Pointer(S);
     
       { RefCount = 1}
       List.Add(P);
     
       {S est une variable sur quatre octets pour mettre un nil ou un zéro}
       Pointer(S):=nil;
       {ou simplement Integer(S):=0; }
     
       { end;}
       { Fin de procedure S est null rien se passe}
       { RefCount = 1 le compteur n'est pas null donc la chaine n'est pas libérée}
       { le pointeur sur la list est valide }
    end;
    Exemple 3
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    var
     S:string;
     P:Pointer;
    begin
       S:=StringOfChar('A',$20);
       { RefCount = 1}
     
       {Le Pointeur P vaut S et le compteur de références n'est pas incrementé}
       P:=Pointer(S);
     
       { RefCount = 1}
       List.Add(P);
     
       { end;}
       { Fin de procedure S n'est pas null on decremente le compteur de références}
       { RefCount = 0 le compteur est null la chaine est libérée}
       { le pointeur sur la list n'est pas valide {une violation d'accsses}
    end;
    Pour terminer le compteur de references n'est que le poiteur utilisé par Freemem pour libérer la chaine à Pointer(S) -12 (avec le gestionaire de memoire par défaut)on trouve sa taille en octets
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    S:=StringOfChar(#32,128);
      {PInteger(Integer(S)-12)^ and not cFlags - SizeOf(TUsed)}
     
      {Affiche 140}
      Showmessagefmt('Size = %d',[PInteger(Integer(S)-12)^ and $7FFFFFFC -4]);
     
     
      {on a la chaine string + #0 + 8 octets pour enregister le longueur }
      {de la chaine et le compteur de réferences}
      {128 + 1 + 8 = 137 alignés sur 4  = 140}

  2. #2
    Membre éprouvé
    Avatar de Dr.Who
    Inscrit en
    Septembre 2009
    Messages
    980
    Détails du profil
    Informations personnelles :
    Âge : 45

    Informations forums :
    Inscription : Septembre 2009
    Messages : 980
    Points : 1 294
    Points
    1 294
    Par défaut
    mauvais test :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
     
    procedure TForm1.Button1Click(Sender: TObject);
    var
     P:Pointer;
     sc:string;
     G:cardinal;
     PS,PE: int64;
     MaxTests : integer;
    begin
      sc := StringOfChar(#32, $FF); 
      MaxTests := 1000000; // 10, 100, 1000, 10000 ...
     
      G := GetTickCount;
      QueryPerformanceCounter(PS);
      repeat
        P := DynAddRef_V4(sc);
        DynRelease_V4(P);
        dec(MaxTests);
      until MaxTests = 0;
      QueryPerformanceCounter(PE);
      G := GetTickCount-G;
     
      Showmessagefmt('RefCnt = %d :  ~%d ms ~%d cycles',[PInteger(Integer(sc)-8)^,G, PE-PS]);
    end;
    repeat until n = 0 est la boucle la plus rapide générée par delphi (celle qui à le moins d'assembleur : dec(r32); jnz @;

    QueryPerformanceCounter donné une indication un peu plus precise que GetTickCount.
    GetTickCount se calcul tout de suite aprés la boucle et non dans la fonction format ou autre qui peut placer des instructions avant le dernier appel à gettickcount, notamment ici, le calcul du pointeur et l'assignation avant le call sur ShowMessageFmt.
    Le premier appel a GetTickCount se fait avant QueryPerformanceCounter, qui lui doit être le plus prés possible de la boucle.
    le dernier appel à GetTickCount se fait aprés QueryPerformanceCounter, qui lui aussi doit être le plus prés possible de la fin de la boucle.

    on effectue les test sur des puissances de 10 (10, 100, 1000 etc) plus simple pour les calculs de precision :
    cycles par boucles : (PE-PS) * 0.000001 c/l (1 000 000 de tests)
    temps par boucles : G * 0.000000001 s/l (1 000 000 de tests, 1 sec = 1000 ms)

    avec 8 000 000 ou autre nombres non puissance de 10 de tests il faudrait faire appel à une division puisque :

    n/10 = n * 0.1
    n/8 = n * 0.125 = pffff
    [ Sources et programmes de Dr.Who | FAQ Delphi | FAQ Pascal | Règlement | Contactez l'équipe ]
    Ma messagerie n'est pas la succursale du forum... merci!

  3. #3
    Membre éprouvé
    Avatar de Montor
    Homme Profil pro
    Autre
    Inscrit en
    Avril 2008
    Messages
    879
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Autre

    Informations professionnelles :
    Activité : Autre

    Informations forums :
    Inscription : Avril 2008
    Messages : 879
    Points : 963
    Points
    963
    Par défaut
    Bonjour
    Merci pour les remarques...je ne voulais pas donner allusion à un truc de performante,c'est pour comparrer la quartième fonction avec les trois premières l'ecart était si flagrant et GetTickCount peut faire l'affaire !
    Si tu veux acceder directement au compteur du pocesseur pour des prélevements rapides
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
       asm
        DW  310Fh
        MOV DWORD PTR PS   ,EAX
        MOV DWORD PTR PS[4],EDX
       end;
        {test}
       ...
       asm
        DW  310Fh
        MOV DWORD PTR PE   ,EAX
        MOV DWORD PTR PE[4],EDX
       end;
       PE:=(PE-PS);
    Pour la boucle repeat,while est un peu plus rapide sans condition de sortie
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
          repeat
     
     
            dec(MaxTests)
            if MaxTests = 0 then break;
          until False ;
    Laisse tout ça ...je ne veux pas aborder sur ce coté...

Discussions similaires

  1. référence ou pointeur
    Par storm_2000 dans le forum Débuter
    Réponses: 1
    Dernier message: 11/10/2009, 20h57
  2. Réponses: 2
    Dernier message: 30/08/2009, 10h55
  3. Le compteur de référence
    Par DarkVader dans le forum VB 6 et antérieur
    Réponses: 2
    Dernier message: 29/07/2009, 19h58
  4. Différence entre référence et pointeur ?
    Par Vivian Pennel dans le forum Langage
    Réponses: 3
    Dernier message: 03/08/2007, 17h19
  5. Références et pointeurs sur un tableau
    Par smag dans le forum C++
    Réponses: 2
    Dernier message: 01/03/2005, 20h29

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo