OVH Cloud OVH Cloud

Du bon usage de const et des typedef...

19 réponses
Avatar
Marc Boyer
J'ai des interrogations de style sur les signatures de fonctions
associées aux types utilisateurs, surtout question de style, pas
vitales, mais bon...

Prenons un type Pile, pile d'entiers.

Je vois 4 familles de définition de ce type
A) struct avec taille+tableau dynamique
typedef struct {
int* tab;
size_t capacite, taille;
} Pile;
B) Ptr sur 1ere element d'une liste chainee
struct cel {
struct cel* next;
int i;
};
typedef struct cel * Pile;
C) TAD pur
typedef Pile; // dans le .h
// version A ou B dans le .c
D) Tableau de taille fixe (pile bornée + délimiteur de fin)
typedef int Pile[CAPA+1];

Quelles signatures donner aux fonctions qui la manipulent ? Pour
alimenter la discution, prenons simplement initialisation et taille.

Moi, ma tendance serait:

A+B+D: // struct, pointeur et tableau
bool initPile(Pile* p);
size_t sizePile(const Pile p);
C: // TAD
Pile* newPile(void);
size_t sizePile(const Pile *p);

Mais (et c'est là que commencent mes interrogations):

Dans A et B (struct et pointeur), le const de size est superflux: de
toute façon on a une copie, c'est pas plus pertinent que
foo(const int);
En plus, pour A, j'entends déjà les critiques de perfs parce que
je passe une structure en paramètre, et que ça va pénaliser les perfs
là où
sizePile(const Pile* p)
aurait été bien meilleur (mais plus mauvais pour B puisqu'on
rajouterais une indirection supplémentaire)

Sauf si les compilateurs savent remplacer les appels du genre
foo(const T) en foo(const T*) s'ils pensent que ce sera plus
rapide (inline ou cout indirection negligeable par rapport au
gain en copie).

Dans D (tableau), c'est initPile qui rajoute une indirection inutile.

Il n'y a que pour le TAD que c'est simple: comme on a décidé par
design de payer une indirection à chaque fois, tout est simple
ensuite.

Des idées, des commentaires, vos pratiques ?

Marc Boyer
--
La contractualisation de la recherche, c'est me donner de l'argent pour
faire ce que je ne sais pas faire, que je fais donc mal, pendant que ce
que je sais faire, je le fais sans moyens...

9 réponses

1 2
Avatar
Jean-Marc Bourguet
"Antoine Leca" writes:

En , Jean-Marc Bourguet va escriure:
"Antoine Leca" writes:

Mmmmh. Cela peut justement *être* l'ABI: les structures ne sont pas
passées par valeur, mais toujours par référence. Si la structure est
modifiée dans la fonction appelée, celle-ci est priée d'en faire une
copie locale.


Pas uniquement si, toujours a cause de la possibilite d'alias.


À quoi penses-tu ?


struct s {
int m;
};

struct s g;

void f(struct s a) {
g.m = a.m+1;
printf("%dn", a.m);
}

f(g);

OK, on ne doit pas toujours copier, mais il ne faut pas copier
uniquement si on modifie le parametre.

A+

--
Jean-Marc
FAQ de fclc: http://www.isty-info.uvsq.fr/~rumeau/fclc
Site de usenet-fr: http://www.usenet-fr.news.eu.org



Avatar
Marc Boyer
Laurent Deniau wrote:
Marc Boyer wrote:
Mais les programmeurs C ne vont-il pas etre reticents à
payer un appel de fonction pour des choses comme l'acces
à la taille ?


Je ne connais pas de cas en C ou l'appel de fonction est trop couteux si
on programme correctement (ex: eviter les while(n++ < strlen(s)){} ).


Je ne suis pas tout a fait sur que tu sois representant de
la communauté ;-)

Apres avoir ete dans l'exces avec les inline (la version de OOC que tu
as en est un bon exemple), j'ai tout vire.

Sur mon portable Pentium-M a 1.5 Ghz, je peux faire environs 175000000
d'appels de fonction par seconde avec deux pointeurs en argument, un
retour de pointeur plus une bete incrementation de conteur dedans (d'une
valeur pointee). Cette architecture n'est pas specialement rapide.

Alors de mon point de vue, la bonne question est quelle est l'algorithme
qui ne sera pas domine par les mouvements memoire (ex: cache) et qui
aurait besoin d'etre plus rapide? S'il existe (??), on peut le specialiser.

Pour moi les inlines on un sens dans tres tres peu de cas, sauf en C++
ou ils sont indispensable avec les templates.


Tu peux me preciser ce que tu entends pas là.

p->stack[n] a la meme rapidite que stack soit un tableau ou un pointeur
sur la pluspart des architectures modernes. Surtout depuis que les
languages objets se sont repandus. J'ai mesure des variations de +-5%
suivant les archis et les compilos. Completement negligeable en
comparaison de tes structures passees par valeur!


Je suis positivement étonné. Je prends note.


Real-ity is simple until you make it complex.

Je sais plus de qui s'est ni si c'est la phrase exacte, mais l'idee y
est :-)


Disons que la dernière fois qu'on m'a expliqué d'un peu prêt
un processeur, j'ai eut l'impression de faire partie de la dernière
génération qui pouvait y comprendre quelque chose.
Depuis, je vis un peu sur mes acquis...

Marc Boyer
--
La contractualisation de la recherche, c'est me donner de l'argent pour
faire ce que je ne sais pas faire, que je fais donc mal, pendant que ce
que je sais faire, je le fais sans moyens...



Avatar
Laurent Deniau
Marc Boyer wrote:
Laurent Deniau wrote:
Apres avoir ete dans l'exces avec les inline (la version de OOC que tu
as en est un bon exemple), j'ai tout vire.

Sur mon portable Pentium-M a 1.5 Ghz, je peux faire environs 175000000
d'appels de fonction par seconde avec deux pointeurs en argument, un
retour de pointeur plus une bete incrementation de conteur dedans (d'une
valeur pointee). Cette architecture n'est pas specialement rapide.

Alors de mon point de vue, la bonne question est quelle est l'algorithme
qui ne sera pas domine par les mouvements memoire (ex: cache) et qui
aurait besoin d'etre plus rapide? S'il existe (??), on peut le specialiser.

Pour moi les inlines on un sens dans tres tres peu de cas, sauf en C++
ou ils sont indispensable avec les templates.



Tu peux me preciser ce que tu entends pas là.


Heu sur quel point?

Sur la rapidite des appels de fonctions, ce que je voulais dire c'est
que ce n'est pas ce qui predomine dans les algos ou la complexite est
correcte.

Pour le C++, je veux dire que sans inlining (implicite ou explicite),
beaucoup d'application des templates perdent de leur interet.

a+, ld.


Avatar
Antoine Leca
En , Jean-Marc Bourguet va escriure:
struct s g;
<snip; f() modifie la globale g, puis utilise son paramètre>

f(g);


Tout le monde le dit, il ne faut pas utiliser les globales :-)))


[ Je ne me rappelle plus où j'ai vu cette idée des structures par
référence -- je n'ai rien inventé --, mais que je me souvienne, on pouvait
avoir des structures globales, et bien sûr il n'y avait rien de prévu pour
le cas que tu décrit ci-dessus. Pourtant, je ne me souviens pas que cela
pose problème dans la pratique... ]

Antoine

Avatar
Jean-Marc Bourguet
"Antoine Leca" writes:

En , Jean-Marc Bourguet va escriure:
struct s g;
<snip; f() modifie la globale g, puis utilise son paramètre>

f(g);


Tout le monde le dit, il ne faut pas utiliser les globales :-)))


[ Je ne me rappelle plus où j'ai vu cette idée des structures par
référence -- je n'ai rien inventé --, mais que je me souvienne, on pouvait
avoir des structures globales, et bien sûr il n'y avait rien de prévu pour
le cas que tu décrit ci-dessus. Pourtant, je ne me souviens pas que cela
pose problème dans la pratique... ]


Je ne sais pas. Ce que je sais c'est que certains langages (Ada en
particulier) précise explicitement que pour des paramètres IN (donc
non modifiés), il n'est pas spécifié si le passage se fait par copie
ou par référence (en fait en Ada il est des cas où c'est spécifié,
mais bon ce n'est pas l'objet du groupe) pour que justement un
programme qui dépende que ça ne soit pas conforme.

A+

--
Jean-Marc
FAQ de fclc: http://www.isty-info.uvsq.fr/~rumeau/fclc
Site de usenet-fr: http://www.usenet-fr.news.eu.org


Avatar
Marc Boyer
Laurent Deniau wrote:
Marc Boyer wrote:
Pour moi les inlines on un sens dans tres tres peu de cas, sauf en C++
ou ils sont indispensable avec les templates.


Tu peux me preciser ce que tu entends pas là.


Heu sur quel point?


Le fait qu'inline ne soit utile (indispensable même)
qu'avec C++ et les templates.

Sur la rapidite des appels de fonctions, ce que je voulais dire c'est
que ce n'est pas ce qui predomine dans les algos ou la complexite est
correcte.


Oui, mais un ratio 1,3 ou 2, c'est important pour certains.

Pour le C++, je veux dire que sans inlining (implicite ou explicite),
beaucoup d'application des templates perdent de leur interet.


C'est là que je ne te suis plus.

Marc Boyer
--
La contractualisation de la recherche, c'est me donner de l'argent pour
faire ce que je ne sais pas faire, que je fais donc mal, pendant que ce
que je sais faire, je le fais sans moyens...



Avatar
Laurent Deniau
Marc Boyer wrote:
Laurent Deniau wrote:

Marc Boyer wrote:

Pour moi les inlines on un sens dans tres tres peu de cas, sauf en C++
ou ils sont indispensable avec les templates.



Tu peux me preciser ce que tu entends pas là.


Heu sur quel point?



Le fait qu'inline ne soit utile (indispensable même)
qu'avec C++ et les templates.


Je vais faire une precision de Normand en te retournant la question:
dans quel cas en C estimes-tu que les inlines dans les headers sont
indispensables?

Pour ma part, je n'ai considere que des cas tres rares ou par exemple on
veut implementer un mecanisme similaire a un appel de fonction et ou on
veut rester proche des performances d'un appel de fonction.

Par exemple, un message dispatch a la Objective-C (voir mon thread
recent sur comp.lang.objective-c ou je precisais que je faisais du
message dispatch environ 4 fois plus rapide que les compilateurs objc
actuel mais apparemment ca n'interesse personne).

Ou une implementation des interfaces a la Java en C (les protocols dans
OOC).

Dans ces cas, on veut un mecanisme different avec des performances
proches des appels de fonction (e.g +50% pour le message dispatch et
+20% pour les protocols). Si on utilise pas l'inline, on tombe
immediatement a > +(100+X)%

Sur la rapidite des appels de fonctions, ce que je voulais dire c'est
que ce n'est pas ce qui predomine dans les algos ou la complexite est
correcte.


Oui, mais un ratio 1,3 ou 2, c'est important pour certains.


Des exemples concrets?

Je precise toutefois un point: l'inlining (implicite) est tres utile
dans l'implementation pour ameliorer les performances, mais je parlais
surtout de l'inlining explicite dans les interfaces (dans les headers).

Pour le C++, je veux dire que sans inlining (implicite ou explicite),
beaucoup d'application des templates perdent de leur interet.


C'est là que je ne te suis plus.


? Je redis la meme chose que dans la premiere phrase que tu cites.

a+, ld.




Avatar
Marc Boyer
Laurent Deniau wrote:
Le fait qu'inline ne soit utile (indispensable même)
qu'avec C++ et les templates.


Je vais faire une precision de Normand en te retournant la question:
dans quel cas en C estimes-tu que les inlines dans les headers sont
indispensables?


Hein ?
Disons que dans les codes que j'ai écris jusqu'à présent,
je n'ai *jamais* eut de problème de perfs, mais c'est plus
lié au fait que je suis pas développeur, juste enseignant.
J'ai relevé sur ce forum des gens qui disaient que c'était
indispensable (et qu'ils utilisaient static avant et que
les compilos faisaient l'inline avant que le mot n'existe)
parfois.

Disons que là, je tente des iterateurs et des accesseurs
en C, et il me semble pertinent d'en faire un static inline,
mais bon, ça reste très académique.

Pour ma part, je n'ai considere que des cas tres rares ou par exemple on
veut implementer un mecanisme similaire a un appel de fonction et ou on
veut rester proche des performances d'un appel de fonction.


C'est pas le contraire inline ? C'est pas "garder la sémantique
d'un appel de fonction mais ne pas payer un appel de fonction".

Par exemple, un message dispatch a la Objective-C (voir mon thread
recent sur comp.lang.objective-c ou je precisais que je faisais du
message dispatch environ 4 fois plus rapide que les compilateurs objc
actuel mais apparemment ca n'interesse personne).

Ou une implementation des interfaces a la Java en C (les protocols dans
OOC).

Dans ces cas, on veut un mecanisme different avec des performances
proches des appels de fonction (e.g +50% pour le message dispatch et
+20% pour les protocols). Si on utilise pas l'inline, on tombe
immediatement a > +(100+X)%

Sur la rapidite des appels de fonctions, ce que je voulais dire c'est
que ce n'est pas ce qui predomine dans les algos ou la complexite est
correcte.


Oui, mais un ratio 1,3 ou 2, c'est important pour certains.


Des exemples concrets?


Pas autre chose que des choses racontées...
Le principal intérêt de ce forum pour moi c'est de questionner
les gens sur leur pratique du C. La mienne n'est pas représentative.

Je precise toutefois un point: l'inlining (implicite) est tres utile
dans l'implementation pour ameliorer les performances, mais je parlais
surtout de l'inlining explicite dans les interfaces (dans les headers).


Mais qui dit implémentation dit static, non ? (Histoire de pas polluer
l'espace de noms). Hors, le semble qu'Emmanuel racontait que les
compilateurs faisaient ça avec les static bien avant que le mot
n'apparaisse dans le langage.

Mais avec tout ça, tu ne m'as pas dit pourquoi tu considères ça
nécessaire en C++.

Marc
--
La contractualisation de la recherche, c'est me donner de l'argent pour
faire ce que je ne sais pas faire, que je fais donc mal, pendant que ce
que je sais faire, je le fais sans moyens...



Avatar
Laurent Deniau
Marc Boyer wrote:
Laurent Deniau wrote:

Le fait qu'inline ne soit utile (indispensable même)
qu'avec C++ et les templates.


Je vais faire une precision de Normand en te retournant la question:
dans quel cas en C estimes-tu que les inlines dans les headers sont
indispensables?


Hein ?
Disons que dans les codes que j'ai écris jusqu'à présent,
je n'ai *jamais* eut de problème de perfs, mais c'est plus
lié au fait que je suis pas développeur, juste enseignant.


Sur des programmes "reels" c'est encore plus vrai!

La semantique "inline" permet d'ameliorer (parfois) les perfs cote
utilisateur, mais pas necessaire celle du programme dans sa globalite,
bibliotheques incluses.

J'ai relevé sur ce forum des gens qui disaient que c'était
indispensable (et qu'ils utilisaient static avant et que
les compilos faisaient l'inline avant que le mot n'existe)
parfois.


voui. J'en fais parti. J'essaye de rester le plus possible en C89.

Disons que là, je tente des iterateurs et des accesseurs
en C, et il me semble pertinent d'en faire un static inline,
mais bon, ça reste très académique.


Pour un iterateur, ca me parait justifie s'il n'est pas trop complique
ou qu'il doit etre type' par un mecanisme a-la-template.

Pour ma part, je n'ai considere que des cas tres rares ou par exemple on
veut implementer un mecanisme similaire a un appel de fonction et ou on
veut rester proche des performances d'un appel de fonction.


C'est pas le contraire inline ? C'est pas "garder la sémantique
d'un appel de fonction mais ne pas payer un appel de fonction".


Exact. Prenons l'exemple du message dispatch. Tu peux avant de deleguer
au messenger, faire un test de presence du message dans le cache
(accessible publiquement) et l'invoquer directement s'il est present.
Sinon tu delegues. Si ton caches fonctionne bien, 99.9% de ton programme
invoquera tes messages aussi vite qu'un appel de fonction (avec +50%
d'overhead seulement). Sinon tu delegues au messenger et tu montes a
+200%-800% (ce que font les compilateurs actuel). Donc tu choisis la
performance au detriment d'un code un peu plus gros cote utilisateur
(8-10 octets de plus par appel sur un i386 avec gcc).

Dans ce cas, l'inlining est indispensable pour obtenir des perfs
correctes de maniere generale cote utilisateur tout en faisant du
message dispatch qqchose d'apparente' a un appel de fonction.

Mais hormis de rare cas comme celui-ci, le decouplage des classes ou les
ADT (quand c'est possible) me paraissent beaucoup plus important que
l'inline. Mon principal soucis est d'applatir au maximum les DAG de
classes qui provoque des couplages trop complexe sur les moyens/gros
projets (d'ou OOC 2.0).

Par exemple, un message dispatch a la Objective-C (voir mon thread
recent sur comp.lang.objective-c ou je precisais que je faisais du
message dispatch environ 4 fois plus rapide que les compilateurs objc
actuel mais apparemment ca n'interesse personne).

Ou une implementation des interfaces a la Java en C (les protocols dans
OOC).

Dans ces cas, on veut un mecanisme different avec des performances
proches des appels de fonction (e.g +50% pour le message dispatch et
+20% pour les protocols). Si on utilise pas l'inline, on tombe
immediatement a > +(100+X)%


Sur la rapidite des appels de fonctions, ce que je voulais dire c'est
que ce n'est pas ce qui predomine dans les algos ou la complexite est
correcte.


Oui, mais un ratio 1,3 ou 2, c'est important pour certains.


Des exemples concrets?


Pas autre chose que des choses racontées...
Le principal intérêt de ce forum pour moi c'est de questionner
les gens sur leur pratique du C. La mienne n'est pas représentative.


Toutes les experiences sont importantes. C'est parfois les aspects
academiques d'un langage qui en font sa popularite. Meme si ce n'est pas
un aspect decisif dans le developement professionnel.

Je precise toutefois un point: l'inlining (implicite) est tres utile
dans l'implementation pour ameliorer les performances, mais je parlais
surtout de l'inlining explicite dans les interfaces (dans les headers).



Mais qui dit implémentation dit static, non ? (Histoire de pas polluer
l'espace de noms).


Pas necessairement en C. Le C99 donne une nouvelle semantique a inline
qui est differente de celle du C++ (histoire de mettre encore un peu
plus de confusion?).

Hors, le semble qu'Emmanuel racontait que les
compilateurs faisaient ça avec les static bien avant que le mot
n'apparaisse dans le langage.


Yep. Je n'ai pas ete assez precis. Je parlais des le debut de la
semantique de "static inline" qu'elle soit explicite ou implicite. Au
passage, l'absence de static ne veut pas dire que la fonction n'est pas
inlinee dans l'unite de compilation ou elle est definie.

Mais avec tout ça, tu ne m'as pas dit pourquoi tu considères ça
nécessaire en C++.


Enleve la semantique d'inline en C++ (explicite ou implicite) et dit moi
ce qui marche encore dans la STL...

a+, ld.




1 2