et qui est loki aujuste? ou pourrais-je trouver son code?
Boost mes managers veulent pas l'utiliser.
Merci
Version imprimable
et qui est loki aujuste? ou pourrais-je trouver son code?
Boost mes managers veulent pas l'utiliser.
Merci
Je craint alors que Loki, ce ne soit pas mieux. Cf pages bibliothèques C++.
Sinon, j'aurais tendance à regarder la piste de screetch : un new de 500000 ou un reserve/resize de 500000 s'il s'agit d'un vecteur ou d'une liste.
Ouai, surtout que j'ai un chercher dans mes archives de la ML de boost, semblerait que boost.pool est assez contreversé. (notamment au niveau de la complexité de free()), bref pas forcément recommandé. Donc +1 pour tout alloué d'un coup si tu le peux.
Directement inspiré des entrailles de MFC (il y a longtemps...)Mais le small object allocator de Loki est certainement plus universel et dans l'esprit STL.Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72 template <typename T> class Collector { struct Cell { Cell *s_next; //char s_data[sizeof(T) !!! -sizeof(Cell *)]; }; VarBlock f_vb; Cell *f_freeCells; unsigned f_cellsUsedCount,f_cellsPerBlock; //disable copy and assign Collector(const Collector &); Collector & operator=(const Collector &); void Destroy() { DestroyBlocks(f_vb); } bool AllocCells() { typedef char T_Must_Be_Complete[ sizeof(T)>=sizeof(Cell) ? 1 : -1 ]; Cell *cell=(Cell *)NewBlock(f_vb,f_cellsPerBlock*sizeof(T));//may throw bad_alloc if (!cell) return false; // free in reverse order to make it easier to debug ((char *&)cell)+=(f_cellsPerBlock-1)*sizeof(T); for (unsigned u=f_cellsPerBlock;0<u;--u) { cell->s_next=f_freeCells; f_freeCells=cell; ((char *&)cell)-=sizeof(T); } return true; } public: Collector(unsigned cellsPerBlock)//cells per block, which value for what strategy ? :f_freeCells((Cell *)0),f_cellsUsedCount(0),f_cellsPerBlock(cellsPerBlock<2?2:cellsPerBlock) { InitBlocks(f_vb); } ~Collector() { Destroy(); } void Reset(unsigned cellsPerBlock) { Destroy(); f_freeCells=(Cell *)0; f_cellsUsedCount=0; f_cellsPerBlock=(cellsPerBlock<2?2:cellsPerBlock); InitBlocks(f_vb); } unsigned UsedCount() { return f_cellsUsedCount; } unsigned FreeCount() { return unsigned(f_cellsPerBlock*f_vb.s_count)-f_cellsUsedCount; } T *NewMem() { if (f_freeCells==(Cell *)0) if (!AllocCells()) return (T *)0; Cell *newCell=f_freeCells; f_freeCells=newCell->s_next; ++f_cellsUsedCount; return (T *)newCell; } void DeleteMem(T *data) { if (data==(T *)0) return; //??? --f_cellsUsedCount; Cell *cell=(Cell *)data; // debug: should check if cell is actually owned by f_vb!!! cell->s_next=f_freeCells; f_freeCells=cell; } };
A propos des différences de performances constatées dans le tout premier post, le code en apparence plus "complexe" est plus rapide car il utilise beaucoup moins de mémoire (on l'a dit, c'est sans doute la même zone qui est réallouée). Les caches mémoires du processeur peuvent jouer un rôle important.
Salut,
Une question que j'aimerais poser, moi, c'est "as tu réellement besoin en permanence de 500 000 objets :question:"
Je ne dis pas que tu n'en a jamais besoin, loin de là, mais, si tu veux en avoir 500 000 simplement "par sécurité" mais que tu passe une grande partie de ton temps à n'en gérer réellement qu'un nombre largement inférieur (une centaine, voire un millier ou deux), tu gagnera surement (au niveau de l'occupation mémoire, en tout cas) à n'allouer l'espace qu'à la demande, quitte à ce que cela se fasse par coup de 1000 éléments pour éviter les réallocations successives et massives :P
En fait, mon code est une machine virtuelle Javascript.
Donc si dans ton script tu a une boucle avec disons 50.000 elements tu va te retrouver avec un nombre enorme de JSValue crées, disons 150.000.
Je benchmark ma VM avec 500.000 valeurs, il se peut qu'avec un script x ou y un nombre enorme de jsvalue soient créees.
Je ne peux pas savoir a l'avance ce qu'il y a dans le script, donc je dois anticiper une potentielle utilisation enorme de JSValues. Je pense qu'il faut se diriger vers un mempool pour accelerer les allocations, et eviter la fragmentation memoire.
Vous en pensez quoi?
+1 pour le mempool
mais c'est beaucoup plus simple lorsque tu donnes le probleme dans son entier :)
le mempool a beaucoup d'avantages, il est rapide et thread-safe assez facilement (dépendant de la plate-forme, mais sur chaque CPU il y a moyen d'implémenter le mempool en multithread avec des opérations lock-free)
et c'est ce qui te donnera les meilleurs perfs.
Tous tes JSValue font la meme taille ? c'est la condition pour utiliser un mempool.
Je suis d'accord sur le principe d'utiliser un mempool, mais, je suis plus réservé quant à la quantité de données que notre ami s'apprête à lui faire gérer...
En effet, il part du principe de réserver mettons 500 000 éléments "pour être sur d'avoir assez", mais, si on suit le raisonnement jusqu'au bout, alors, pourquoi se "limiter" à 500 000 éléments, et ne pas partir sur 1 000 000, ou un milliard... ou deux :question:
La conséquence ne se fera pas attendre: en fonction du nombre d'éléments choisi à la base et de leur taille, tu risque d'encombrer très sérieusement la mémoire pour... finalement pas grand chose.
J'aurais personnellement tendance à partir sur un pool plus "restreint" (mettons, un petit millier d'éléments), quitte à doubler cette taille lorsque l'on en atteint la limite.
On perdra, sans doute, un peu de temps à retravailler le pool lorsque l'on atteint la limite, mais, au moins, tu n'en viens pas à affréter un cargo entier pour un kilo de cacahuètes :D
tout la est la question, comment savoir la taille du mempool. J ai jamais travailler avec un mempool en fait, je comprend juste le principe, peut etre que quelqu un de plus eclaire pourrait m expliquer comment dans les applications commerciales on gere la taille de ce mempool, en fonction de quel critere, ...
Merci
l'avantage des mempools c'est qu'ils sont redimensionnable.
Le principe, c'est simplement une liste chaînée de blocs libres, on prend la tete quand on en a besoin et on remet en tete de liste lorsqu'on en libère.
Au milieu du programme, si le besoin se fait sentir, rien ne t'empeche de réallouer un gros bloc, le couper en petits morceaux et le mettre dans la liste. Le fait qu'il y a duex gros blocs mémoire au lieu d'un est invisible aux yeux de l'appelant.
Le bout de code proposé en #24 fait exactement ce que tu décris screetch, sauf que la taille des gros blocks est définie une fois pour toute lors de l'instantiation du pooler. Ce code est modifiable afin de pouvoir changer cette taille en cours de route.
ok c est plus clair la, je vais essayer ce code decrit en #24 et voir ce que cela donne.
Merci encore, on avance bien dans ce forum
Tu vas avoir besoin de ceci dans un .h:Et ceça dans un .cpp:Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 /* VarBlock is an allocator of memory blocks that are linked in a list. "s_count" of the root list element contains the count of allocated blocks. "s_count" of allocated blocks contains the count of available block bytes. */ struct VarBlock { VarBlock *s_next; size_t s_count; //char strBuf[]; }; inline void InitBlocks(VarBlock & vb) { vb.s_next=(VarBlock *)0;vb.s_count=0; } void *NewBlock(VarBlock & vb,size_t blockSize); void DestroyBlocks(VarBlock & vb); bool DeleteBlock(VarBlock & vb,void *block); bool IsBlock(const VarBlock & vb,const void *block);
Aussi, il faut comprendre le template dans #24. C'est assez bas niveau comme code. Il ne fait qu'allouer et désallouer de la mémoire dans le pooler. Il doit rester en vie tant que des fragments de mémoire sont actifs.Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59 void *NewBlock(VarBlock & vb,size_t blockSize) { //assert(blockSize>0); //debug VarBlock *pb=(VarBlock *)::operator new(sizeof(VarBlock)+blockSize); if (pb) { pb->s_next=vb.s_next; pb->s_count=blockSize; vb.s_next=pb; // change head (adds in reverse order for simplicity) ++vb.s_count; return pb+1; } return 0; } void DestroyBlocks(VarBlock & vb) { VarBlock *pb=vb.s_next; while (pb!=(VarBlock *)0) { VarBlock *t_pb=pb->s_next; ::operator delete(pb); pb=t_pb; } vb.s_next=(VarBlock *)0; vb.s_count=0; } bool DeleteBlock(VarBlock & vb,void *block) { VarBlock **ppb=&vb.s_next; VarBlock *pb=((VarBlock *)block)-1; while (*ppb!=(VarBlock *)0) { if (*ppb==pb) { *ppb=pb->s_next; ::operator delete(pb); --vb.s_count; return true; } ppb=&(*ppb)->s_next; } //assert(*ppb!=(VarBlock *)0); //debug return false; } bool IsBlock(const VarBlock & vb,const void *block) { VarBlock *t_pb=vb.s_next; VarBlock *pb=((VarBlock *)block)-1; while (t_pb!=(VarBlock *)0) { if (t_pb==pb) return true; t_pb=t_pb->s_next; } return false; }
Tu dois par ailleurs gérer toi-même la construction (avec displacement new) et la destruction (via appel explicite de destructeur e.g. p->~Object()) de tes instances de classe.
Solution : utiliser un mempool.
Merci a tous