Envoyé par
DonQuiche
Oui c'est une gestion manuelle qui rompt avec le comptage de références. Et tous les graphes d'objets ne sont pas aussi simples que des arbres bidirectionnels, si bien qu'utiliser correctement ces weak_ptr peut être un casse-tête et une source de bogues. En tout cas c'est très loin d'être transparent, rien à voir avec le GC.
Il faudrait que j'ai un exemple de graphes d'objets "compliqué pour voir.
Justement non car c'est une incrémentation
atomique. Le problème est que lorsque deux threads tentent simultanément d'incrémenter une variable, celle-ci n'est parfois incrémentée qu'une seul fois au total, si les instructions se sont produites dans l'ordre suivant :
Thread1 : charger variable de la mémoire vers le registre
Thread2 : charger variable de la mémoire vers le registre
Thread2: incrémenter registre puis mettre le résultat en mémoire
Thread1: incrémenter registre puis mettre le résultat en mémoire
Autant pour moi .
D'un côté ton coût est proportionnel au nombre d'assignations, de l'autre il est proportionnel au nombre de nouveaux objets depuis la dernière passe du GC (c'est l'intérêt d'un algo générationnel). Le second est plus faible que le premier.
D'où tiens-tu cela ?
C'est vrai sauf qu'en pratique ce n'est pas un problème : les seules fois où on a besoin d'un destructeur en code managé c'est pour libérer des ressources non-managées. Or dans un tel cas de figure, assez rare, on préfère utiliser un idiome qui enferme le traitement consommant ces ressources dans un bloc qui libère explicitement les ressources à la fin et désenregistre le destructeur. L'exemple typique c'est celui du fichier :
1 2 3 4
| using (var file = new FileStream("c:\\truc.txt"))
{
ReadFile(file);
} |
En pratique je dois écrire trois destructeurs par an et ils ne sont jamais exécutés sauf exception.
Mais si on a besoin d'utiliser une ressource qui doit durer plus d'un bloc ?
Exemple pour les BDD ou des fichiers de logs, qu'on ne sait pas forcément quand ils seront fermés.
Oui c'est l'avantage du C++ : pouvoir tout faire. Sauf que ton temps de développement n'est pas infini, que tu as déjà perdu suffisamment de temps à gérer tes références cycliques et à écrire des destructeurs, et que de toute façon ce genre d'optimisation est peu rentable par rapport à un choix judicieux d'algorithme et de parallélisation. Et à ce dernier titre pouvoir écrire rapidement un code fiable, simple et lisible est un gros avantage.
En quoi écrire des constructeur est une "perte de temps" ?
Avec le principe RAII et les shared_ptr/unique_ptr, on a pas grand chose à faire au niveau de la mémoire et on peut alors se charger de faire des actions plus spécifiques qu'on retrouvera de toute façon avec un GC.
Par rapport au C#, je ne vois pas en quoi le code serait plus "rapide" que le C++ par exemple pour peu qu'on utilise les bonnes bibliothèques...
Envoyé par
DonQuiche
100 cycles ce n'est pas énorme. Mais quand tu dois payer ce prix à chaque assignation c'est un autre problème : à 60 ips, entre deux images, tu passes de 10M à 100k assignations possibles.
En C++11 (en C++14 on a std::make_ptr ou un truc du genre), il te suffit de faire cela :
std::shared_ptr ptr(new Pixels[BEAUCOUP]);
Donc une assignation d'une image ne coûte absolument rien.
Par contre, si le GC doit tester chaque élément du tableau... Même s'il ne le fait pas souvent c'est quand même assez .
Partager