Bonjour a vous,
Je cherche a allouer un tableau d'objet (de type foo) en C++ en controllant independement la creation de chacun d'entre eux. Cependant, si j'utilise l'operateur new[], alors les objets sont construits avec le constructeur par defaut et il me faut ensuite les affecter.
Je veux aussi qu'ils soient contigus en memoire, donc je ne peux pas ajouter d'indirection a la "foo** f =new f*[size]; for (int i=0; i<size, i++) f[i] = new foo(bar++);"
Je ne sais donc pas trop comment faire ca. une facon de faire est d'utiliser un "placement new" comme presente dans [1]. Cependant quand je fais ca, je dois detruire tous les objets manuellement, et je ne peux pas juste faire "delete[] f;". Savez vous pourquoi ?
Voyez vous une autre facon de faire ca?
Qu'est ce que je devrais savoir de plus sur placement new pour ne pas me tirer une balle dans le pied ?
Merci de vos commentaires.
[1] http://pastebin.com/Xw5sijQU
Pas sur d'avoir tout saisi. Tu veux qu'en allouant en tableau de la facon la plus minimale le constructeur change son comportement ?
Si c'est le cas et que j'ai a peu pres compris tes contraintes je crois qu'il faudrait passer par quelques attributs statiques dans ta classe foo, ou un fichier ou quoi que ce soit qui garde implicitement en memoire.
Mais je sens que je suis a cote de la plaque.
Basiquement, je veux passer des parametres au constructeur de chaque element du tableau. Et pour chaque element j'ai une regle complexe qui me sert a determiner quel constructeur appeller ave quel parametre.
Pour l'exemple, on peut supposer que le constructeur prends un entier en parametre, pour le premier element, le parametre vaut 1, pour le deuxieme il vaut 2... Mais moralement, la regle est complexe.
Je ne peux pas construire l'element avec le constructeur par defaut, sinon, le monde s'ecroule.
J'espere avoir ete plus clair.
Basiquement definitely sounds like an anglicism to me
Tu as ete plus clair mais je ne suis pas sur de comprendre toutes les contraintes que tu (t')ajoutes. Je vais y reflechir a tete reposee la je bosse.
J'avais lu cet article il n'y à pas longtemps où il utilise malloc et free ça ressemble un peu à ta solution [1] mais ça ne résout pas le problème du delete[]
http://altdevblogaday.org/2011/05/17/keep-data-close-part-1/
tbop2, je n'ai aucun indice de ce que tu parles!
Paulop, merci pour ce lien, je le lirais en details.
C'est peut-être trop tard mais bon:
Je ne comprend probablement pas ce que tu veux mais pourquoi ne pas utiliser un std::vector (ou une std::list) pour stocker tes objets? Ca te permet de créer tes objets quand tu en as besoin avec le ctor de ton choix.
Par exemple:
http://pastebin.com/1LJQgXv1
En vrai je converti un code depuis des vectors vers des tableaux, parceque les vector (et list) sont lents.
PS: j'ai des benchmarks a l'appuie.
Ha ok! D'accord c'est plus lent mais est)ce vraiment un problème? Ca me fait penser à une optimisation prémature...
Perso, dans le projety de moteur graphique qui m'occupe j'tulise extensivement la stl et ce n'est absolument pas un bottleneck pour moi.
A quoi ressemble le ctor de tes objets?
Ne peut tu pas passer des params par défauts et les initialiser par après?
Avec un tableau en style c, tu ne peux pas avoir de taille variable, tu vas devoir te retrouver à faire ce que fait la stl (probablement en mieux d'ailleur) c'est à dire ajouter un nouveau niveau d'indirection et recréer un nouvrau tableau à chaque fois que sa taille change et copier tous les elems dans le nouveau tableau.
Ha un autre truc, utilise tu std::vector<T>::reserve quand tu crée un vecteur, ça pourrait grandemene améliiorer la vitesse des opérations!
renega_666, le code sur lequel je travaille est quasiment termine et a ete prototype par un physicien pour rendre le code correcte. Mon travail actuellement est de rendre le code plus rapide. L'utilisation approprie de structure de taille fixe permet de reduire de le nombre de test pour verifier si il est necessaire d'allouer plus de memoire.
Ca permet egalement a toutes la structure de donne de tenir dans une memoire plus petite. Cela pour plusieurs raison comme toutes les allocations sont statiques et contigue en memoire (bonne propriete pour une utilisation correcte du cache du processeur), je peux utiliser des entiers plutot que des pointeurs, sur une machine 64 bits, ca me permet d'utiliser que 4 bytes au lieu de 8 bytes pour addresser des elements. D'ailleurs certaines structure ne contiendront jamais plus de 64K valeur, ce qui me permet d'utiliser un entier 16 bits reduisant l'empreinte memoire de la structure d'un facteur 4.
Pour les contraintes de taille des structures de donnes, on compile des versions differentes pour chaque type de simulation que le physicien essaye de realiser.
Globalement, ces optimisations ont apporte de l'ordre de 25% de gain de performances. Ce qui permet de faire tourner des simulations plus large ou d'utiliser 25% de machines en moins.
Si ca t'interesse les prochaines etapes sont : verifier que les kernels de calcul sont correctement vectorialise, puis envisager une implementation parallele a memoire partage pour pouvoir reutiliser certains calcul qui n'ont besoin d'etre fait qu'une seule fois. Si c'est toujours trop lent, on ira peut exporter une partie des calculs sur un GPU ou sur les nouveau accelerateurs d'intel ou d'amd...
Ok,maintenant je comprend mieux pourquoi tu as décidé de changer cette partie du code. (Souvent on entend des gens dire la stl c'est trop lent alors qu'ils n'ont même pas profilé d'où ma question...).
Pour en revenir à ton problème,à ma connaissance,en utilisant un tableau style C, le constructeur par défaut sera toujours appelé, quoi que tu fasses!
Il faut t'arranger pour scinder l'initialisation de la construction si c'est possible ou du moins prévoir une initialisation par défaut qui ne plante pas toute l'application.
Sinon, et j'y reviens, utiliser au mieux les classes de la stl peut apporter un boost des performance;notamment réserver l'espace de ton std::vector permet
de ne pas gâcher de la mémoire et d'éviter de multiple destructions de valeurs temporaires et réallocations(lors du premier push back, si le tableau n'as pas une capacité réservée,il alloue lui même un espace assez conséquent). En utilisant le std::vector correctement, les performance sont fortement comparables à celle d'un tableau de style c (maintenant je n'ai pas profilé pour comparer mais normalement le std::vector n'est qu'un wrapper autour d'un tableau c, une std::list par contre est une toute autre affaire).
Aussi,désactiver la validation des iterator permet de gagner en performance. Ou alors il reste la solution d'utiliser stlport qui est réputée rapide.
Ha et tant que j'y suis, si tu en as la possibilité,compiler le code en activant le support du c++0x et implémenter les move constructor pourrait
aider(http://en.wikipedia.org/wiki/C%2B%2B0x#Rvalu
e_references_and_move_constructors)
Quel compilateur utilises tu?
Foo( ParamComplex* param = ParamComplex::Default);
Foo* array = new Foo[SIZE];
for( int i = 0; i < SIZE; i++)
{
}
owned by tab ![]()
Un truc du genre n'est paspossible?
Foo
{
Foo( ParamComplex* param = ParamComplex::Default);
void Init(ParamComplex* param);
...
};
Foo* array = new Foo[SIZE];
for( int i = 0; i < SIZE; i++)
{
array[i].Init(new ParamCoplex(....));
}
Le code est compile avec le compilateur d'intel. Les vectors sont plus lent que les tableaux d'a peu pres 15% selon le timing de mes operations.
Je ne prefere pas utiliser de fonctionnalite experimentale de C++ parceque je ne sais pas avec quel compilateur le code sera compile sur la machine finale.
"Pour en revenir à ton problème,à ma connaissance,en utilisant un tableau style C, le constructeur par défaut sera toujours appelé, quoi que tu fasses!"
C'est a ca que sert "placement new", regarde l'exemple de code de mon premier message.
Ajouter un constructeur ou une methode d'initialisation n'est pas une solution facile parceque l'objet cree de facon sous jacente est complexe. Certaines des operations des constructeurs ont des effets de bord. Comme je ne suis pas l'auteur principal du code, je ne veux pas faire de modification qui pourrait modifier silencieusement le comportement du programme. On a des tests de regression bien evidement, mais ils sont assez sommaire et pourraient ne pas detecter des problemes subtile. Donc je prefere ne pas prendre de risque.
Je te remercie pour tes contributions mais l'utilisation de placement new a regler mon probleme.
Je ne connaissais pas le placement new (content d'apprendre
)
"Je te remercie pour tes contributions mais l'utilisation de placement new a regler mon probleme. "
De rien mais tu aurais dû le dire tout de suite lol!
Et donc? L'optimisation en valait-elle la peine?
Bonne continuation pour ton projet qui a l'air très intéressant!
sur la structure qui stocke des elements qui n'ont pas de constructeur par defaut, passer de vector a un tableau a diminue le temps de calcul de l'application de 3%. Je n'ai pas fini de faire tourner le benchmark complet sur les grosses instances, mais globalement utiliser des structures de taille fixe a reduit le temps total de l'application de 10%.
Il y a encore pas mal d'utilisation de vector mais sur des structures dont je ne sais pas predire la taille et ils sont utilise partout. Donc je vais profiler pour verifier, mais problablement qu'ils vont rester comme ca... Je pense que le plus gros bottleneck est dans des kernels ecrit il y a 20 ans en fortran...
Pas mal!
"Je pense que le plus gros bottleneck est dans des kernels ecrit il y a 20 ans en fortran..."
Il y a des chances en effet...
Allez bonne soirée et bon codage!