Pour otimiser mon code, j'ai voulu transformer en procédure la fonction de remplacement que j'utilise depuis longtemps déjà :
function CaractereRemplacer( CSource, CDestination : Char; const SChaine : String ) : String;
var
ILong : Longint;
Source, Dest : PChar;
begin
ILong := Length( SChaine );
SetLength( Result, ILong );
Source := Pointer( SChaine );
Dest := Pointer( Result );
while ILong <> 0 do begin
if Source^ = CSource
then Dest^ := CDestination
else Dest^ := Source^;
Inc( Source );
Inc( Dest );
Dec( ILong );
end;
end;
Son équivalent en procédure me semblait assez évident :
procedure CaractereRemplacerP( CSource, CDestination : Char; var SChaine : String );
var
ILong : Longint;
Source : PChar;
begin
ILong := Length( SChaine );
Source := Pointer( SChaine );
while ILong <> 0 do begin
if Source^ = CSource
then Source^ := CDestination;
Inc( Source );
Dec( ILong );
end;
end;
Mais, bien sûr , çà ne marche pas comme je voudrais :
j'obtiens une merveilleuse "Violation d'accès" lorqu'un caractère doit être remplacé par Source^ := CDestination
J'ai bien pensé à un problème de référencement des chaînes et j'ai donc ajouté la ligne
"UniqueString( SChaine );" en début de procédure :
çà fonctionne alors mais les performances ne sont plus à la hauteur !
Si vous avez une petite îdée sur la question, merci de m'en faire part.
Effectivement Jinh a raison :
- sa procédure marche
- le "@SChaine[1]" doit bien être équivalent à "UniqueString" : la vitesse d'exécution est la même
Cependant cette vitesse ne semble pas pouvoir être meilleure que celle de la version fonction :
- environ 10% moins rapide sur mes tests
- dans les 2 cas la chaîne résultante doit être allouée : mais en fonction la copie des caractères ne nécessite qu'une passe au lieu de 2
Il semble que le problème est le suivant :
- soit le cas simple
STest1 := 'toto';
STest2 := STest1; // Poiteur sur la même zone mémoire pour le moment !
CaractereRemplacerP( 'o', 'a', STest2 );
- si la procédure faisait le remplacement directement (sans UniqueString)
. on aurait au final : STest1 = STest2 = 'tata'
. c'est pas vraiment le but car il ne faut pas modifier STest1
- Delphi est donc très fort (qui en doutait !) : il a raison de planter
- on n'optimise donc rien en transformant la fonction en procédure
Je pense qu'en réalité, tu obtiendrais les meilleurs gain en performance en passant par l'asm(sur phidels.com, tu trouveras quelques posts traitant de ce sujet si cela t'intéresse ;)).
Oui, en quelque sorte, car ma fonction correspond plutot à un "CharReplace".
La fonction marche très bien et est environ 12 fois plus rapide que son équivalent effectué par stringreplace.
En procédure elle devrait être plus rapide encore ... si çà marchait.
Vous n’avez pas trouvé la réponse que vous recherchez ?
L'adresse du premier caractère d'une string Delphi n'est pas équivalent à l'adresse du string elle-même(préambule de 8 octets: compteur de référence + longueur).
Cette version marche, dis nous si les performances sont meilleures(même si je pense que l'appel à @SChaine[1] revient à faire une UniqueString(sTaChaine), puisque cela force à placer l'adresse sur le tas).
procedure CaractereRemplacerP( CSource, CDestination : Char; var SChaine : String );
var
ILong : Longint;
Source : PChar;
begin
ILong := Length( SChaine );
Source := Pointer( @SChaine[1] );
while ILong <> 0 do begin
if Source^ = CSource
then Source^ := CDestination;
Inc( Source );
Dec( ILong );
end;
end;
procedure CaractereRemplacerP( CSource, CDestination : Char; var SChaine : String );
var
Source : PChar;
begin
Source := PChar( SChaine );
while Source^<>#0 do
begin
if Source^ = CSource then Source^ := CDestination;
Inc( Source );
end;
end;
Tout problème a sa solution... Mais en général, celle que l'on trouve n'est jamais la bonne...
ou même :
<hr />
procedure CaractereRemplacerP( CSource, CDestination : Char; var SChaine : String );
var
Source : PChar;
begin
Source := @SChaine[1];
while Source^<>#0 do
begin
if Source^ = CSource then Source^ := CDestination;
Inc( Source );
end;
end;
<hr />
c'est équivalent à
<hr />
procedure CaractereRemplacerP( CSource, CDestination : Char; var SChaine : String );
var
Source : PChar;
begin
Source := @SChaine[1];
while Source^<>^@ do
begin
if Source^ = CSource then Source^ := CDestination;
Inc( Source );
end;
end;
<hr />
si j'ai bien compris ta fonction est plus rapide que l'équvalent en procédure
Essaye celle-ci, en fait c'est exactement la même que la fonction mais en procédure
Procedure CaractereRemplacer( CSource, CDestination : Char; const SChaine : String; out result : String );
var ILong : Longint;
Source, Dest : PChar;
begin ILong := Length( SChaine );
SetLength( Result, ILong );
Source := Pointer( SChaine );
Dest := Pointer( Result );
while ILong <> 0 dobegin if Source^ = CSource
then Dest^ := CDestination
else Dest^ : = Source^;
Inc( Source );
Inc( Dest );
Dec( ILong );
end;
end;
// Utilisation :
procedure TfrmMain.Button6Click(Sender: TObject);
Var Tmp : String;
begin CaractereRemplacer('e', 'i', 'Teste de remplacement de caractères', Tmp);
Label1.Caption := Tmp;
end;
Effectivement Cirec, ta procédure ne marche pas dans le cas d'appels du type :
Tmp := 'Teste de remplacement de caractères';
CaractereRemplacer('e', 'i', Tmp, Tmp);
La variable de sortie est mise à vide.
Dommage car effectivement dans le cas que tu donne les performances sont meilleures que ma fonction initiale.
Par contre j'ai essayé :
procedure CaractereRemplacer( CSource, CDestination : Char; const SChaine : String; var Result : String );
var
ILong : Longint;
Source, Dest : PChar;
begin
ILong := Length( SChaine );
if Length( Result ) <> ILong
then SetLength( Result, ILong ); // Réallocation si nécessaire
Source := Pointer( SChaine );
Dest := Pointer( Result );
while ILong <> 0 do begin
if Source^ = CSource
then Dest^ := CDestination
else Dest^ := Source^;
Inc( Source );
Inc( Dest );
Dec( ILong );
end;
end;
Sur un appel du type
CaractereRemplacer('e', 'i', Tmp, Tmp);
cela donne une superbe démonstration du même plantage que ma procédure initiale (celle sans UniqueString) :
'Violation d'accès'.
Et effectivement là çà me parait tout à fait logique : on veut écrire dans une chaîne constante et donc en lecture seule.
Qu'est-ce qu'ils sont durs à gober ces fichus pointeurs !
// S, cOld et cNew en const = -37..62 ms pour 100Kr
function ReplaceChar(const S : string; const cOld, cNew : Char) : String;
var
pResult : PChar;
i : integer;
begin
SetLength(Result,Length(S));
// ne pas utiliser de PChar sur S = -8..14 ms pour 100Kr
pResult := pChar(Result);
i := 1;
// repeat plus rapide que while = -10..40 ms pour 100Kr
repeat
// assignation directe du caractere = -1..3 ms pour 100Kr
pResult[0] := S[i];
if pResult[0] = cOld then
pResult[0] := cNew;
inc(pResult);
inc(i);
until S[i] = #0;
end;