如果一个内存池需要线程同步了,估计和默认的内存操作也差不了多远了。现在对内存操作的优化只是在优化线程同步操作上面了。默认的lock和unlock可能实现得过于完美,因而要求更多的cpu周期。如果选择更原子的lock和unlock实现,还是可以加快内存操作的速度的。
多线程内存池在实现上也就是在申请和释放外面包裹了一对加锁和解锁操作而已。
如果我们采用模板编程,就可以实现一个功能强悍的模板类,该模板类有两个参数,单线程内存池和锁。这样的话,可以组合出很多情况。比如说,单线程内存池有对象大小固定和不固定两种实现,锁在Windows下可以用临界区,互斥体,信号量,事件实现。这样最多可以组合出8种不同的实例化。
一般说来互斥体比临界区慢很多,在这里可以进行很好的测试。我实现了临界区和互斥体版本的锁,经过测试发现前者比后者快30多倍。因为互斥体据说是内核对象,而临界区是用户态对象,那么互斥体的使用就需要系统在用户态和内核态之间进行切换,肯定会消耗更多的时间。
互斥体版本代码如下,
1 | #include <stdio.h> |
2 | #include <stdlib.h> |
3 | #include <time.h> |
4 | #include <windows.h> |
5 |
6 | template < typename T> class MemoryPool |
7 | { |
8 | public : |
9 | MemoryPool( size_t size = EXPANSION_SIZE) |
10 | { |
11 | expandTheFreeList(size); |
12 | } |
13 | ~MemoryPool(); |
14 | //allocate a T element from the free list. |
15 | void * alloc( size_t size) |
16 | { |
17 | if (next == NULL) |
18 | { |
19 | expandTheFreeList(); |
20 | } |
21 | MemoryPool<T>* head = next; |
22 | next = head->next; |
23 | return head; |
24 | } |
25 | //return a T element to the free list. |
26 | void free ( void * doomed) |
27 | { |
28 | MemoryPool<T>* head = static_cast < MemoryPool<T>* > (doomed); |
29 | head->next = next; |
30 | next = head; |
31 | } |
32 |
33 | private : |
34 | //next element on the free list. |
35 | MemoryPool<T>* next; |
36 | //if the freelist is empty, expand it by this amount. |
37 | enum {EXPANSION_SIZE = 32}; |
38 | //add free elements to the free list |
39 | void expandTheFreeList( int howMany = EXPANSION_SIZE); |
40 | }; |
41 |
42 | template < typename T> MemoryPool<T> :: ~MemoryPool() |
43 | { |
44 | MemoryPool<T>* nextPtr = NULL; |
45 | while (nextPtr) |
46 | { |
47 | nextPtr = next; |
48 | next = next->next; |
49 | delete [] nextPtr; |
50 | } |
51 | } |
52 |
53 | template < typename T> void MemoryPool<T> :: expandTheFreeList( int howMany) |
54 | { |
55 | //we must allocate an object enough to contain the next pointer |
56 | size_t size = sizeof (T) > sizeof (MemoryPool<T>*) ? sizeof (T) : |
57 | sizeof (MemoryPool<T>*); |
58 |
59 | MemoryPool<T>* runner = (MemoryPool<T>*) new char [size]; |
60 | next = runner; |
61 | for ( int i = 0; i < howMany; ++i) |
62 | { |
63 | runner->next = (MemoryPool<T>*) new char [size]; |
64 | runner = runner->next; |
65 | } |
66 | runner->next = NULL; |
67 | } |
68 |
69 | class ABClock //abstract base class |
70 | { |
71 | public : |
72 | virtual ~ABClock() {} |
73 | virtual void lock() = 0; |
74 | virtual void unlock() = 0; |
75 | }; |
76 |
77 | class MutexLock : public ABClock |
78 | { |
79 | public : |
80 | MutexLock() |
81 | { |
82 | hMutex = CreateMutex(NULL, FALSE, NULL); |
83 | } |
84 | ~MutexLock() |
85 | { |
86 | CloseHandle(hMutex); |
87 | } |
88 | void lock() |
89 | { |
90 | WaitForSingleObject(hMutex, INFINITE); |
91 | } |
92 | void unlock() |
93 | { |
94 | ReleaseMutex(hMutex); |
95 | } |
96 | private : |
97 | HANDLE hMutex; |
98 | }; |
99 |
100 | template < typename POOLTYPE, typename LOCK> |
101 | class MTMemoryPool |
102 | { |
103 | public : |
104 | //allocate an element from the freelist. |
105 | void * alloc( size_t size) |
106 | { |
107 | void * mem; |
108 | theLock.lock(); |
109 | mem = stPool.alloc(size); |
110 | theLock.unlock(); |
111 | return mem; |
112 | } |
113 |
114 | //return an element to the freelist |
115 | void free ( void * someElement) |
116 | { |
117 | theLock.lock(); |
118 | stPool. free (someElement); |
119 | theLock.unlock(); |
120 | } |
121 |
122 | private : |
123 | POOLTYPE stPool; //Single-threaded pool. |
124 | LOCK theLock; |
125 | }; |
126 |
127 | class Rational |
128 | { |
129 | public : |
130 | Rational( int a = 0, int b = 1) : n(a), d(b) {} |
131 | void * operator new ( size_t size) |
132 | { |
133 | return memPool->alloc(size); |
134 | } |
135 | void operator delete ( void * doomed, size_t size) |
136 | { |
137 | memPool-> free (doomed); |
138 | } |
139 | static void newMemPool() |
140 | { |
141 | memPool = new MTMemoryPool< MemoryPool<Rational>, MutexLock>; |
142 | } |
143 | static void deleteMemPool() |
144 | { |
145 | delete memPool; |
146 | } |
147 |
148 | private : |
149 | int n; |
150 | int d; |
151 | static MTMemoryPool< MemoryPool<Rational>, MutexLock>* memPool; |
152 | }; |
153 | MTMemoryPool< MemoryPool<Rational>, MutexLock>* Rational::memPool = NULL; |
154 |
155 | int main() |
156 | { |
157 | const int ARRAY_SIZE = 1000; |
158 | const int LOOP_TIMES = 5000; |
159 |
160 | Rational* array[ARRAY_SIZE]; |
161 | clock_t beg = clock (); |
162 | Rational::newMemPool(); |
163 | for ( int i = 0; i < LOOP_TIMES; ++i) |
164 | { |
165 | for ( int j = 0; j < ARRAY_SIZE; ++j) |
166 | { |
167 | array[j] = new Rational(j); |
168 | } |
169 | for ( int j = 0; j < ARRAY_SIZE; ++j) |
170 | { |
171 | delete array[j]; |
172 | } |
173 | } |
174 | clock_t end = clock (); |
175 | printf ( "use %f second(s).\n" , 1.0 * (end - beg) / CLOCKS_PER_SEC); |
176 | system ( "pause" ); |
177 | Rational::deleteMemPool(); |
178 |
179 | return 0; |
180 | } |
运行结果,
临界区版本代码如下,
1 | #include <stdio.h> |
2 | #include <stdlib.h> |
3 | #include <time.h> |
4 | #include <windows.h> |
5 |
6 | template < typename T> class MemoryPool |
7 | { |
8 | public : |
9 | MemoryPool( size_t size = EXPANSION_SIZE) |
10 | { |
11 | expandTheFreeList(size); |
12 | } |
13 | ~MemoryPool(); |
14 | //allocate a T element from the free list. |
15 | void * alloc( size_t size) |
16 | { |
17 | if (next == NULL) |
18 | { |
19 | expandTheFreeList(); |
20 | } |
21 | MemoryPool<T>* head = next; |
22 | next = head->next; |
23 | return head; |
24 | } |
25 | //return a T element to the free list. |
26 | void free ( void * doomed) |
27 | { |
28 | MemoryPool<T>* head = static_cast < MemoryPool<T>* > (doomed); |
29 | head->next = next; |
30 | next = head; |
31 | } |
32 |
33 | private : |
34 | //next element on the free list. |
35 | MemoryPool<T>* next; |
36 | //if the freelist is empty, expand it by this amount. |
37 | enum {EXPANSION_SIZE = 32}; |
38 | //add free elements to the free list |
39 | void expandTheFreeList( int howMany = EXPANSION_SIZE); |
40 | }; |
41 |
42 | template < typename T> MemoryPool<T> :: ~MemoryPool() |
43 | { |
44 | MemoryPool<T>* nextPtr = NULL; |
45 | while (nextPtr) |
46 | { |
47 | nextPtr = next; |
48 | next = next->next; |
49 | delete [] nextPtr; |
50 | } |
51 | } |
52 |
53 | template < typename T> void MemoryPool<T> :: expandTheFreeList( int howMany) |
54 | { |
55 | //we must allocate an object enough to contain the next pointer |
56 | size_t size = sizeof (T) > sizeof (MemoryPool<T>*) ? sizeof (T) : |
57 | sizeof (MemoryPool<T>*); |
58 |
59 | MemoryPool<T>* runner = (MemoryPool<T>*) new char [size]; |
60 | next = runner; |
61 | for ( int i = 0; i < howMany; ++i) |
62 | { |
63 | runner->next = (MemoryPool<T>*) new char [size]; |
64 | runner = runner->next; |
65 | } |
66 | runner->next = NULL; |
67 | } |
68 |
69 | class ABClock //abstract base class |
70 | { |
71 | public : |
72 | virtual ~ABClock() {} |
73 | virtual void lock() = 0; |
74 | virtual void unlock() = 0; |
75 | }; |
76 |
77 | class CriticalSectionLock : public ABClock |
78 | { |
79 | public : |
80 | CriticalSectionLock() |
81 | { |
82 | InitializeCriticalSection(&csMyCriticalSection); |
83 | } |
84 | ~CriticalSectionLock() |
85 | { |
86 | DeleteCriticalSection(&csMyCriticalSection); |
87 | } |
88 | void lock() |
89 | { |
90 | EnterCriticalSection(&csMyCriticalSection); |
91 | } |
92 | void unlock() |
93 | { |
94 | LeaveCriticalSection(&csMyCriticalSection); |
95 | } |
96 | private : |
97 | CRITICAL_SECTION csMyCriticalSection; |
98 | }; |
99 |
100 | template < typename POOLTYPE, typename LOCK> |
101 | class MTMemoryPool |
102 | { |
103 | public : |
104 | //allocate an element from the freelist. |
105 | void * alloc( size_t size) |
106 | { |
107 | void * mem; |
108 | theLock.lock(); |
109 | mem = stPool.alloc(size); |
110 | theLock.unlock(); |
111 | return mem; |
112 | } |
113 |
114 | //return an element to the freelist |
115 | void free ( void * someElement) |
116 | { |
117 | theLock.lock(); |
118 | stPool. free (someElement); |
119 | theLock.unlock(); |
120 | } |
121 |
122 | private : |
123 | POOLTYPE stPool; //Single-threaded pool. |
124 | LOCK theLock; |
125 | }; |
126 |
127 | class Rational |
128 | { |
129 | public : |
130 | Rational( int a = 0, int b = 1) : n(a), d(b) {} |
131 | void * operator new ( size_t size) |
132 | { |
133 | return memPool->alloc(size); |
134 | } |
135 | void operator delete ( void * doomed, size_t size) |
136 | { |
137 | memPool-> free (doomed); |
138 | } |
139 | static void newMemPool() |
140 | { |
141 | memPool = new MTMemoryPool< MemoryPool<Rational>, CriticalSectionLock>; |
142 | } |
143 | static void deleteMemPool() |
144 | { |
145 | delete memPool; |
146 | } |
147 |
148 | private : |
149 | int n; |
150 | int d; |
151 | static MTMemoryPool< MemoryPool<Rational>, CriticalSectionLock>* memPool; |
152 | }; |
153 | MTMemoryPool< MemoryPool<Rational>, CriticalSectionLock>* Rational::memPool = NULL; |
154 |
155 | int main() |
156 | { |
157 | const int ARRAY_SIZE = 1000; |
158 | const int LOOP_TIMES = 5000; |
159 |
160 | Rational* array[ARRAY_SIZE]; |
161 | clock_t beg = clock (); |
162 | Rational::newMemPool(); |
163 | for ( int i = 0; i < LOOP_TIMES; ++i) |
164 | { |
165 | for ( int j = 0; j < ARRAY_SIZE; ++j) |
166 | { |
167 | array[j] = new Rational(j); |
168 | } |
169 | for ( int j = 0; j < ARRAY_SIZE; ++j) |
170 | { |
171 | delete array[j]; |
172 | } |
173 | } |
174 | clock_t end = clock (); |
175 | printf ( "use %f second(s).\n" , 1.0 * (end - beg) / CLOCKS_PER_SEC); |
176 | system ( "pause" ); |
177 | Rational::deleteMemPool(); |
178 |
179 | return 0; |
180 | } |
运行结果,
两个版本的代码只在线程同步锁的实现上有差别,但是速度却相差了30多倍。