Fred_Ca_Pulse
Messages postés20Date d'inscriptionmercredi 15 mars 2006StatutMembreDernière intervention19 juin 2008
-
16 août 2006 à 09:44
Fred_Ca_Pulse
Messages postés20Date d'inscriptionmercredi 15 mars 2006StatutMembreDernière intervention19 juin 2008
-
17 août 2006 à 11:05
Bonjour à tous
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.
Fred_Ca_Pulse
Messages postés20Date d'inscriptionmercredi 15 mars 2006StatutMembreDernière intervention19 juin 2008 16 août 2006 à 11:24
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
jinh68
Messages postés215Date d'inscriptionmardi 29 juillet 2003StatutMembreDernière intervention 1 septembre 2006 16 août 2006 à 11:30
Très beau bilan ;)!
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 ;)).
Fred_Ca_Pulse
Messages postés20Date d'inscriptionmercredi 15 mars 2006StatutMembreDernière intervention19 juin 2008 16 août 2006 à 10:26
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 ?
jinh68
Messages postés215Date d'inscriptionmardi 29 juillet 2003StatutMembreDernière intervention 1 septembre 2006 16 août 2006 à 10:43
Salut,
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;
Emandhal
Messages postés194Date d'inscriptiondimanche 2 mars 2003StatutMembreDernière intervention10 octobre 20063 16 août 2006 à 10:59
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...
DRJEROME
Messages postés436Date d'inscriptionjeudi 9 janvier 2003StatutMembreDernière intervention 5 février 2015 16 août 2006 à 11:12
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 />
DRJEROME
Messages postés436Date d'inscriptionjeudi 9 janvier 2003StatutMembreDernière intervention 5 février 2015 16 août 2006 à 11:26
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 />
Cirec
Messages postés3833Date d'inscriptionvendredi 23 juillet 2004StatutModérateurDernière intervention18 septembre 202250 16 août 2006 à 13:49
Salut,
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;
Fred_Ca_Pulse
Messages postés20Date d'inscriptionmercredi 15 mars 2006StatutMembreDernière intervention19 juin 2008 16 août 2006 à 14:59
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 !
f0xi
Messages postés4205Date d'inscriptionsamedi 16 octobre 2004StatutModérateurDernière intervention12 mars 202237 16 août 2006 à 16:14
28.3 .. 29.8 ms pour 100Kr
// 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;