OVH Cloud OVH Cloud

Pourquoi encore C++ ?

26 réponses
Avatar
jeanmarc.vanel
Je suis pr=EAt =E0 accorder qu'il reste un avantage =E0 C++ pour la
rapidit=E9, de plus en plus marginal d'ailleurs.
Il est vrai aussi que cette avantage se paie par des temps de
d=E9veloppement colossaux.
Alors quelles sont les raisons de la persistence de C++ dans des
projets anciens et nouveaux ?

Je pense que la vraie raison de la persistence de C++ tient =E0 2
facteurs:
- 1. il y a du code h=E9rit=E9 ("legacy"), C et C++
- 2. les mauvaises performances de nombreuses applications Java

Sur le point 2, il me semble que le probl=E8me n'est pas d=FB de mani=E8re
intrins=E8que =E0 Java, mais peut-=EAtre =E0 la JVM ou =E0 la biblioth=E8qu=
e
standard.
Mais surtout il me semble que le facteur "culturel" est tr=E8s
important.
Beaucoup de d=E9veloppeurs Java n'ont pas une conscience claire de o=F9 et
comment la m=E9moire s'alloue et se d=E9salloue, de o=F9 est pass=E9 le tem=
ps
CPU.
Et cela pour une raison bien simple: =E9crire une application Java
utilisable mais peu performante est possible sans cette conscience
claire .
Alors qu'en C/C++ c'est presque impossible.

L'exemple typique d'application o=F9 Java n'a pas perc=E9 est le
navigateur.

10 réponses

1 2 3
Avatar
Francois
Wykaaa a écrit :

S'il n'y avait qu'en Java que le GC est mal compris. Hélas, la plupart
des développeurs ne possèdent pas la culture nécessaire pour bien
comprendre le GC.



Tout ce que je sais sur le GC, c'est que dès qu'un objet n'est plus
accessible car il n'y a plus de référence pour l'atteindre, alors cet
objet est candidat au GC, mais on ne sait pas quand exactement l'objet
sera détruit.

Est-ce juste ? Est-il nécessaire d'en savoir davantage ?

PS : je ne suis pas développeur loin de là, je me contente d'apprendre
le langage Java.

--
François
Avatar
Wykaaa
Francois a écrit :
Wykaaa a écrit :

S'il n'y avait qu'en Java que le GC est mal compris. Hélas, la plupart
des développeurs ne possèdent pas la culture nécessaire pour bien
comprendre le GC.



Tout ce que je sais sur le GC, c'est que dès qu'un objet n'est plus
accessible car il n'y a plus de référence pour l'atteindre, alors cet
objet est candidat au GC, mais on ne sait pas quand exactement l'objet
sera détruit.

Est-ce juste ?



Oui, globalement c'est juste.

Est-il nécessaire d'en savoir davantage ?

Ca dépend du type d'application, de ce qu'elle fait, etc.

Si tu as une application qui tourne 24h/24 (une appli transactionnelle
comme de la réservation de place, par exemple) et qu'elle crée des
objets, éventuellement volumineux, qui ne sont jamais libérés (parce que
le développeur aura programmé de telle façon qu'il reste toujours au
moins une référence sur les objets), cela va poser des problèmes au bout
d'un certain temps.

Ceci se produisait sous X11/Motif. Il m'arrivait de ne pas quitter ma
session pendant une semaine et au bout d'un moment, la machine ramait
parce qu'elle "swappait" sans arrêt à cause de la saturation mémoire due
à la non libération de certains objets.


PS : je ne suis pas développeur loin de là, je me contente d'apprendre
le langage Java.



Bon courage. Est-ce ton premier langage ?
Avatar
Francois
Wykaaa a écrit :

Bon courage. Est-ce ton premier langage ?



Non. J'avais d'abord tenté le C, que je trouve dur, puis le Python que
j'aime bien et j'ai voulu voir le Java qui me convient bien (il me
semble) à cause de son aspect
"compilateur-qui-empêche-d'écrire-trop-de-bêtises".

Voilà.


--
François
Avatar
Samuel Devulder
Wykaaa a écrit :

Pour changer de sujet et revenir au sujet initial de ce fil, la
réputation de mauvaise performance de Java date des implémentations
initiales des JVM. Aujourd'hui, avec les JIT (Just In Time) compiler, il
ne doit pas y avoir de problème sauf que plus de 80% des programmeurs
Java codent comme des pieds (ou avec leurs pieds, au choix). Hélas c'est
aussi vrai (encore plus probablement) pour les programmeurs C++.



Le soucis est que l'algorithmique est peu comprise de la plupart des
nouveaux programmeurs. Ainsi on voit fleurir ce genre de construction:

List<E> col = ....;
for(int i = 0; i<col.size(); i++) {
E element = col.get(i);
...
}

Or si la liste est une liste dont le size() est coûteux à calculer (par
exemple parce que c'est une liste chaînée écrite à la main), on se
retrouve à avoir du code en N^2 qui ne passe pas à l'échelle. Et même
dans l'absolu si le size() prend un temps constant à calculer, il faut
voir que l'appel d'une méthode est forcément plus coûteux que l'accès à
une variable locale. Aussi il vaudrait mieux écrire:

for(int i=0, max=col.size(); i<max; ++i) {
...
}

Ce qui donnera un code un peu plus performant (tout dépend du nombre
d'itérations et de ce que l'on calcule dans la boucle).

Ça n'est qu'un exemple.. mais on le trouve vraiment partout.

sam.
Avatar
TestMan
jlp wrote:
Jean-Claude Arbaut a écrit :
jlp wrote:


Beaucoup de développeurs Java n'ont pas une conscience claire de où et
comment la mémoire s'alloue et se désalloue, de où est passé le temps
CPU.





Par nature du garbage collector, je pense qu'il n'est pas possible
d'avoir une conscience claire de où et comment la mémoire se libère.
L'idée étant plutôt que ça ne doit pas être important.







Et jusqu'à certains qui croient que le GC est capable de décider ce
qui doit être garbager sans besoin de dé-référencer!




Tiens, je prends le fil au passage :-) Si on a une structure de
données assez complexe (nombreux pointeurs, éventuellement
avec des "cycles"), est-il suffisant de "déréférencer" la ou les
variables qui accèdent à tout ça, ou faut-il "casser" la structure ?
(est-ce que ça change quelque chose pour le GC ?)



Ben non justement, il faut dé-référencer toutes les ressources.
Par exemple la fuite mémoire de débutant sont sur les Collections où le
développeur dé-référence la Collection ( le conteneur) sans
dé-référencer le contenu ( sans faire Collection.clear() par exemple).



Bonjour,

Ceci est faux, même s'il y a des cycles, le GC fait son taf ! C'est
justement la diférence entre un simple méchanisme de compreur de
référence (à la ObjectiveC par exemple) et un garbage collector.

Le nettoyage "à la main" est donc quasiment inutile.

Le seul effet est de suppression "en avance" les références afin de
permetre au GC de libérer plus tôt la mémoire de ces éléments (utiles
par exemple sur une grosse structure si le vecteur lui même reste
partielement utilisé par exemple).

La vrai question qui tue est : "comment le GC diférencie un cycle d'un
utilisation réelle ?" ;-)

A+
TM
Avatar
Wykaaa
Samuel Devulder a écrit :
Wykaaa a écrit :

Pour changer de sujet et revenir au sujet initial de ce fil, la
réputation de mauvaise performance de Java date des implémentations
initiales des JVM. Aujourd'hui, avec les JIT (Just In Time) compiler,
il ne doit pas y avoir de problème sauf que plus de 80% des
programmeurs Java codent comme des pieds (ou avec leurs pieds, au
choix). Hélas c'est aussi vrai (encore plus probablement) pour les
programmeurs C++.



Le soucis est que l'algorithmique est peu comprise de la plupart des
nouveaux programmeurs. Ainsi on voit fleurir ce genre de construction:

List<E> col = ....;
for(int i = 0; i<col.size(); i++) {
E element = col.get(i);
...
}



Est-ce l'enseignement qui est défaillant ?

Je persiste à penser, avec 35 ans d'expérience en informatique et
l'encadrement d'équipes de développeurs de" haut niveau" (écoles
d'ingénieurs du groupe 1), qu'un programmeur qui ne maîtrise pas
l'algorithmique de base ET le langage de programmation dans lequel il
code est comme un menuisier qui ne saurait pas utiliser un rabot ou une
scie....
Autrement dit, 90% des programmeurs ne sont que des "pousseurs de
caractères" mais certainement pas des personnes adéquates pour "faire le
job".

Or si la liste est une liste dont le size() est coûteux à calculer (par
exemple parce que c'est une liste chaînée écrite à la main), on se
retrouve à avoir du code en N^2 qui ne passe pas à l'échelle. Et même
dans l'absolu si le size() prend un temps constant à calculer, il faut
voir que l'appel d'une méthode est forcément plus coûteux que l'accès à
une variable locale. Aussi il vaudrait mieux écrire:

for(int i=0, max=col.size(); i<max; ++i) {
...
}

Ce qui donnera un code un peu plus performant (tout dépend du nombre
d'itérations et de ce que l'on calcule dans la boucle).

Ça n'est qu'un exemple.. mais on le trouve vraiment partout.

sam.



Ton exemple n'est pas un problème spécifique à Java. Il est général,
même dans des langages non objets.
Un programmeur qui écrit comme ça doit être envoyé en formation ou viré
sur le champ !
Avatar
Samuel Devulder
Wykaaa a écrit :

Ton exemple n'est pas un problème spécifique à Java. Il est général,
même dans des langages non objets.



Absolument.

Un programmeur qui écrit comme ça doit être envoyé en formation ou viré
sur le champ !



Ca nécessiterait de reformer 99% des gens, y compris certains chez ibm :)

Sur le fond, ca dépend de la boucle et du temps qu'elle prend par
rapport au temps total. Si c'est une boucle occasionnelle, peu importe
de l'optimiser (je ne suis pas de ceux qui préconisent l'optim pour
l'optim).

Non, ce qu'il faut apprendre pour l'occasion c'est de savoir utiliser un
profiler pour trouver les goulets d'étranglement du programme plutôt que
de se trouver des excuses dans les lieux communs aujourd'hui largement
faux en disant "java ca rame" et en refusant de se remettre en cause en
examinant si on aurait par hasard pas codé de façon in-efficace ou
efficace sur des petites instances mais sans tenir compte du passage à
l'échelle.

Le jdk1.6.0_07 possède un outil sympa pour (entre autre) profiler:
jvisualvm. Savoir l'utiliser est un must à mon avis.

sam.
Avatar
Wykaaa
Samuel Devulder a écrit :
Wykaaa a écrit :

Ton exemple n'est pas un problème spécifique à Java. Il est général,
même dans des langages non objets.



Absolument.

Un programmeur qui écrit comme ça doit être envoyé en formation ou
viré sur le champ !



Ca nécessiterait de reformer 99% des gens, y compris certains chez ibm :)



IBM, de ce point de vue, n'est pas au-dessus du lot...

Sur le fond, ca dépend de la boucle et du temps qu'elle prend par
rapport au temps total. Si c'est une boucle occasionnelle, peu importe
de l'optimiser (je ne suis pas de ceux qui préconisent l'optim pour
l'optim).

Non, ce qu'il faut apprendre pour l'occasion c'est de savoir utiliser un
profiler pour trouver les goulets d'étranglement du programme plutôt que
de se trouver des excuses dans les lieux communs aujourd'hui largement
faux en disant "java ca rame" et en refusant de se remettre en cause en
examinant si on aurait par hasard pas codé de façon in-efficace ou
efficace sur des petites instances mais sans tenir compte du passage à
l'échelle.

Le jdk1.6.0_07 possède un outil sympa pour (entre autre) profiler:
jvisualvm. Savoir l'utiliser est un must à mon avis.

sam.



Tout à fait d'accord. Il faut savoir utiliser les outils adéquats du
développement. Les profilers sont très importants.

Moi non plus je ne suis pas partisan de l'optimisation à tout va mais
quand on dit à un programmeur que la priorité du projet ce sont les
performances, il cherche à tout optimiser alors que la règle des 80/20
s'applique : un programme passe, en général, 80% de son temps dans 20%
du code. Ce sont ces 20% qu'il faut optimiser et c'est là que les
profilers entrent en scène pour aider à trouver ces 20%.
Avatar
jlp
Eric Razny a écrit :
Le Sun, 23 Nov 2008 19:00:24 +0100, jlp a écrit :


Et jusqu'à certains qui croient que le GC est capable de décider ce
qui doit être garbager sans besoin de dé-référencer!




Tiens, je prends le fil au passage :-) Si on a une structure de
données assez complexe (nombreux pointeurs, éventuellement
avec des "cycles"), est-il suffisant de "déréférencer" la ou les
variables qui accèdent à tout ça, ou faut-il "casser" la structure ?
(est-ce que ça change quelque chose pour le GC ?)




Ben non justement, il faut dé-référencer toutes les ressources.
Par exemple la fuite mémoire de débutant sont sur les Collections où le
développeur dé-référence la Collection ( le conteneur) sans
dé-référencer le contenu ( sans faire Collection.clear() par exemple).




Salut.
Es-tu sur?

C'est l'opposé de la réponse de Samuel Devulder
(<4929ac3a$0$27457$)

Pour faire plus simple je la cite :

==== > Oui elle sera éliminée si elle n'est plus accessible depuis nulle part...

Grossièrement le GC effectue un parcours de graphe depuis les objets
persistants (les objets présents sur la pile d'appel ou les references
static). Si ta structure complexe se trouve sur un ilot isolé non
accessible depuis ces objets "persitants" alors elle sera éliminée.
==== >
Si tu déréférences un Vecteur par exemple (en supposant que ses
éléments ne soient pas référencés ailleurs), on se retrouve face à
la "structure complexe sur un ilôt isolé" non?

Sinon on va finir par regretter les destructeurs du C++ qui eux sont
appelés à coup sur, contrairement au finalize ;)

Eric.


Je reconnais que ma réponse n'est pas tout à fait exacte. C'est une
simplification hative. En fait comme dit plus bas, chaque editeur à son
propre algorithme ( Il n'y a qu'à voir comme est géré le GC des Soft
References pour chaque editeur) . Mais quand la collection devient
complexe ( Collections de Collections de ...), voire partagée par des
threads concurrents, il vaut mieux forcer le dé-référencement dans le
code dans une clause finally d'un try/catch quand le besoin est.


Quant au lien sur la derniere strtégie de Garbage ( Garbage First) elle
est encore toute récente et prometeuse pour se rapprocher du "Temps
Reel" par rapport aux strategie GC Parallel / GC Concurrent plus
ancienne.
Avatar
Christian Laborde
Quel est le rapport de la clause finally d'un try/catch avec la destruction d'un
objet ?

jlp a écrit :
Eric Razny a écrit :
Le Sun, 23 Nov 2008 19:00:24 +0100, jlp a écrit :


Et jusqu'à certains qui croient que le GC est capable de décider ce
qui doit être garbager sans besoin de dé-référencer!




Tiens, je prends le fil au passage :-) Si on a une structure de
données assez complexe (nombreux pointeurs, éventuellement
avec des "cycles"), est-il suffisant de "déréférencer" la ou les
variables qui accèdent à tout ça, ou faut-il "casser" la structure ?
(est-ce que ça change quelque chose pour le GC ?)




Ben non justement, il faut dé-référencer toutes les ressources.
Par exemple la fuite mémoire de débutant sont sur les Collections où
le développeur dé-référence la Collection ( le conteneur) sans
dé-référencer le contenu ( sans faire Collection.clear() par exemple).




Salut.
Es-tu sur?

C'est l'opposé de la réponse de Samuel Devulder
(<4929ac3a$0$27457$)

Pour faire plus simple je la cite :
==== >> Oui elle sera éliminée si elle n'est plus accessible depuis nulle part...

Grossièrement le GC effectue un parcours de graphe depuis les objets
persistants (les objets présents sur la pile d'appel ou les references
static). Si ta structure complexe se trouve sur un ilot isolé non
accessible depuis ces objets "persitants" alors elle sera éliminée.
==== >>
Si tu déréférences un Vecteur par exemple (en supposant que ses
éléments ne soient pas référencés ailleurs), on se retrouve face à
la "structure complexe sur un ilôt isolé" non?

Sinon on va finir par regretter les destructeurs du C++ qui eux sont
appelés à coup sur, contrairement au finalize ;)

Eric.


Je reconnais que ma réponse n'est pas tout à fait exacte. C'est une
simplification hative. En fait comme dit plus bas, chaque editeur à son
propre algorithme ( Il n'y a qu'à voir comme est géré le GC des Soft
References pour chaque editeur) . Mais quand la collection devient
complexe ( Collections de Collections de ...), voire partagée par des
threads concurrents, il vaut mieux forcer le dé-référencement dans le
code dans une clause finally d'un try/catch quand le besoin est.


Quant au lien sur la derniere strtégie de Garbage ( Garbage First) elle
est encore toute récente et prometeuse pour se rapprocher du "Temps
Reel" par rapport aux strategie GC Parallel / GC Concurrent plus
ancienne.



--
Christian Laborde
La Révolution citoyenne, c'est sur : http://c.lab.over-blog.com/
Le forum des électrons libres : http://electrons-libres.forumactif.fr
True E-mail : remove -no-spam-
Sentier des Vinches
CH 1091 Grandvaux
Suisse
1 2 3