怎么实现释放和分配时间复杂度都为常数(O(1))的内存池
以前在一个高性能的场合,需要十分频繁的使用临时缓存。由于所需的缓存大小我事先知道,所以我索性开了一块全局缓存,不要骂我土,它背后可以一个崇高的设计哲学:奥坎姆的剃刀。
不过,后边我越看越恶心,越看越想吐,于是就想实现一个分配和释放都为常数时间的MemPool. 虽然内存池是一个老得不老再老的话题,但是我并没有找到一个能达到我要求的设计。
虽然我对C++的任何细节都毫不知情,不过还是免为其难的写了一个有诸多缺陷的XMemPool。
先说下大致思路:
1 POOL将分配划分成两种情况,小缓存分配,大缓存分配。小于4K的我把它当成小缓存。小缓存块BlockSize按8字节步长增长,大块默认按4K步长增长。每种Size的块用两个链表进行管理(free & used)。
2 分配。要是size超过了最大能分配的大小或者池容量超过限制就直接调用系统分配。计算相应块的index(如果是调用系统分配的index会置为-1), 把free list第一个结点移到used list的第一个结点
3 释放。 如果块的index为-1直接delete。将要释放的块直接移动到free list的第一个结点。
这样的话缺点也是相当明显的:
1 用户必要从Block获取缓存
2 用户非常有必要对缓存大小事先按需求进行合理估计。
1
#ifndef _X_MEM_POOL_H_
2
#define
_X_MEM_POOL_H_
3![]()
4
//
! (1)alloc:O(1)
5
//
! (2)free :O(1)
6
//
! (3)not thread safe
7
8
class
XMemPool;
9
class
XMemBlock
10![]()
{
11
public:
12
XMemBlock( const int& index, const size_t& size ):
13
_index( index ),
14
_next( 0 ),
15
_pre( 0 ),
16
_size( size )
17![]()
{
18
_data = new char[size];
19
}
20
~XMemBlock()
21![]()
{
22
delete []_data;
23
}
24![]()
25
public:
26
friend class XMemPool;
27
template<class T>
28![]()
T* getMem() const
{ return (T*)_data; }
29![]()
30
private:
31
const int _index;
32
const size_t _size;
33
char *_data;
34
XMemBlock *_next;
35
XMemBlock *_pre;
36
}
;
37![]()
38
const
size_t S_MEM_THRE
=
4096
;
//
4K
39
const
size_t S_POOL_STEP
=
8
;
40
const
size_t LARGE_POOL_STEP
=
4096
;
//
4K
41
const
size_t SMAX_SUB_POOL_CAP
=
128
;
42
const
size_t LMAX_SUB_POOL_CAP
=
64
;
43![]()
44
class
XMemPool
45![]()
{
46
public:
47![]()
/**//*
48
maxBlockSize 此内存池能分配的最大的内存
49
maxSubPoolCapability 每个子内存池的最大容量
50
poolXep 相邻子内存池之间的BlockSize的差距,即每个子内存池大小为poolXep的整数倍
51![]()
52
这里小块的容量,大小写死了的,只有大块的可以配置
53
*/
54
XMemPool( const size_t& maxBlockSize, const size_t& lmaxSubPoolCapability = LMAX_SUB_POOL_CAP,\
55
const size_t& lpoolXep = LARGE_POOL_STEP );
56
~XMemPool();
57
public:
58
XMemBlock *alloc( size_t size );
59
void free( XMemBlock* block );
60
void destroy();
61![]()
size_t getTotalMemSize() const
{ return _totalMemSize; };
62![]()
63
private:
64
const size_t _maxBlockSize; //最大能分配的内存块
65
const size_t _lmaxSubPoolCapability; //每种块的最大小数(大块)
66
static const size_t\
67
_smaxSubPoolCapability = SMAX_SUB_POOL_CAP;//每种块的最大小数(小块)
68
const size_t _lpoolXep; //大块的增长步长
69
static const size_t\
70
_spoolXep = S_POOL_STEP; //小块的步长
71
XMemBlock **_ssubPool[2];//0:free 1:used 小块的链表数组
72
XMemBlock **_lsubPool[2];//大块的链表数组
73
size_t _ssubPoolNum;//小块的种类个数
74
size_t _lsubPoolNum;//大块的种类个数
75
size_t *_lsubPoolSize;//每种size大块的个数
76
size_t *_ssubPoolSize;//每种size小块的个数
77
size_t _totalMemSize;//内存池总容量
78
}
;
79![]()
80
#endif
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
1
XMemPool::XMemPool(
const
size_t
&
maxBlockSize,
const
size_t
&
lmaxSubPoolCapability,
const
size_t
&
lpoolXep ):\
2
_maxBlockSize( maxBlockSize ),
3
_lmaxSubPoolCapability( lmaxSubPoolCapability ),
4
_lpoolXep( lpoolXep )
5![]()
{
6
_ssubPoolNum = ( S_MEM_THRE + _spoolXep - 1 ) / _spoolXep;
7
_lsubPoolNum = ( _maxBlockSize + _lpoolXep - 1 ) / _lpoolXep;
8
_totalMemSize = 0;
9
_ssubPoolSize = new size_t[_ssubPoolNum];
10
_lsubPoolSize = new size_t[_lsubPoolNum];
11
_ssubPool[0] = new XMemBlock*[_ssubPoolNum];
12
_ssubPool[1] = new XMemBlock*[_ssubPoolNum];
13
for( int i = 0; i < _ssubPoolNum; i++ )
14![]()
{
15
_ssubPool[0][i] = 0;
16
_ssubPool[1][i] = 0;
17
_ssubPoolSize[i] = 0;
18
}
19
_lsubPool[0] = new XMemBlock*[_lsubPoolNum];
20
_lsubPool[1] = new XMemBlock*[_lsubPoolNum];
21
for( int i = 0; i < _lsubPoolNum; i++ )
22![]()
{
23
_lsubPool[0][i] = 0;
24
_lsubPool[1][i] = 0;
25
_lsubPoolSize[i] = 0;
26
}
27
}
28![]()
29
XMemBlock
*
XMemPool::alloc( size_t size )
//
byte unit
30![]()
{
31
if( size <= S_MEM_THRE )
32![]()
{
33
size_t idx = ( size + _spoolXep - 1 ) / _spoolXep - 1;
34
XMemBlock *block = 0;
35
if( _ssubPool[0][idx] )
36![]()
{
37
block = _ssubPool[0][idx];
38
_ssubPool[0][idx] = block->_next;
39
if( _ssubPool[1][idx] )
40
_ssubPool[1][idx]->_pre = block;
41
block->_next = _ssubPool[1][idx];
42
_ssubPool[1][idx] = block;
43
_ssubPool[1][idx]->_pre = 0;
44
return block;
45
}
46
else if( _ssubPoolSize[idx] < _smaxSubPoolCapability )
47![]()
{
48
size_t msize = (idx + 1)*_spoolXep;
49
_totalMemSize += msize;
50
block = new XMemBlock( idx, msize );
51
_ssubPoolSize[idx]++;
52
if( _ssubPool[1][idx] )
53
_ssubPool[1][idx]->_pre = block;
54
block->_next = _ssubPool[1][idx];
55
_ssubPool[1][idx] = block;
56
return block;
57
}
58
}
59
else if( size <= _maxBlockSize )
60![]()
{
61
size_t idx = ( size + _lpoolXep - 1 ) / _lpoolXep - 1;
62
XMemBlock *block = 0;
63
if( _lsubPool[0][idx] )
64![]()
{
65
block = _lsubPool[0][idx];
66
_lsubPool[0][idx] = block->_next;
67
if( _lsubPool[1][idx] )
68
_lsubPool[1][idx]->_pre = block;
69
block->_next = _lsubPool[1][idx];
70
_lsubPool[1][idx] = block;
71
_lsubPool[1][idx]->_pre = 0;
72
return block;
73
}
74
else if( _lsubPoolSize[idx] < _lmaxSubPoolCapability )
75![]()
{
76
size_t msize = (idx + 1)*_lpoolXep;
77
_totalMemSize += msize;
78
block = new XMemBlock( idx, msize );
79
_lsubPoolSize[idx]++;
80
if( _lsubPool[1][idx] )
81
_lsubPool[1][idx]->_pre = block;
82
block->_next = _lsubPool[1][idx];
83
_lsubPool[1][idx] = block;
84
return block;
85
}
86
}
87![]()
88
return (new XMemBlock( -1, size ));
89
}
90![]()
91
void
XMemPool::free( XMemBlock
*
block )
92![]()
{
93
if( block->_index < 0 )
94![]()
{
95
delete block;
96
return;
97
}
98
if( block->_size <= S_MEM_THRE )
99![]()
{
100
if( block->_next )
101
block->_next->_pre = block->_pre;
102
if( block->_pre )
103
block->_pre->_next = block->_next;
104
else if( !block->_next && !block->_pre )
105
_ssubPool[1][block->_index] = 0;
106
block->_next = _ssubPool[0][block->_index];
107
if( _ssubPool[0][block->_index] )
108
_ssubPool[0][block->_index]->_pre = block;
109
_ssubPool[0][block->_index] = block;
110
}
111
else
112![]()
{
113
if( block->_next )
114
block->_next->_pre = block->_pre;
115
if( block->_pre )
116
block->_pre->_next = block->_next;
117
else if( !block->_next && !block->_pre )
118
_lsubPool[1][block->_index] = 0;
119
block->_next = _lsubPool[0][block->_index];
120
if( _lsubPool[0][block->_index] )
121
_lsubPool[0][block->_index]->_pre = block;
122
_lsubPool[0][block->_index] = block;
123
}
124
}
125![]()
126
void
XMemPool::destroy()
127![]()
{
128
for( int i = 0; i < _ssubPoolNum; i++ )
129![]()
{
130
XMemBlock *block = _ssubPool[0][i], *tmp;
131
while( block )
132![]()
{
133
tmp = block->_next;
134
delete block;
135
block = tmp;
136
}
137
_ssubPool[0][i] = 0;
138![]()
139
block = _ssubPool[1][i];
140
while( block )
141![]()
{
142
tmp = block->_next;
143
delete block;
144
block = tmp;
145
}
146
_ssubPool[1][i] = 0;
147
_ssubPoolSize[i] = 0;
148
}
149
for( int i = 0; i < _lsubPoolNum; i++ )
150![]()
{
151
XMemBlock *block = _lsubPool[0][i], *tmp;
152
while( block )
153![]()
{
154
tmp = block->_next;
155
delete block;
156
block = tmp;
157
}
158
_lsubPool[0][i] = 0;
159![]()
160
block = _lsubPool[1][i];
161
while( block )
162![]()
{
163
tmp = block->_next;
164
delete block;
165
block = tmp;
166
}
167
_lsubPool[1][i] = 0;
168
_lsubPoolSize[i] = 0;
169
}
170
}
171![]()
172
XMemPool::
~
XMemPool()
173![]()
{
174
destroy();
175
delete []_ssubPoolSize;
176
delete []_lsubPoolSize;
177
delete []_ssubPool[0];
178
delete []_ssubPool[1];
179
delete []_lsubPool[0];
180
delete []_lsubPool[1];
181
}
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
132

133
134
135
136
137
138
139
140
141

142
143
144
145
146
147
148
149
150

151
152
153

154
155
156
157
158
159
160
161
162

163
164
165
166
167
168
169
170
171
172
173

174
175
176
177
178
179
180
181
1
XMemPool samplePool(
4096
);
2
XMemBlock
*
sampleBlock
=
samplePool.alloc(
sizeof
(
int
)
*
100
);
3
int
*
sampleData
=
sampleBlock
->
getMem
<
int
>
();
4
samplePool.free(sampleBlock);
2
3
4
很多细节还没考虑到,谁能为我推荐一个好的设计方案?双O(1)的