Bonjour,
j'ai fait quelque Benchmark et je me pose des questions. avant tout un petit warning:
ATTENTION : ici il n'est pas question de discuter de C vs C++ en terme de performance, ceux que ca interesse je suis sur qu'il y a pleins de sujet ouvert. les questions sont poses en fin de poste, ce poste est long car je prensente le probleme dans l'ordre.
Commencons:
J'ai ecris un moteur de jeu en C et avant d'arriver trop loin je me suis dis que comme j'ai de plus en plus besoin de C++ (nouveau boulot exige) je vais le reecrire en c++ pour bien exercer et mieux comprendre ce language. En d'autres terme ici il n'y a aucun imperatif particulier sinon apprendre et me perfectionner. J'ai donc decider de commencer par la gestion de la memoire, j'ai un allocateur plutot complex en C et pour apprendre j'ai fait un tres simple venant d'un tutoriel IBM qui etait justement en C++ et c'est la que j'ai eu le premier probleme.
Problem :
Le test montre difference de 3secondes entre l'utilisation de la memoryPool et le programme temoin utilisant juste des new/delete pour 100.000 boucles allouant et detruisant 10.000 objets. la difference n'avait parrut faible j'ai donc par la suite ecrit le meme code en pur C en me forcant a traduire de tel sorte que les codes aient le moins de differences possibles pour ne pas biaiser le resultat en faveur du C. et la, le programe C avec memoryPool gagne 6 secondes sur le temoin en C (malloc/free). Cependant les 2 programmes temoins (malloc/free vs new/delete) alterne les performances, en gros c'est du 50 , 50 donc je les consideres equivalent. resultat des benchmarks (ca ete fait des dizaines de fois resultat moyens) je vais fournir aussi tous les codes pour pouvoir etre eclaire sur ce qui clochej'ai passer deux jours a lire et relire le code pour comprendre pouquoi un tel ecart et puis j'ai eu l'idee de faire un 2eme code C++. dans le nouveau code ici l'objet alloue n'est pas une class mais une simple structure et la ca change tout. voici les resultat moyens:C with malloc/free :
real 0m18.945s
user 0m18.893s
sys 0m0.001s
_________________________
real 0m13.073s
user 0m13.035s
sys 0m0.001s
_________________________
CPP with new/delete :
real 0m18.928s
user 0m18.877s
sys 0m0.000s
_________________________
CPP with memory pool oop:
real 0m16.257s
user 0m16.206s
sys 0m0.001s
ATTENTION : encore une fois j'ai fait des tonnes de test et voir C(0m13.327s) et Cpp(0m13.820s) ne signifie rien car dans le test suivant les temps peuvent etre inverse.
en gros a la fin j'ai conclus en terme de vitesse (Malloc/free = new/delete ) < (class pool) < (pure Cpool = cpp sans class) en terme de vitesse dans mon exemple.C with malloc/free :
real 0m19.051s
user 0m18.998s
sys 0m0.001s
_________________________
C with memoryPool :
real 0m13.327s
user 0m13.284s
sys 0m0.000s
_________________________
CPP with new/delete :
real 0m19.574s
user 0m19.513s
sys 0m0.001s
_________________________
CPP with memory pool oop:
real 0m16.239s
user 0m16.192s
sys 0m0.001s
_________________________
CPP with memory pool struct:
real 0m13.820s
user 0m13.778s
sys 0m0.001s
Avant de poster les codes je rappel que le but n'est pas de prouver qui est plus rapide car j'ai suffisement lu et compris pourquoi que Cpp n'a rien a envier au C en terme de vitesse en general et que dans des cas TRES particulier l'un est plus rapide que l'autre. Je le rappel c'est pas le debat, Je viens vers vous pour analyser le code et voir ce que j'ai mal fait en C++. Oui je suspect le programmeur en l'occurence ici moi.
ps: j'avais vu que virtual pouvait ralentir, j'ai supprimer les functions virtuelles mais la difference de vitesse pour mon cas etait pas significative et j'ai donc remis car dans mon design j'en aurai besoin (pour des raisons de productivite)
c'est partie:
on appellera temoin le programme brut ecrit n'est pas suppose etre le plus rapide
CODE 1 : temoin.c
CODE 2 : temoin.cpp
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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 #include <stdlib.h> #include <stdio.h> struct Objtest { int i; float f; }; //------------- Main fucntion --------------- int main(int argc, char **argv) { struct Objtest * o[10000]; for(int i = 0; i< 100000; ++i) { for(int j = 0; j < 10000; ++j) { o[j] = (struct Objtest *) malloc(sizeof(struct Objtest)); o[j]->i = i; o[j]->f = 2.0f; } for(int j = 0; j < 10000; ++j) free(o[j]); } return EXIT_SUCCESS; }
CODE 1 : main.c , memory.c, memory.h
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 class Objtest { public: Objtest(int a, float b):i(a), f(b){}; private: int i; float f; }; //------------- Main fucntion --------------- int main(int argc, char **argv) { Objtest * obj[10000]; for(int i = 0; i < 100000; ++i) { for(int j = 0; j < 10000; ++j) obj[j] = new Objtest(i,2.0f); for(int j = 0; j < 10000; ++j) delete obj[j]; } return 0; }
CODE 2 :main_class.cpp, memory_class.cpp, memory_class.h
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105 /******************** main.c ********************************/ #include <stdlib.h> #include <stdio.h> #include "memory.h" struct Objtest { int i; float f; }; //------------- Main fucntion --------------- int main(int argc, char **argv) { struct Objtest * o[10000]; memory_init(); for(int i = 0; i< 100000; ++i) { for(int j = 0; j < 10000; ++j) { o[j] = (struct Objtest *) getMemory(); o[j]->i = i; o[j]->f = 2.0f; } for(int j = 0; j < 10000; ++j) release(o[j]); } memory_destroy(); return EXIT_SUCCESS; } /******************** memory.c ********************************/ #include <stdlib.h> #include "memory.h" typedef struct mnode { struct mnode * next; }mnode; static mnode * head = NULL; static void expand(); /*--------------------------------------------------------------------------------*/ void * getMemory() { if(NULL == head) expand(); mnode * h = head; head = h->next; return h; } /*--------------------------------------------------------------------------------*/ void release(void * ptr_to_delete) { mnode * h = (mnode *) ptr_to_delete; h->next = head; head = h; return; } /*--------------------------------------------------------------------------------*/ void memory_destroy() { mnode * nextPtr = head; for(;nextPtr; nextPtr = head) { head = head->next; free(nextPtr); } return; } /*--------------------------------------------------------------------------------*/ static void expand() { size_t POOLSIZE = 1000; size_t size = (8 > sizeof(mnode)) ? 8 : sizeof(mnode); mnode * h = (mnode *)malloc(sizeof(char) * size); head = h; for(int i = 0; i < POOLSIZE; i++) { h->next = (mnode *)malloc(sizeof(char) * size); h = h->next; } h->next = NULL; } /******************** memory.h ********************************/ #ifndef __MEMORY_H_ #define __MEMORY_H_ void memory_init(); void memory_destroy(); void * getMemory(); void release(void * ptr_to_delete); #endif
CODE 2 :main.cpp, memory.cpp, memory.h
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131 /******************** main.cpp ********************************/ #include "memory.h" class Objtest { public: Objtest(int a, float b):i(a), f(b){}; void * operator new (size_t size) { return mm->allocate(size);}; void operator delete(void * ptr_to_free) { return mm->free(ptr_to_free);}; private: int i; float f; }; //------------- Main fucntion --------------- int main(int argc, char **argv) { Mmanager_init(); Objtest * o[10000]; for(int i = 0; i < 100000; i++) { for(int j = 0; j < 10000; j++) o[j] = new Objtest(1, 2.0f); for(int j = 0; j < 10000; j++) delete o[j]; } return 0; } /******************** memory.cpp ********************************/ #include "memory.h" Mmanager * mm = NULL; void Mmanager_init(void) { mm = new Mmanager(); } /*--------------------------------------------------------------------------------*/ Mmanager::Mmanager() { head = NULL; } /*--------------------------------------------------------------------------------*/ Mmanager::~Mmanager() { cleanUp(); } /*--------------------------------------------------------------------------------*/ inline void * Mmanager::allocate(size_t size) { if(NULL == head) expandPool(); mnode * h = head; head = h->next; return h; } /*--------------------------------------------------------------------------------*/inline void Mmanager::free(void * deleted) { mnode * h = static_cast<mnode *>(deleted); h->next = head; head = h; } /*--------------------------------------------------------------------------------*/ void Mmanager::expandPool() { size_t POOLSIZE = 1000; size_t size = (8 > sizeof(mnode)) ? 8 : sizeof(mnode); mnode * h = reinterpret_cast<mnode *>(new char[size]); head = h; for(int i = 0; i < POOLSIZE; i++) { h->next = reinterpret_cast<mnode *>(new char[size]); h->next = (mnode *)malloc(sizeof(char) * size); h = h->next; } h->next = NULL; } /*--------------------------------------------------------------------------------*/ void Mmanager::cleanUp() { mnode * nextPtr = head; for(;nextPtr; nextPtr = head) { head = head->next; delete [] nextPtr; } } /******************** memory.hpp ********************************/ #ifndef __MEMORY_H #define __MEMORY_H #include <cstddef> class IMemoryManager { public : virtual void * allocate(size_t) = 0; virtual void free(void *) = 0; }; /*--------------------------------------------------------------------------------*/ class Mmanager: public IMemoryManager { private: struct mnode { mnode * next; }; void expandPool(); void cleanUp(); mnode * head; public: Mmanager(); virtual ~Mmanager(); virtual void * allocate(size_t); virtual void free(void *); }; extern Mmanager * mm; void Mmanager_init(void); #endif
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102 /******************** main ********************************/ #include "memory.h" struct Objtest { int i; float f; }; int main(int argc, char **argv) { Objtest * o[10000]; for(int i = 0; i < 100000; i++) { for(int j = 0; j < 10000; j++) { o[j] = static_cast<Objtest *>(Mmanager::allocate()); o[j]->i = i; o[j]->f = 2.0f; } for(int j = 0; j < 10000; j++) Mmanager::free(o[j]); } return 0; } /******************** memory.c ********************************/ #include "memory.h" mnode * Mmanager::head = NULL; Mmanager::Mmanager() { head = NULL; } /*--------------------------------------------------------------------------------*/ void * Mmanager::allocate() { if(NULL == head) { size_t POOLSIZE = 1000; size_t size = (8 > sizeof(mnode)) ? 8 : sizeof(mnode); mnode * h = reinterpret_cast<mnode *>(new char[size]); head = h; for(int i = 0; i < POOLSIZE; i++) { h->next = reinterpret_cast<mnode *>(new char[size]); h = h->next; } h->next = NULL; } mnode * h = head; head = h->next; return h; } /*--------------------------------------------------------------------------------*/ void Mmanager::free(void * deleted) { mnode * h = static_cast<mnode *>(deleted); h->next = head; head = h; } /*--------------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------------*/ void Mmanager::cleanUp() { mnode * nextPtr = head; for(;nextPtr; nextPtr = head) { head = head->next; delete [] nextPtr; } } /******************** memory.h ********************************/ #ifndef __MEMORY_H #define __MEMORY_H #include <cstddef> struct mnode { mnode * next; }; /*--------------------------------------------------------------------------------*/ class Mmanager { private: static mnode * head; public: Mmanager(); static void * allocate(); static void free(void *); static void cleanUp(); }; #endif
La question ici c'est que fais qui n'est pas bien et qui provoque un tel ecart, et sinon si les codes sont equivalent alors qu'elle est l'interet d'utiliser des objets si vraiment c'est plus lent que de ne pas en utiliser?
Merci pour avoir eut la patience de lire tout ca.
Partager