OVH Cloud OVH Cloud

Impression de me compliquer ... (strcat/sprintf)

49 réponses
Avatar
Kinou
Bonsoir,

J'ai un petit problème avec strcat/sprintf notamment en C.

En fait je "parse" un char* caractere par caractere, et je concatene ces
caracteres dans une autre chaine (chaineDest) de caractere char*.
Le probleme est que ma chaine chaineDest, si au depart je lui mets une
taille de 20, et que j'ai besoin ensuite de lui concatener plus de 20
caracteres, comment je fais ?

La solution que j'aurais tendance à utiliser est que quand je vois que
la taille devient trop courte, je sauvegarde ma chaine de caractere
(avec strcpy) chaineDest, je lui realloue la taille qu'il faut avec un
malloc, et je recopie ensuite dans l'autre sens pour remettre le contenu
de ma chaine sauvegardé dans la nouvelle chaine de taille plus grande.

Mais ca me parait abérant de faire ça pour toute concaténation en C ...
J'aime croire que le C n'est pas si barbare qu'on le dit, mais que c'est
plutot parce que je suis plus que juste en C ...

Merci par avance pour vos réponses.

--
Cordialement,
Vincent

9 réponses

1 2 3 4 5
Avatar
Richard Delorme
Richard Delorme wrote on 15/11/04 :

En dehors de quelques cas triviaux où memcpy est entièrement déroulé,
memcpy doit s'exécuter dans une boucle où l'on teste que le bon nombre
de caractère a été copié. C'est aussi long que de vérifier si le
dernier caractère est atteint. memcpy peut être plus rapide que strcpy
parce qu'il est plus facile de lui appliquer certaine optimisation
tordue, comme la copie par bloc, mais c'est tout.


Sur x86, il y a possibilité d'insérer du code machine très rapide qui
fait la copie en une ou deux instructions (sorte de DMA).


Si tu penses à REP MOVS, c'est généralement peu efficace sur les
processeurs x86 actuels. Par exemple, pour le processeur que j'utilise
actuellement (athlon-xp), on peut lire ici (p 68) :
http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/22007.pdf

« The REP MOVS instructions are often not as efficient as an explicit
loop which uses simple "RISC" instructions. »
Les pages suivantes donnent plusieurs exemples de code assembleur pour
réaliser un memcpy, le plus rapide étant 2 à 3 fois plus rapide que ceux
basés sur de simple REP MOVS.

--
Richard


Avatar
Richard Delorme
"Richard Delorme" wrote in message
news:41990722$0$15747$



La librairie standard glibc optimise memcpy a mort, remplaçant la plupart



des

appels par du code généré directement en lieu et place de l'appel, et
particulierement efficace quant la taille de la copie est constante et
l'alignement des pointeurs connu.


1) C'est le compilateur qui peut générer du code en ligne, pas une
bibliothèque.
2) le memcpy de la glibc est plutôt lent.



Les headers font partie de la libc, ils contiennent des implementations en
ligneet des macros qui font que le compilateur genere du code en ligne. C'est
donc de la combinaison des deux que résult l'optimisation.


Oui et non. Indépendamment des entêtes et de la bibliothèque, gcc peut
remplacer memcpy() par __builtin_memcpy() qui va générer un code enligne
plus ou moins optimisé selon les circonstances.

Quelle expérience as-tu du memcpy de la glibc (utilisée dans des programmes
compilés avec gcc) ?
Quelle alternative connais-tu qui soit plus rapide ?


Par exemple xine, un programme de visualisation de vidéo sous X11 fait
un petit test lors de sa première utilisation, où il compare le memcpy
de la libc avec différents algorithmes :

Benchmarking memcpy methods (smaller is better):
libc memcpy() : 784152313
linux kernel memcpy() : 1062236958
MMX optimized memcpy() : 800939578
MMXEXT optimized memcpy() : 437205373
SSE optimized memcpy() : 435446692

L'algorithme qu'il utilise finalement est presque deux fois plus rapide
que celui de la libc.

On peut sans doute faire encore mieux en lisant attentivement les
conseils du fabriquant du micropresseur que l'on cible. Sur mon
Athlon-XP, le plus rapide, pour des gros blocs de mémoire, est de faire
un "prefetching" suivit d'une lecture par bloc avec les registres SSE.

--
Richard



Avatar
cedric
Emmanuel Delahaye wrote:
Effectivement, mes connaissances en assembleur x86 sont anciennes et
probablement dépassées.


Si ma mémoire est bonne c'était déjà déconseillé sur 486 (j'ai arreté
l'assembleur intel à peut près à ce moment là)

Avatar
Charlie Gordon
Quelle expérience as-tu du memcpy de la glibc (utilisée dans des programmes
compilés avec gcc) ?
Quelle alternative connais-tu qui soit plus rapide ?


Par exemple xine, un programme de visualisation de vidéo sous X11 fait
un petit test lors de sa première utilisation, où il compare le memcpy
de la libc avec différents algorithmes :

Benchmarking memcpy methods (smaller is better):
libc memcpy() : 784152313
linux kernel memcpy() : 1062236958
MMX optimized memcpy() : 800939578
MMXEXT optimized memcpy() : 437205373
SSE optimized memcpy() : 435446692

L'algorithme qu'il utilise finalement est presque deux fois plus rapide
que celui de la libc.

On peut sans doute faire encore mieux en lisant attentivement les
conseils du fabriquant du micropresseur que l'on cible. Sur mon
Athlon-XP, le plus rapide, pour des gros blocs de mémoire, est de faire
un "prefetching" suivit d'une lecture par bloc avec les registres SSE.


Oui, pour optimiser les transferts de gros blocs de memoire, on peut faire mieux
que le code relativement générique de la glibc et utiliser des techniques
lourdes, dont l'overhead et la spécificité sont importants, mais qui gagnent
encore un facteur significatif pour des blocs appropriés (grande taille,
contraintes particulières sur l'alignement et la taille, compatibilité avec
l'utilisation des registres spéciaux...).

L'optimisation de memcpy par gcc/glibc marche quand même très bien pour le cas
général, en particulier pour les petits blocs de taille connue à compile-time :
copie de structures, copie de parties de tableaux, ou l'alignement est connu, la
taille un multiple de la taille du mot et le code generé souvent ne fait meme
pas de boucle. Le facteur gagné par de telles techniques sur l'appel d'une
fonction de bibliothèque partagée est alors de bien supérieur à 2.

Chqrlie.


Avatar
Marc Boyer
Charlie Gordon wrote:
"Targeur fou" wrote in message
[SNIP]

Je ne partage pas ton optimisme, mais je connais qu'il m'a fallu beaucoup de
temps pour admettre que la question n'est pas de maitriser ou non strncat ou
strncpy, mais de refuser de piéger son propre code avec ces mines qui
attendraient patiemment le programmeur suivant pour lui pourrir la vie avec leur
sémantique ambiguë.


Comme je dois toujours: "Toujours prévoir que le code pourra être
relu par quelqu'un de moins intelligent que soi." ;-)

Marc Boyer
--
Je ne respecte plus le code de la route à vélo depuis une double fracture
due au fait que j'étais le seul à le respecter.

Avatar
Antoine Leca
En , Emmanuel Delahaye va escriure:
Sur x86, il y a possibilité d'insérer du code machine très rapide qui
fait la copie en une ou deux instructions (sorte de DMA).


Depuis les Pentium (1993) ce n'est plus la méthode la plus efficace.
Déjà avec les caches des 486 (1990), il y avait des manières de faire aussi
vite avec des boucles explicites; et en déroulant les boucles tu gagnais.

D'autre part et pendant qu'on y est, à l'époque des 386/486, l'utilisation
du DMA (mémoire-mémoire) permettait d'obtenir des performances plus
qu'intéressantes sur des gros blocs, surtout sur des programmes
multi-tâches: memcpy devient: tu programmes le DMA, tu attrapes le traitant
d'interruption (ou mieux tu le branches sur awake()), tu lances le transfert
et tu rends la main au scheduler. Au niveau des benchs, tu obtiens un memcpy
indépendant de la longueur de bloc!
Je ne sais pas comment marche les blitters d'aujourd'hui sur la mémoire
principale (avec les caches L2/L3 cela doit être nettement moins clair).


Antoine

Avatar
drkm
Marc Boyer writes:

Comme je dois toujours: "Toujours prévoir que le code pourra être
relu par quelqu'un de moins intelligent que soi." ;-)


Moi de même, en me disant souvent que cet autre a de grandes chances
d'être moi-même ...

--drkm

Avatar
Pierre Maurette
drkm a écrit:

Marc Boyer writes:

Comme je dois toujours: "Toujours prévoir que le code pourra être
relu par quelqu'un de moins intelligent que soi." ;-)


Moi de même, en me disant souvent que cet autre a de grandes chances
d'être moi-même ...
Moi, je n'ai pas ce problème, l'hypothèse de quelqu'un de moins

intelligent que moi étant improbable ;-)
[oui, je sais, ce sera moi qui relirai le code]
--
Pierre


Avatar
rtroadec
"Charlie Gordon" wrote in message news:<cnarqd$n3k$...
"Targeur fou" wrote in message
news:
"Charlie Gordon" wrote in message
news:<cn6ho9$5m$...


size_t pos = strlen(s);
if (pos < lng-1)


les unsigned ont encore frappé !
ce test se vautre si lng vaut 0
il faut mieux écrire pos + 1 < lng
ou utiliser des ssize_t ou des int pour les tailles de tableau.
qui plus est lng est mal choisi comme nom pour la taille du tableau, c'est
trop


ambigu, on pourrait confondre avec la longueur de la chaine qui s'y trouve.


Aïe, bien vu, je me suis dit aujourd'hui "Tiens, je pourrais corriger
par if (pos < (int)(lng-1))", et là, erreur fatale (cause priorité
opérateurs), ça revient au même et c'est pire, ca obfusque davantage.
if (pos < (int)lng-1) serait correct avec toutefois un avertissement
sur la comparaison signé/non signé.


Hélas, non, cela ne suffirait pas, car la réalité est encore plus sordide :

La comparaison signé/non signé a une sémantique assez piégeuse que tu ne sembles
pas encore maitriser.
Le problème ici n'est pas la précédence des opérations, mais les règles
d'arithmétique non-signée.



Hé hé, j'ai voulu faire le malin, mais j'avais complètement zappé les
conversions engendrées par l'opérateur relationnel si bien expliquées
par toi et Antoine.

[coupé]

Je ne partage pas ton optimisme, mais je connais qu'il m'a fallu beaucoup de
temps pour admettre que la question n'est pas de maitriser ou non strncat ou
strncpy, mais de refuser de piéger son propre code avec ces mines qui
attendraient patiemment le programmeur suivant pour lui pourrir la vie avec leur
sémantique ambiguë. La bonne solution est de ne pas utiliser ces saletés, voire
d'en empêcher l'usage, et d'utiliser à la place des fonctions plus claires,
implémentées localement.


Ca dépend, la preuve par l'exemple, le mien dans ce thread. Si le code
que j'écris est susceptible de générer régulièrement des bugs vicieux
à cause d'erreurs du même tenant que celle évoquée dans ce thread, je
peux très bien me dire que je ne suis pas capable de créer des
fonctions au moins aussi "correctes" que celles fournies avec une
libc. C'est un travail que j'effectuerai quand j'aurai assez de temps
pour l'effort de tests, mais en attendant, je préfère utiliser au
maximum les fonctions de la libc, à peu près tout sauf gets().

Regis




1 2 3 4 5