Skip to content

Commit 38caa52

Browse files
committed
fix the bugs, The tcmalloc project is basically completed, but there are still many areas that need to be modified and optimized.
1 parent 32c3d85 commit 38caa52

File tree

8 files changed

+91
-22
lines changed

8 files changed

+91
-22
lines changed

README.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
- [thread\_cache内存释放](#thread_cache内存释放)
2121
- [central\_cache内存释放](#central_cache内存释放)
2222
- [page\_cache内存释放](#page_cache内存释放)
23+
- [大于256k的情况](#大于256k的情况)
24+
- [处理代码中`new`的问题](#处理代码中new的问题)
2325

2426
***
2527

@@ -1180,4 +1182,18 @@ void page_cache::release_span_to_page(span* s) {
11801182
为什么这里不用循环存储呢?
11811183

11821184
因为这里的pc的内存只是被span挂起来啊,不会被切啊,所以知道地址就了啊!
1183-
给cc的那些,会被切开变成很多固定大小的内存块啊!所以这里不用循环存。
1185+
给cc的那些,会被切开变成很多固定大小的内存块啊!所以这里不用循环存。
1186+
1187+
## 大于256k的情况
1188+
1189+
1. <=256kb -> 按照前面三层缓存的情况进行操作
1190+
2. \>256kb的情况
1191+
a. 128\*8k > size > 32\*8k这个情况: 还可以找pagecache
1192+
b. 否则直接找系统
1193+
1194+
然后这一部分就是有多处要改,不过都很简单很容易找到,大家可以直接看代码。处理完之后,测试一下申请大内存就行。
1195+
1196+
1197+
## 处理代码中`new`的问题
1198+
1199+
代码中有些地方用了`new span`。这个就很不对。我们弄这个tcmalloc是用来替代malloc的,既然是替代,那我们的代码里面怎么能有`new``new`也是调用`malloc`的,所以我们要改一下。

debug

-174 KB
Binary file not shown.

include/common.hpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,17 @@ inline static void* system_alloc(size_t kpage) {
4848
return ptr;
4949
}
5050

51+
inline static void system_free(void* ptr, size_t size = 0) {
52+
/**
53+
* linux的munmap需要给大小
54+
*/
55+
#if defined(_WIN32) || defined(_WIN64)
56+
VirtualFree(ptr, 0, MEM_RELEASE);
57+
#elif defined(__linux__) // ...
58+
munmap(ptr, size);
59+
#endif
60+
}
61+
5162
// 管理切分好的小对象的自由链表
5263
class free_list {
5364
private:
@@ -116,8 +127,8 @@ class size_class {
116127
else if (size <= 256 * 1024)
117128
return __round_up(size, 8 * 1024);
118129
else {
119-
assert(false);
120-
return -1;
130+
// 大内存
131+
return __round_up(size, 1 << PAGE_SHIFT);
121132
}
122133
}
123134
// 计算映射的哪一个自由链表桶

include/page_cache.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class page_cache {
2020
static page_cache* get_instance() { return &__s_inst; }
2121
span* map_obj_to_span(void* obj);
2222
// 释放空闲的span回到pc,并合并相邻的span
23-
void release_span_to_page(span* s);
23+
void release_span_to_page(span* s, size_t size = 0);
2424
public:
2525
// 获取一个K页的span
2626
span* new_span(size_t k);

include/tcmalloc.hpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,20 @@
44

55
#include "common.hpp"
66
#include "log.hpp"
7+
#include "page_cache.hpp"
78
#include "thread_cache.hpp"
89

910
static void* tcmalloc(size_t size) {
11+
if (size > MAX_BYTES) {
12+
// 处理申请大内存的情况
13+
size_t align_size = size_class::round_up(size);
14+
size_t k_page = align_size >> PAGE_SHIFT;
15+
page_cache::get_instance()->__page_mtx.lock();
16+
span* cur_span = page_cache::get_instance()->new_span(k_page); // 直接找pc
17+
page_cache::get_instance()->__page_mtx.unlock();
18+
void* ptr = (void*)(cur_span->__page_id << PAGE_SHIFT); // span转化成地址
19+
return ptr;
20+
}
1021
if (p_tls_thread_cache == nullptr)
1122
// 相当于单例
1223
p_tls_thread_cache = new thread_cache;
@@ -17,6 +28,13 @@ static void* tcmalloc(size_t size) {
1728
}
1829

1930
static void tcfree(void* ptr, size_t size) {
31+
if (size > MAX_BYTES) {
32+
span* s = page_cache::get_instance()->map_obj_to_span(ptr); // 找到这个span
33+
page_cache::get_instance()->__page_mtx.lock();
34+
page_cache::get_instance()->release_span_to_page(s, size); // 直接调用pc的
35+
page_cache::get_instance()->__page_mtx.unlock();
36+
return;
37+
}
2038
assert(p_tls_thread_cache);
2139
p_tls_thread_cache->deallocate(ptr, size);
2240
}

out

-170 KB
Binary file not shown.

src/page_cache.cc

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,39 @@ page_cache page_cache::__s_inst;
66

77
// cc向pc获取k页的span
88
span* page_cache::new_span(size_t k) {
9-
assert(k > 0 && k < PAGES_NUM);
9+
assert(k > 0);
10+
// 处理大内存情况
11+
if (k > PAGES_NUM - 1) {
12+
void* ptr = system_alloc(k);
13+
span* cur_span = new span;
14+
cur_span->__page_id = (PAGE_ID)ptr >> PAGE_SHIFT;
15+
cur_span->__n = k;
16+
// map记录一下
17+
__id_span_map[cur_span->__page_id] = cur_span;
18+
return cur_span;
19+
}
1020
// 先检查第k个桶是否有span
1121
// #ifdef PROJECT_DEBUG
1222
// LOG(DEBUG) << "before ***" << std::endl;
1323
// #endif
14-
if (!__span_lists[k].empty())
15-
return __span_lists[k].pop_front(); // ? __span_lists->pop_front();
24+
if (!__span_lists[k].empty()) {
25+
span* s = __span_lists[k].pop_front(); // ? __span_lists->pop_front();
26+
// 建立id和span的映射,方便central cache回收小块内存时,查找对应的span
27+
for (PAGE_ID i = 0; i < s->__n; ++i) {
28+
__id_span_map[s->__page_id + i] = s;
29+
}
30+
return s;
31+
}
1632
// #ifdef PROJECT_DEBUG
1733
// LOG(DEBUG) << "after ***" << std::endl;
1834
// #endif
1935
// 第k个桶是空的->去检查后面的桶里面有无span,如果有,可以把它进行切分
2036
for (size_t i = k + 1; i < PAGES_NUM; i++) {
2137
if (!__span_lists[i].empty()) {
22-
// 可以开始切了
23-
// 假设这个页是n页的,需要的是k页的
24-
// 1. 从__span_lists中拿下来 2. 切开 3. 一个返回给cc 4. 另一个挂到 n-k 号桶里面去
25-
#ifdef PROJECT_DEBUG
26-
LOG(DEBUG) << "before ***" << std::endl;
27-
#endif
38+
// 可以开始切了
39+
// 假设这个页是n页的,需要的是k页的
40+
// 1. 从__span_lists中拿下来 2. 切开 3. 一个返回给cc 4. 另一个挂到 n-k 号桶里面去
2841
span* n_span = __span_lists[i].pop_front();
29-
#ifdef PROJECT_DEBUG
30-
LOG(DEBUG) << "after ***" << std::endl;
31-
#endif
3242
span* k_span = new span;
3343
// 在n_span头部切除k页下来
3444
k_span->__page_id = n_span->__page_id; // <1>
@@ -82,7 +92,16 @@ span* page_cache::map_obj_to_span(void* obj) {
8292
return nullptr;
8393
}
8494

85-
void page_cache::release_span_to_page(span* s) {
95+
void page_cache::release_span_to_page(span* s, size_t size) {
96+
// 第二个参数是为了linux下一次释放大内存,需要大小
97+
// std::cout << s->__n << std::endl; // 33
98+
if (s->__n >= PAGES_NUM) {
99+
// 处理大内存
100+
void* ptr = (void*)(s->__page_id << PAGE_SHIFT);
101+
system_free(s, size);
102+
delete s;
103+
return;
104+
}
86105
// 对span前后对页尝试进行合并,缓解内存碎片问题
87106
while (true) {
88107
PAGE_ID prev_id = s->__page_id - 1; // 前一块span的id一定是当前span的id-1
@@ -103,7 +122,7 @@ void page_cache::release_span_to_page(span* s) {
103122
delete prev_span; // 删掉这个span
104123
} // 向前合并的逻辑 while end;
105124
while (true) {
106-
PAGE_ID next_id = s->__page_id + s->__n - 1; // 注意这里的页号是+n-1了
125+
PAGE_ID next_id = s->__page_id + s->__n; // 注意这里的页号是+n了
107126
auto ret = __id_span_map.find(next_id);
108127
if (ret == __id_span_map.end()) // 后面的页号没有了
109128
break;

unit_test.cc

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11

22

33
#include "./include/tcmalloc.hpp"
4+
#include <functional>
45
#include <iostream>
56
#include <map>
67
#include <random>
78
#include <thread>
8-
#include <functional>
99

1010
void alloc1() {
1111
for (size_t i = 0; i < 5; i++) {
@@ -48,7 +48,7 @@ void test_dealloc(int alloc_times = 10) {
4848
// 创建随机数生成器
4949
std::random_device rd;
5050
std::mt19937 gen(rd()); // 以随机设备作为种子
51-
std::uniform_int_distribution<> distrib(1, 127 * 1024); // 设置随机数分布范围1-127
51+
std::uniform_int_distribution<> distrib(1, 127 * 100); // 设置随机数分布范围1-127
5252
// 生成并输出随机数
5353
std::map<void*, size_t> s;
5454
for (int i = 0; i < alloc_times; i++) {
@@ -61,14 +61,19 @@ void test_dealloc(int alloc_times = 10) {
6161
}
6262

6363
void test_multi_thread() {
64-
std::thread t1(std::bind(test_dealloc, 200));
64+
std::thread t1(std::bind(test_dealloc, 1000));
6565
// std::thread t2(std::bind(test_dealloc, 20));
6666
t1.join();
6767
// t2.join();
6868
std::cout << "run successful" << std::endl;
6969
}
7070

71+
void big_alloc() {
72+
void* ptr = tcmalloc(8 * 127 * 1024);
73+
tcfree(ptr, 8 * 127 * 1024);
74+
}
75+
7176
int main() {
72-
test_multi_thread();
77+
big_alloc();
7378
return 0;
7479
}

0 commit comments

Comments
 (0)