├── LICENSE ├── README-cn.md ├── README.md ├── assets ├── 0.png ├── 1.png ├── 2.png ├── 3.png ├── 4.png ├── 5.png └── Google.png ├── bench_mark.cc ├── include ├── central_cache.hpp ├── common.hpp ├── log.hpp ├── object_pool.hpp ├── page_cache.hpp ├── page_map.hpp ├── tcmalloc.hpp └── thread_cache.hpp ├── makefile ├── src ├── central_cache.cc ├── page_cache.cc └── thread_cache.cc ├── test └── test1.log ├── unit_test.cc └── work.md /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Fengcheng Yu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README-cn.md: -------------------------------------------------------------------------------- 1 | ![](./assets/Google.png) 2 | 3 | 4 | # Google-tcmalloc-simulation-implementation 5 | 6 | 谷歌开源项目tcmalloc高并发内存池学习和模拟实现 7 | 8 | ## 🔧bugs to fix 9 | 10 | 1. 在ubuntu_arm64环境下,如果调用多线程,出现段错误(原因未知,待解决) **[issues#7](https://github.com/ffengc/Google-tcmalloc-simulation-implementation/issues/7)** 11 | 2. 在ubuntu_arm64环境下,radix tree需要用第三棵,前两棵用不了,需要解决。**[issues#7](https://github.com/ffengc/Google-tcmalloc-simulation-implementation/issues/7)** 12 | 3. 在window32位环境下,可以偶尔成功运行,出现偶发段错误,原因未知,待解决。**[issues#8](https://github.com/ffengc/Google-tcmalloc-simulation-implementation/issues/8)** 13 | 14 | 经过radixtree优化后,模拟实现的tcmalloc效率高于malloc。(win32下测试,会出现偶发段错误) 15 | 16 | ![](./assets/5.png) 17 | 18 | ## 💻项目基本信息 19 | 20 | 当前项目是实现一个高并发的内存池,他的原型是google的一个开源项目tcmalloc,tcmalloc全称 Thread-Caching Malloc,即线程缓存的malloc,实现了高效的多线程内存管理,用于替代系统的内存分配相关的函数(malloc、free)。 21 | 这个项目是把tcmalloc最核心的框架简化后拿出来,模拟实现出一个自己的高并发内存池,目的就是学习tcamlloc的精华,这种方式有点类似我们之前学习STL容器的方式。但是相比STL容器部分,tcmalloc的代码量和复杂度上升了很多。 22 | 23 | **另一方面tcmalloc是全球大厂google开源的,可以认为当时顶尖的C++高手写出来的,他的知名度也是非常高的,不少公司都在用它,Go语言直接用它做了自己内存分配器。所以很多程序员是熟悉这个项目的。** 24 | 25 | 现代很多的开发环境都是多核多线程,在申请内存的场景下,必然存在激烈的锁竞争问题。malloc本身其实已经很优秀,那么我们项目的原型tcmalloc就是在多线程高并发的场景下更胜一筹,所以这次我们实现的内存池需要考虑以下几方面的问题。 26 | 1. 性能问题。 27 | 2. 多线程环境下,锁竞争问题。 28 | 3. 内存碎片问题。 29 | 30 | **concurrent memory pool主要由以下3个部分构成:** 31 | 1. **thread cache:** 线程缓存是每个线程独有的,用于小于256KB的内存的分配,线程从这里申请内存不需要加锁,每个线程独享一个cache,这也就是这个并发线程池高效的地方。有几个线程,就会创建几个threadCache,每个线程都独享一个Cache。threadCache如果没有内存了,就去找centralCache 32 | 2. **central cache:** 中心缓存是所有线程所共享,thread cache是按需从central cache中获取的对象。central cache合适的时机回收thread cache中的对象,避免一个线程占用了太多的内存,而 其他线程的内存吃紧,达到内存分配在多个线程中更均衡的按需调度的目的。central cache是存在竞争的,**所以从这里取内存对象是需要加锁,首先这里用的是桶锁,其次只有threadCache的没有内存对象时才会找central cache,所以这里竞争不会很激烈。如果两个threadCache去不同的桶找内存,不用加锁!** 33 | 3. **page cache:** 页缓存是在central cache缓存上面的一层缓存,存储的内存是以页为单位存储及分配的,centralCache没有内存对象时,从pageCache分配出一定数量的page,并切割成定长大小的小块内存,分配给centralCache。当一个span的几个跨度页的对象都回收以后,pageCache 会回收centralCache满足条件的span对象,并且合并相邻的页,组成更大的页,缓解内存碎片的问题。 34 | 35 | 项目结构图如下所示。 36 | 37 | ![](./assets/0.png) 38 | 39 | ## 📚项目详细实现记录 40 | 41 | - **[work.md](./work.md)** -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](./assets/Google.png) 2 | 3 | # Google-tcmalloc-simulation-implementation 4 | 5 | Google open source project tcmalloc high concurrency memory pool learning and simulation implementation. 6 | 7 | - **[Google TcMalloc repo link](https://github.com/google/tcmalloc)** 8 | 9 | ## 🔧bugs to fix 10 | 11 | 1. In the ubuntu_arm64 environment, if multithreading is called, a segmentation fault occurs (the reason is unknown and needs to be resolved). **[issues#7](https://github.com/ffengc/Google-tcmalloc-simulation-implementation/issues/7)** 12 | 2. In the ubuntu_arm64 environment, the third radix tree needs to be used. The first two cannot be used and need to be resolved. **[issues#7](https://github.com/ffengc/Google-tcmalloc-simulation-implementation/issues/7)** 13 | 3. In the Window 32-bit environment, it can occasionally run successfully, but occasional segmentation errors occur. The reason is unknown and needs to be resolved. **[issues#8](https://github.com/ffengc/Google-tcmalloc-simulation-implementation/issues/8)** 14 | 15 | After radixtree optimization, the efficiency of the simulated tcmalloc is higher than that of malloc. (Tested under win32, occasional segmentation errors may occur) 16 | 17 | ![](./assets/5.png) 18 | 19 | ## 💻Basic information of the project 20 | 21 | The current project is to implement a high-concurrency memory pool. Its prototype is an open source project of Google called tcmalloc. The full name of tcmalloc is Thread-Caching Malloc, which is a thread-cached malloc. It implements efficient multi-threaded memory management and is used to replace the system's memory allocation related functions (malloc, free). 22 | This project simplifies the core framework of tcmalloc and simulates a high-concurrency memory pool of its own. The purpose is to learn the essence of tcmalloc. This method is somewhat similar to the way we learned STL containers before. However, compared with the STL container part, the amount of code and complexity of tcmalloc have increased a lot. 23 | 24 | **On the other hand, tcmalloc is open sourced by Google, a global giant. It can be considered that it was written by the top C++ experts at the time. It is also very well-known. Many companies are using it. Go language directly uses it as its own memory allocator. Therefore, many programmers are familiar with this project.** 25 | 26 | Many modern development environments are multi-core and multi-threaded. When applying for memory, there will inevitably be fierce lock competition. Malloc itself is actually very good, so the prototype of our project, tcmalloc, is even better in the scenario of multi-threaded high concurrency, so the memory pool we implemented this time needs to consider the following issues. 27 | 1. Performance issues. 28 | 2. Lock competition issues in a multi-threaded environment. 29 | 3. Memory fragmentation issues. 30 | 31 | **Concurrent memory pool is mainly composed of the following three parts:** 32 | 1. **thread cache:** The thread cache is unique to each thread and is used to allocate memory less than 256KB. Threads do not need to lock when applying for memory from here. Each thread has a cache, which is where the concurrent thread pool is efficient. If there are several threads, several threadCaches will be created, and each thread has a cache. If threadCache is out of memory, it will look for centralCache 33 | 2. **central cache:** The central cache is shared by all threads. The thread cache is an object obtained from the central cache on demand. The central cache recycles objects in the thread cache at the right time to avoid one thread occupying too much memory while other threads are short of memory, so as to achieve the purpose of more balanced on-demand scheduling of memory allocation among multiple threads. There is competition in the central cache, so it is necessary to lock when taking memory objects from here. First of all, the bucket lock is used here. Secondly, the central cache will only be found when there is no memory object in the threadCache, so the competition here will not be very fierce. If two threadCaches go to different buckets to find memory, no locking is required! ** 34 | 3. **page cache:** Page cache is a cache layer above the central cache. The memory is stored and allocated in pages. When there is no memory object in centralCache, a certain number of pages are allocated from pageCache and cut into small blocks of fixed length and allocated to centralCache. When the objects of several span pages of a span are recycled, pageCache will recycle the span objects that meet the conditions in centralCache and merge adjacent pages to form larger pages to alleviate the problem of memory fragmentation. 35 | 36 | The structure diagram is shown below. 37 | 38 | ![](./assets/0.png) 39 | 40 | ## 📚Detailed project implementation records 41 | 42 | - **[work.md](./work.md) (Chinese Only)** -------------------------------------------------------------------------------- /assets/0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ffengc/Google-tcmalloc-simulation-implementation/85053bfc20bd64dbd315e240c70b46051c5a5213/assets/0.png -------------------------------------------------------------------------------- /assets/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ffengc/Google-tcmalloc-simulation-implementation/85053bfc20bd64dbd315e240c70b46051c5a5213/assets/1.png -------------------------------------------------------------------------------- /assets/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ffengc/Google-tcmalloc-simulation-implementation/85053bfc20bd64dbd315e240c70b46051c5a5213/assets/2.png -------------------------------------------------------------------------------- /assets/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ffengc/Google-tcmalloc-simulation-implementation/85053bfc20bd64dbd315e240c70b46051c5a5213/assets/3.png -------------------------------------------------------------------------------- /assets/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ffengc/Google-tcmalloc-simulation-implementation/85053bfc20bd64dbd315e240c70b46051c5a5213/assets/4.png -------------------------------------------------------------------------------- /assets/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ffengc/Google-tcmalloc-simulation-implementation/85053bfc20bd64dbd315e240c70b46051c5a5213/assets/5.png -------------------------------------------------------------------------------- /assets/Google.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ffengc/Google-tcmalloc-simulation-implementation/85053bfc20bd64dbd315e240c70b46051c5a5213/assets/Google.png -------------------------------------------------------------------------------- /bench_mark.cc: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include "./include/tcmalloc.hpp" 4 | #include 5 | #include 6 | 7 | // ntimes 一轮申请和释放内存的次数 8 | // rounds 轮次 9 | void BenchmarkMalloc(size_t ntimes, size_t nworks, size_t rounds) { 10 | std::vector vthread(nworks); 11 | std::atomic malloc_costtime(0); 12 | std::atomic free_costtime(0); 13 | for (size_t k = 0; k < nworks; ++k) { 14 | vthread[k] = std::thread([&, k]() { 15 | std::vector v; 16 | v.reserve(ntimes); 17 | for (size_t j = 0; j < rounds; ++j) { 18 | size_t begin1 = clock(); 19 | for (size_t i = 0; i < ntimes; i++) { 20 | v.push_back(malloc(16)); 21 | // v.push_back(malloc((16 + i) % 8192 + 1)); 22 | } 23 | size_t end1 = clock(); 24 | size_t begin2 = clock(); 25 | for (size_t i = 0; i < ntimes; i++) { 26 | free(v[i]); 27 | } 28 | size_t end2 = clock(); 29 | v.clear(); 30 | malloc_costtime += (end1 - begin1); 31 | free_costtime += (end2 - begin2); 32 | } 33 | }); 34 | } 35 | for (auto& t : vthread) { 36 | t.join(); 37 | } 38 | std::cout << nworks << "threads run" << rounds << " times, each round malloc " << ntimes << " times, cost: " << malloc_costtime.load() << "ms\n"; 39 | std::cout << nworks << "threads run" << rounds << " times, each round free " << ntimes << " times, cost: " << free_costtime.load() << " ms\n"; 40 | std::cout << nworks << "threads run malloc and free " << nworks * rounds * ntimes << " time, total cost: " << malloc_costtime.load() + free_costtime.load() << " ms\n"; 41 | } 42 | 43 | // 单轮次申请释放次数 线程数 轮次 44 | void BenchmarkConcurrentMalloc(size_t ntimes, size_t nworks, size_t rounds) { 45 | std::vector vthread(nworks); 46 | std::atomic malloc_costtime(0); 47 | std::atomic free_costtime(0); 48 | for (size_t k = 0; k < nworks; ++k) { 49 | vthread[k] = std::thread([&]() { 50 | std::vector v; 51 | v.reserve(ntimes); 52 | for (size_t j = 0; j < rounds; ++j) { 53 | size_t begin1 = clock(); 54 | for (size_t i = 0; i < ntimes; i++) { 55 | v.push_back(tcmalloc(16)); 56 | // v.push_back(ConcurrentAlloc((16 + i) % 8192 + 1)); 57 | } 58 | size_t end1 = clock(); 59 | size_t begin2 = clock(); 60 | for (size_t i = 0; i < ntimes; i++) { 61 | tcfree(v[i]); 62 | } 63 | size_t end2 = clock(); 64 | v.clear(); 65 | malloc_costtime += (end1 - begin1); 66 | free_costtime += (end2 - begin2); 67 | } 68 | }); 69 | } 70 | for (auto& t : vthread) { 71 | t.join(); 72 | } 73 | std::cout << nworks << "threads run" << rounds << " times, each round malloc " << ntimes << " times, cost: " << malloc_costtime.load() << "ms\n"; 74 | std::cout << nworks << "threads run" << rounds << " times, each round free " << ntimes << " times, cost: " << free_costtime.load() << " ms\n"; 75 | std::cout << nworks << "threads run tcmalloc and tcfree " << nworks * rounds * ntimes << " time, total cost: " << malloc_costtime.load() + free_costtime.load() << " ms\n"; 76 | } 77 | 78 | int main() { 79 | size_t n = 1000; 80 | BenchmarkConcurrentMalloc(n, 4, 10); 81 | std::cout << std::endl 82 | << std::endl; 83 | BenchmarkMalloc(n, 4, 10); 84 | return 0; 85 | } -------------------------------------------------------------------------------- /include/central_cache.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __YUFC_CENTRAL_CACHE_HPP__ 3 | #define __YUFC_CENTRAL_CACHE_HPP__ 4 | 5 | #include "./common.hpp" 6 | 7 | class central_cache { 8 | private: 9 | span_list __span_lists[BUCKETS_NUM]; // 有多少个桶就多少个 10 | private: 11 | static central_cache __s_inst; 12 | central_cache() = default; // 构造函数私有 13 | central_cache(const central_cache&) = delete; // 不允许拷贝 14 | public: 15 | static central_cache* get_instance() { return &__s_inst; } 16 | // 将中心缓存获取一定数量的对象给threadCache 17 | size_t fetch_range_obj(void*& start, void*& end, size_t batch_num, size_t size); 18 | // 获取一个非空的span 19 | span* get_non_empty_span(span_list& list, size_t size); 20 | 21 | public: 22 | // 将一定数量的对象释放到span中 23 | void release_list_to_spans(void* start, size_t byte_size); 24 | 25 | public: 26 | }; 27 | 28 | #endif -------------------------------------------------------------------------------- /include/common.hpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef __YUFC_COMM_HPP__ 4 | #define __YUFC_COMM_HPP__ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #ifdef PROJECT_DEBUG 12 | #include "log.hpp" 13 | #include 14 | #endif 15 | 16 | #if defined(_WIN32) || defined(_WIN64) 17 | #include 18 | #elif defined(__aarch64__) // ... 19 | #include 20 | #else 21 | #include 22 | #endif 23 | 24 | static const size_t MAX_BYTES = 256 * 1024; // 256kb 25 | static const size_t BUCKETS_NUM = 208; // 一共208个桶 26 | static const size_t PAGES_NUM = 129; // pageCahche设置128个桶 27 | static const size_t PAGE_SHIFT = 13; 28 | 29 | #if defined(_WIN64) || defined(__x86_64__) || defined(__ppc64__) || defined(__aarch64__) 30 | typedef unsigned long long PAGE_ID; 31 | #define SYS_BYTES 64 32 | #else 33 | typedef size_t PAGE_ID; 34 | #define SYS_BYTES 32 35 | #endif 36 | 37 | inline static void* system_alloc(size_t kpage) { 38 | void* ptr = nullptr; 39 | #if defined(_WIN32) || defined(_WIN64) 40 | ptr = VirtualAlloc(0, kpage << 13, MEM_COMMIT | MEM_RESERVE, 41 | PAGE_READWRITE); 42 | #elif defined(__aarch64__) // ... 43 | ptr = mmap(NULL, kpage << 13, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 44 | #else 45 | std::cerr << "unknown system" << std::endl; 46 | throw std::bad_alloc(); 47 | #endif 48 | if (ptr == nullptr) 49 | throw std::bad_alloc(); 50 | return ptr; 51 | } 52 | 53 | inline static void system_free(void* ptr, size_t size = 0) { 54 | /** 55 | * linux的munmap需要给大小 56 | */ 57 | #if defined(_WIN32) || defined(_WIN64) 58 | VirtualFree(ptr, 0, MEM_RELEASE); 59 | #elif defined(__linux__) // ... 60 | munmap(ptr, size); 61 | #endif 62 | } 63 | 64 | // 管理切分好的小对象的自由链表 65 | class free_list { 66 | private: 67 | void* __free_list_ptr = nullptr; 68 | size_t __max_size = 1; 69 | size_t __size = 0; 70 | 71 | public: 72 | void push(void* obj) { 73 | assert(obj); 74 | __next_obj(obj) = __free_list_ptr; 75 | __free_list_ptr = obj; 76 | ++__size; 77 | } 78 | void push(void* start, void* end, size_t n) { 79 | __next_obj(end) = __free_list_ptr; 80 | __free_list_ptr = start; 81 | __size += n; 82 | } 83 | void* pop() { 84 | assert(__free_list_ptr); 85 | void* obj = __free_list_ptr; 86 | __free_list_ptr = __next_obj(obj); 87 | --__size; 88 | return obj; 89 | } 90 | void pop(void*& start, void*& end, size_t n) { 91 | // 这里是输出参数了 92 | #ifdef PROJECT_DEBUG 93 | LOG(DEBUG) << "call here" << std::endl; 94 | #endif 95 | assert(n <= __size); 96 | start = __free_list_ptr; 97 | end = start; // debug 20240507 miss this 98 | for (size_t i = 0; i < n - 1; i++) 99 | end = free_list::__next_obj(end); 100 | __free_list_ptr = free_list::__next_obj(end); 101 | free_list::__next_obj(end) = nullptr; 102 | __size -= n; 103 | } 104 | bool empty() { return __free_list_ptr == nullptr; } 105 | size_t& max_size() { return __max_size; } 106 | size_t size() { return __size; } 107 | 108 | public: 109 | static void*& __next_obj(void* obj) { 110 | return *(void**)obj; 111 | } 112 | }; 113 | 114 | // 计算对象大小的对齐映射规则 115 | class size_class { 116 | public: 117 | static inline size_t __round_up(size_t bytes, size_t align_number) { 118 | return (((bytes) + align_number - 1) & ~(align_number - 1)); 119 | } 120 | static inline size_t round_up(size_t size) { 121 | if (size <= 128) 122 | return __round_up(size, 8); 123 | else if (size <= 1024) 124 | return __round_up(size, 16); 125 | else if (size <= 8 * 1024) 126 | return __round_up(size, 128); 127 | else if (size <= 64 * 1024) 128 | return __round_up(size, 1024); 129 | else if (size <= 256 * 1024) 130 | return __round_up(size, 8 * 1024); 131 | else { 132 | // 大内存 133 | return __round_up(size, 1 << PAGE_SHIFT); 134 | } 135 | } 136 | // 计算映射的哪一个自由链表桶 137 | static inline size_t __bucket_index(size_t bytes, size_t align_shift) { 138 | return ((bytes + (1 << align_shift) - 1) >> align_shift) - 1; 139 | /* 140 | 这个还是同一道理,bytes不是对齐数的倍数,那就是直接模就行了 141 | 如果是,那就特殊规则一下即可,比如 1~128字节,对齐数字是8 142 | 那就是 bytes / 8 + 1 就是几号桶了 143 | 如果 bytes % 8 == 0 表示没有余数,刚好就是那个桶,就不用+1 144 | 这个也很好理解 145 | 当然这里的 align_shift 也不是对齐数,这种写法给的是 2^n = 对齐数,给的是这个n 146 | */ 147 | } 148 | static inline size_t bucket_index(size_t bytes) { 149 | assert(bytes <= MAX_BYTES); 150 | // 每个区间有多少个链 151 | static int group_array[4] = { 16, 56, 56, 56 }; 152 | if (bytes <= 128) { 153 | return __bucket_index(bytes, 3); 154 | } else if (bytes <= 1024) { 155 | return __bucket_index(bytes - 128, 4) + group_array[0]; 156 | } else if (bytes <= 8 * 1024) { 157 | return __bucket_index(bytes - 1024, 7) + group_array[1] + group_array[0]; 158 | } else if (bytes <= 64 * 1024) { 159 | return __bucket_index(bytes - 8 * 1024, 10) + group_array[2] + group_array[1] 160 | + group_array[0]; 161 | } else if (bytes <= 256 * 1024) { 162 | return __bucket_index(bytes - 64 * 1024, 13) + group_array[3] + group_array[2] + group_array[1] + group_array[0]; 163 | } else { 164 | assert(false); 165 | } 166 | return -1; 167 | } 168 | // 一次threadCache从centralCache获取多少个内存 169 | static inline size_t num_move_size(size_t size) { 170 | if (size == 0) 171 | return 0; 172 | // [2, 512], 一次批量移动多少个对象的(慢启动)上限制 173 | // 小对象一次批量上限高 174 | // 大对象一次批量上限低 175 | int num = MAX_BYTES / size; 176 | if (num < 2) 177 | num = 2; 178 | if (num > 512) 179 | num = 512; 180 | return num; 181 | } 182 | // 计算一次向pc获取几个页 183 | static inline size_t num_move_page(size_t size) { 184 | size_t num = num_move_size(size); 185 | size_t npage = num * size; 186 | npage >>= PAGE_SHIFT; // 相当于 /= 8kb 187 | if (npage == 0) 188 | npage = 1; 189 | return npage; 190 | } 191 | }; 192 | 193 | // 管理大块内存 194 | class span { 195 | public: 196 | PAGE_ID __page_id; // 大块内存起始页的页号 197 | size_t __n = 0; // 页的数量 198 | // 双向链表结构 199 | span* __next = nullptr; 200 | span* __prev = nullptr; 201 | size_t __use_count = 0; // 切成段小块内存,被分配给threadCache的计数器 202 | void* __free_list = nullptr; // 切好的小块内存的自由链表 203 | bool __is_use = false; // 是否在被使用 204 | size_t __obj_size; // 切好的小对象的大小 205 | }; 206 | 207 | // 带头双向循环链表 208 | class span_list { 209 | private: 210 | span* __head = nullptr; 211 | 212 | public: 213 | std::mutex __bucket_mtx; 214 | 215 | public: 216 | span_list() { 217 | __head = new span; 218 | __head->__next = __head; 219 | __head->__prev = __head; 220 | } 221 | void insert(span* pos, span* new_span) { 222 | // 插入的是一个完好的span 223 | assert(pos); 224 | assert(new_span); 225 | span* prev = pos->__prev; 226 | prev->__next = new_span; 227 | new_span->__prev = prev; 228 | new_span->__next = pos; 229 | pos->__prev = new_span; 230 | } 231 | void erase(span* pos) { 232 | assert(pos); 233 | assert(pos != __head); 234 | span* prev = pos->__prev; 235 | span* next = pos->__next; 236 | prev->__next = next; 237 | next->__prev = prev; 238 | } 239 | void push_front(span* new_span) { 240 | insert(begin(), new_span); 241 | } 242 | span* pop_front() { 243 | span* front = __head->__next; 244 | erase(front); // erase只是解除连接,没有删掉front 245 | return front; 246 | } 247 | bool empty() { return __head->__next == __head; } 248 | 249 | public: 250 | // 遍历相关 251 | span* begin() { return __head->__next; } 252 | span* end() { return __head; } 253 | }; 254 | 255 | #endif -------------------------------------------------------------------------------- /include/log.hpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef __YUFC_LOG__ 4 | #define __YUFC_LOG__ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | enum STATUES // 日志等级 11 | { 12 | INFO, 13 | DEBUG, 14 | WARNING, 15 | ERROR, 16 | FATAL 17 | }; 18 | 19 | // 定义颜色代码 20 | #define RESET "\033[0m" 21 | #define RED "\033[1;31m" // 加粗红色 22 | #define GREEN "\033[1;32m" // 加粗绿色 23 | #define YELLOW "\033[1;33m" // 加粗黄色 24 | #define BLUE "\033[1;34m" // 加粗蓝色 25 | #define MAGENTA "\033[1;35m" // 加粗洋红 26 | 27 | // 根据日志等级获取相应颜色 28 | inline const char* GetColor(const std::string& level) { 29 | std::map m = { { "INFO", 0 }, { "DEBUG", 1 }, { "WARNING", 2 }, { "ERROR", 3 }, { "FATAL", 4 } }; 30 | switch (m[level]) { 31 | case INFO: 32 | return BLUE; 33 | case DEBUG: 34 | return GREEN; 35 | case WARNING: 36 | return YELLOW; 37 | case ERROR: 38 | return MAGENTA; 39 | case FATAL: 40 | return RED; 41 | default: 42 | return RESET; 43 | } 44 | } 45 | 46 | // LOG() << "message" 47 | inline std::ostream& Log(const std::string& level, const std::string& file_name, int line) { 48 | // 添加日志等级 49 | std::string levelTag = std::string("[") + level + "]"; 50 | std::string coloredLevelTag = std::string(GetColor(level)) + levelTag + RESET; 51 | std::string message = coloredLevelTag; 52 | // 添加报错文件名称 53 | message += "["; 54 | message += file_name; 55 | message += "]"; 56 | // 添加当前文件的行数 57 | message += "["; 58 | message += std::to_string(line); 59 | message += "]"; 60 | // cout 本质内部是包含缓冲区的 61 | std::cout << message << " "; // 不要endl进行刷新 62 | return std::cout; 63 | } 64 | 65 | // 这种就是开放式的日志 66 | #define LOG(level) Log(#level, __FILE__, __LINE__) 67 | 68 | #endif -------------------------------------------------------------------------------- /include/object_pool.hpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef __YUFC_OBJECT_POOL_HPP__ 4 | #define __YUFC_OBJECT_POOL_HPP__ 5 | 6 | #include 7 | #include 8 | #include "./common.hpp" 9 | 10 | #define __DEFAULT_KB__ 128 11 | 12 | 13 | 14 | template 15 | class object_pool { 16 | private: 17 | char* __memory = nullptr; // char 方便切 18 | size_t __remain_bytes = 0; // 大块内存在切的过程中剩余的字节数 19 | void* __free_list = nullptr; // 还回来的时候形成的自由链表 20 | public: 21 | T* new_() { 22 | T* obj = nullptr; 23 | // 不够空间 首选是把还回来的内存块对象进行再次利用 24 | if (__free_list) { 25 | // 头删 26 | void* next = *((void**)__free_list); 27 | obj = (T*)__free_list; 28 | __free_list = next; 29 | return obj; 30 | } 31 | if (__remain_bytes < sizeof(T)) { 32 | // 空间不够了,要重新开一个空间 33 | __remain_bytes = __DEFAULT_KB__ * 1024; 34 | __memory = (char*)malloc(__remain_bytes); 35 | if (__memory == nullptr) { 36 | throw std::bad_alloc(); 37 | } 38 | } 39 | obj = (T*)__memory; 40 | size_t obj_size = sizeof(T) < sizeof(void*) ? sizeof(void*) : sizeof(T); 41 | __memory += obj_size; 42 | __remain_bytes -= obj_size; 43 | new (obj) T; 44 | return obj; 45 | } 46 | void delete_(T* obj) { 47 | obj->~T(); 48 | *(void**)obj = __free_list; 49 | __free_list = obj; 50 | } 51 | }; 52 | 53 | #endif -------------------------------------------------------------------------------- /include/page_cache.hpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef __YUFC_PAGE_CACHE_HPP__ 4 | #define __YUFC_PAGE_CACHE_HPP__ 5 | 6 | #include "./common.hpp" 7 | #include "./object_pool.hpp" 8 | #include "./page_map.hpp" 9 | 10 | class page_cache { 11 | private: 12 | span_list __span_lists[PAGES_NUM]; 13 | static page_cache __s_inst; 14 | page_cache() = default; 15 | page_cache(const page_cache&) = delete; 16 | // std::unordered_map __id_span_map; 17 | TCMalloc_PageMap3 __id_span_map; 18 | object_pool __span_pool; 19 | 20 | public: 21 | std::mutex __page_mtx; 22 | 23 | public: 24 | static page_cache* get_instance() { return &__s_inst; } 25 | span* map_obj_to_span(void* obj); 26 | // 释放空闲的span回到pc,并合并相邻的span 27 | void release_span_to_page(span* s, size_t size = 0); 28 | 29 | public: 30 | // 获取一个K页的span 31 | span* new_span(size_t k); 32 | }; 33 | 34 | #endif -------------------------------------------------------------------------------- /include/page_map.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __PAGE_MAP_FROM_GOOGLE_TCMALLOC__ 3 | #define __PAGE_MAP_FROM_GOOGLE_TCMALLOC__ 4 | 5 | #include "common.hpp" 6 | #include "object_pool.hpp" 7 | #include 8 | 9 | #define ASSERT assert 10 | 11 | // Single-level array 12 | template 13 | class TCMalloc_PageMap1 { 14 | private: 15 | static const int LENGTH = 1 << BITS; 16 | void** array_; 17 | 18 | public: 19 | typedef uintptr_t Number; 20 | 21 | // explicit TCMalloc_PageMap1(void* (*allocator)(size_t)) { 22 | explicit TCMalloc_PageMap1() { 23 | // array_ = reinterpret_cast((*allocator)(sizeof(void*) << BITS)); 24 | size_t size = sizeof(void*) << BITS; 25 | size_t alignSize = size_class::__round_up(size, 1 << PAGE_SHIFT); 26 | array_ = (void**)system_alloc(alignSize >> PAGE_SHIFT); 27 | memset(array_, 0, sizeof(void*) << BITS); 28 | } 29 | 30 | // Return the current value for KEY. Returns NULL if not yet set, 31 | // or if k is out of range. 32 | void* get(Number k) const { 33 | if ((k >> BITS) > 0) { 34 | return NULL; 35 | } 36 | return array_[k]; 37 | } 38 | 39 | // REQUIRES "k" is in range "[0,2^BITS-1]". 40 | // REQUIRES "k" has been ensured before. 41 | // 42 | // Sets the value 'v' for key 'k'. 43 | void set(Number k, void* v) { 44 | array_[k] = v; 45 | } 46 | }; 47 | 48 | // Two-level radix tree 49 | template 50 | class TCMalloc_PageMap2 { 51 | private: 52 | // Put 32 entries in the root and (2^BITS)/32 entries in each leaf. 53 | static const int ROOT_BITS = 5; 54 | static const int ROOT_LENGTH = 1 << ROOT_BITS; 55 | 56 | static const int LEAF_BITS = BITS - ROOT_BITS; 57 | static const int LEAF_LENGTH = 1 << LEAF_BITS; 58 | 59 | // Leaf node 60 | struct Leaf { 61 | void* values[LEAF_LENGTH]; 62 | }; 63 | 64 | Leaf* root_[ROOT_LENGTH]; // Pointers to 32 child nodes 65 | void* (*allocator_)(size_t); // Memory allocator 66 | 67 | public: 68 | typedef uintptr_t Number; 69 | 70 | // explicit TCMalloc_PageMap2(void* (*allocator)(size_t)) { 71 | explicit TCMalloc_PageMap2() { 72 | // allocator_ = allocator; 73 | memset(root_, 0, sizeof(root_)); 74 | 75 | PreallocateMoreMemory(); 76 | } 77 | 78 | void* get(Number k) const { 79 | const Number i1 = k >> LEAF_BITS; 80 | const Number i2 = k & (LEAF_LENGTH - 1); 81 | if ((k >> BITS) > 0 || root_[i1] == NULL) { 82 | return NULL; 83 | } 84 | return root_[i1]->values[i2]; 85 | } 86 | 87 | void set(Number k, void* v) { 88 | const Number i1 = k >> LEAF_BITS; 89 | const Number i2 = k & (LEAF_LENGTH - 1); 90 | ASSERT(i1 < ROOT_LENGTH); 91 | root_[i1]->values[i2] = v; 92 | } 93 | 94 | bool Ensure(Number start, size_t n) { 95 | for (Number key = start; key <= start + n - 1;) { 96 | const Number i1 = key >> LEAF_BITS; 97 | 98 | // Check for overflow 99 | if (i1 >= ROOT_LENGTH) 100 | return false; 101 | 102 | // Make 2nd level node if necessary 103 | if (root_[i1] == NULL) { 104 | // Leaf* leaf = reinterpret_cast((*allocator_)(sizeof(Leaf))); 105 | // if (leaf == NULL) return false; 106 | static object_pool leafPool; 107 | Leaf* leaf = (Leaf*)leafPool.new_(); 108 | 109 | memset(leaf, 0, sizeof(*leaf)); 110 | root_[i1] = leaf; 111 | } 112 | 113 | // Advance key past whatever is covered by this leaf node 114 | key = ((key >> LEAF_BITS) + 1) << LEAF_BITS; 115 | } 116 | return true; 117 | } 118 | 119 | void PreallocateMoreMemory() { 120 | // Allocate enough to keep track of all possible pages 121 | Ensure(0, 1 << BITS); 122 | } 123 | }; 124 | 125 | // Three-level radix tree 126 | template 127 | class TCMalloc_PageMap3 { 128 | private: 129 | // How many bits should we consume at each interior level 130 | static const int INTERIOR_BITS = (BITS + 2) / 3; // Round-up 131 | static const int INTERIOR_LENGTH = 1 << INTERIOR_BITS; 132 | 133 | // How many bits should we consume at leaf level 134 | static const int LEAF_BITS = BITS - 2 * INTERIOR_BITS; 135 | static const int LEAF_LENGTH = 1 << LEAF_BITS; 136 | 137 | // Interior node 138 | struct Node { 139 | Node* ptrs[INTERIOR_LENGTH]; 140 | }; 141 | 142 | // Leaf node 143 | struct Leaf { 144 | void* values[LEAF_LENGTH]; 145 | }; 146 | 147 | Node* root_; // Root of radix tree 148 | void* (*allocator_)(size_t); // Memory allocator 149 | 150 | Node* NewNode() { 151 | // Node* result = reinterpret_cast((*allocator_)(sizeof(Node))); 152 | static object_pool node_pool; 153 | Node* result = (Node*) node_pool.new_(); 154 | if (result != NULL) { 155 | memset(result, 0, sizeof(*result)); 156 | } 157 | return result; 158 | } 159 | 160 | public: 161 | typedef uintptr_t Number; 162 | 163 | // explicit TCMalloc_PageMap3(void* (*allocator)(size_t)) { 164 | explicit TCMalloc_PageMap3() { 165 | // allocator_ = allocator; 166 | root_ = NewNode(); 167 | } 168 | 169 | void* get(Number k) const { 170 | const Number i1 = k >> (LEAF_BITS + INTERIOR_BITS); 171 | const Number i2 = (k >> LEAF_BITS) & (INTERIOR_LENGTH - 1); 172 | const Number i3 = k & (LEAF_LENGTH - 1); 173 | if ((k >> BITS) > 0 || root_->ptrs[i1] == NULL || root_->ptrs[i1]->ptrs[i2] == NULL) { 174 | return NULL; 175 | } 176 | return reinterpret_cast(root_->ptrs[i1]->ptrs[i2])->values[i3]; 177 | } 178 | 179 | void set(Number k, void* v) { 180 | ASSERT(k >> BITS == 0); 181 | const Number i1 = k >> (LEAF_BITS + INTERIOR_BITS); 182 | const Number i2 = (k >> LEAF_BITS) & (INTERIOR_LENGTH - 1); 183 | const Number i3 = k & (LEAF_LENGTH - 1); 184 | reinterpret_cast(root_->ptrs[i1]->ptrs[i2])->values[i3] = v; 185 | } 186 | 187 | bool Ensure(Number start, size_t n) { 188 | for (Number key = start; key <= start + n - 1;) { 189 | const Number i1 = key >> (LEAF_BITS + INTERIOR_BITS); 190 | const Number i2 = (key >> LEAF_BITS) & (INTERIOR_LENGTH - 1); 191 | 192 | // Check for overflow 193 | if (i1 >= INTERIOR_LENGTH || i2 >= INTERIOR_LENGTH) 194 | return false; 195 | 196 | // Make 2nd level node if necessary 197 | if (root_->ptrs[i1] == NULL) { 198 | Node* n = NewNode(); 199 | if (n == NULL) 200 | return false; 201 | root_->ptrs[i1] = n; 202 | } 203 | 204 | // Make leaf node if necessary 205 | if (root_->ptrs[i1]->ptrs[i2] == NULL) { 206 | Leaf* leaf = reinterpret_cast((*allocator_)(sizeof(Leaf))); 207 | if (leaf == NULL) 208 | return false; 209 | memset(leaf, 0, sizeof(*leaf)); 210 | root_->ptrs[i1]->ptrs[i2] = reinterpret_cast(leaf); 211 | } 212 | 213 | // Advance key past whatever is covered by this leaf node 214 | key = ((key >> LEAF_BITS) + 1) << LEAF_BITS; 215 | } 216 | return true; 217 | } 218 | 219 | void PreallocateMoreMemory() { 220 | } 221 | }; 222 | 223 | #endif -------------------------------------------------------------------------------- /include/tcmalloc.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __YUFC_TCMALLOC_HPP__ 3 | #define __YUFC_TCMALLOC_HPP__ 4 | 5 | #include "common.hpp" 6 | #include "log.hpp" 7 | #include "object_pool.hpp" 8 | #include "page_cache.hpp" 9 | #include "thread_cache.hpp" 10 | 11 | static void* tcmalloc(size_t size) { 12 | if (size > MAX_BYTES) { 13 | // 处理申请大内存的情况 14 | size_t align_size = size_class::round_up(size); 15 | size_t k_page = align_size >> PAGE_SHIFT; 16 | page_cache::get_instance()->__page_mtx.lock(); 17 | span* cur_span = page_cache::get_instance()->new_span(k_page); // 直接找pc 18 | cur_span->__obj_size = size; 19 | page_cache::get_instance()->__page_mtx.unlock(); 20 | void* ptr = (void*)(cur_span->__page_id << PAGE_SHIFT); // span转化成地址 21 | return ptr; 22 | } 23 | if (p_tls_thread_cache == nullptr) { 24 | // 相当于单例 25 | // p_tls_thread_cache = new thread_cache; 26 | static object_pool tc_pool; 27 | p_tls_thread_cache = tc_pool.new_(); 28 | } 29 | #ifdef PROJECT_DEBUG 30 | LOG(DEBUG) << "tcmalloc find tc from mem" << std::endl; 31 | #endif 32 | return p_tls_thread_cache->allocate(size); 33 | } 34 | 35 | static void tcfree(void* ptr) { 36 | span* s = page_cache::get_instance()->map_obj_to_span(ptr); // 找到这个span就能找到obj_size了 37 | size_t size = s->__obj_size; // 找到大小了 38 | if (size > MAX_BYTES) { 39 | span* s = page_cache::get_instance()->map_obj_to_span(ptr); // 找到这个span 40 | page_cache::get_instance()->__page_mtx.lock(); 41 | page_cache::get_instance()->release_span_to_page(s, size); // 直接调用pc的 42 | page_cache::get_instance()->__page_mtx.unlock(); 43 | return; 44 | } 45 | assert(p_tls_thread_cache); 46 | p_tls_thread_cache->deallocate(ptr, size); 47 | } 48 | 49 | #endif -------------------------------------------------------------------------------- /include/thread_cache.hpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef __YUFC_THREAD_CACHE_HPP__ 4 | #define __YUFC_THREAD_CACHE_HPP__ 5 | 6 | #include "./common.hpp" 7 | 8 | class thread_cache { 9 | private: 10 | free_list __free_lists[BUCKETS_NUM]; // 哈希表 11 | 12 | public: 13 | void* allocate(size_t size); 14 | void deallocate(void* ptr, size_t size); 15 | 16 | public: 17 | // 向centralCache获取内存 18 | void* fetch_from_central_cache(size_t index, size_t size); 19 | // 释放对象,链表过长的时候,回收内存到centralCache 20 | void list_too_long(free_list& list, size_t size); 21 | }; 22 | 23 | static __thread thread_cache* p_tls_thread_cache = nullptr; 24 | 25 | #endif -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | out: bench_mark.cc ./src/*.cc 2 | g++ -o $@ $^ -std=c++11 -lpthread -m32 3 | debug: bench_mark.cc ./src/*.cc 4 | g++ -o $@ $^ -std=c++11 -lpthread -DPROJECT_DEBUG -g -m32 5 | unit: unit_test.cc ./src/*.cc 6 | g++ -o $@ $^ -std=c++11 -lpthread -DPROJECT_DEBUG -g -m32 7 | .PHONY:clean 8 | clean: 9 | rm -f out debug 10 | 11 | # out: bench_mark.cc ./src/*.cc 12 | # arm-linux-gnueabihf-g++ -o $@ $^ -std=c++11 -lpthread 13 | # debug: bench_mark.cc ./src/*.cc 14 | # arm-linux-gnueabihf-g++ -o $@ $^ -std=c++11 -lpthread -DPROJECT_DEBUG -g 15 | # unit: unit_test.cc ./src/*.cc 16 | # arm-linux-gnueabihf-g++ -o $@ $^ -std=c++11 -lpthread -DPROJECT_DEBUG -g 17 | # .PHONY:clean 18 | # clean: 19 | # rm -f out debug unit 20 | -------------------------------------------------------------------------------- /src/central_cache.cc: -------------------------------------------------------------------------------- 1 | 2 | #include "../include/central_cache.hpp" 3 | #include "../include/log.hpp" 4 | #include "../include/page_cache.hpp" 5 | 6 | central_cache central_cache::__s_inst; 7 | 8 | size_t central_cache::fetch_range_obj(void*& start, void*& end, size_t batch_num, size_t size) { 9 | size_t index = size_class::bucket_index(size); // 算出在哪个桶找 10 | __span_lists[index].__bucket_mtx.lock(); // 加锁(可以考虑RAII) 11 | #ifdef PROJECT_DEBUG 12 | LOG(DEBUG) << "central_cache::fetch_range_obj() call central_cache::get_non_empty_span()" << std::endl; 13 | #endif 14 | span* cur_span = get_non_empty_span(__span_lists[index], size); // 找一个非空的span(有可能找不到) 15 | assert(cur_span); 16 | assert(cur_span->__free_list); // 这个非空的span一定下面挂着内存了,所以断言一下 17 | 18 | start = cur_span->__free_list; 19 | // 这里要画图理解一下 20 | end = start; 21 | // 开始指针遍历,从span中获取对象,如果不够,有多少拿多少 22 | size_t i = 0; 23 | size_t actual_n = 1; 24 | while (i < batch_num - 1 && free_list::__next_obj(end) != nullptr) { 25 | end = free_list::__next_obj(end); 26 | ++i; 27 | ++actual_n; 28 | } 29 | cur_span->__free_list = free_list::__next_obj(end); 30 | free_list::__next_obj(end) = nullptr; 31 | cur_span->__use_count += actual_n; // 拿走了几个,use_count记得加上去 32 | __span_lists[index].__bucket_mtx.unlock(); // 解锁 33 | return actual_n; 34 | } 35 | 36 | span* central_cache::get_non_empty_span(span_list& list, size_t size) { 37 | // 先查看当前的spanlist中是否还有非空的span 38 | span* it = list.begin(); 39 | while (it != list.end()) { 40 | if (it->__free_list != nullptr) // 找到非空的了 41 | return it; 42 | it = it->__next; 43 | } 44 | #ifdef PROJECT_DEBUG 45 | LOG(DEBUG) << "central_cache::get_non_empty_span() cannot find non-null span in cc, goto pc for mem" << std::endl; 46 | #endif 47 | // 这里先解开桶锁 48 | list.__bucket_mtx.unlock(); 49 | 50 | // 如果走到这里,说明没有空闲的span了,就要找pc了 51 | #ifdef PROJECT_DEBUG 52 | LOG(DEBUG) << "central_cache::get_non_empty_span() call page_cache::get_instance()->new_span()" << std::endl; 53 | #endif 54 | page_cache::get_instance()->__page_mtx.lock(); 55 | span* cur_span = page_cache::get_instance()->new_span(size_class::num_move_page(size)); 56 | cur_span->__is_use = true; // 表示已经被使用 57 | cur_span->__obj_size = size; 58 | page_cache::get_instance()->__page_mtx.unlock(); 59 | #ifdef PROJECT_DEBUG 60 | LOG(DEBUG) << "central_cache::get_non_empty_span() get new span success" << std::endl; 61 | #endif 62 | // 切分的逻辑 63 | // 1. 计算span的大块内存的起始地址和大块内存的大小(字节数) 64 | char* addr_start = (char*)(cur_span->__page_id << PAGE_SHIFT); 65 | size_t bytes = cur_span->__n << PAGE_SHIFT; // << PAGE_SHIFT 就是乘8kb的意思 66 | char* addr_end = addr_start + bytes; 67 | // 2. 把大块内存切成自由链表链接起来 68 | cur_span->__free_list = addr_start; // 先切一块下来做头 69 | addr_start += size; 70 | void* tail = cur_span->__free_list; 71 | #ifdef PROJECT_DEBUG 72 | LOG(DEBUG) << "central_cache::get_non_empty_span() cut span" << std::endl; 73 | #endif 74 | int i = 1; 75 | while (addr_start < addr_end) { 76 | ++i; 77 | free_list::__next_obj(tail) = addr_start; // tail不是空指针 78 | // std::cerr << "here" << std::endl; 79 | tail = free_list::__next_obj(tail); 80 | addr_start += size; 81 | } 82 | free_list::__next_obj(tail) = nullptr; 83 | // 恢复锁 84 | list.__bucket_mtx.lock(); 85 | list.push_front(cur_span); 86 | return cur_span; 87 | } 88 | 89 | void central_cache::release_list_to_spans(void* start, size_t size) { 90 | size_t index = size_class::bucket_index(size); // 先算一下在哪一个桶里面 91 | __span_lists[index].__bucket_mtx.lock(); 92 | // 这里要注意,一个桶挂了多个span,这些内存块挂到哪一个span是不确定的 93 | while (start) { 94 | // 遍历这个链表 95 | void* next = free_list::__next_obj(start); // 先记录一下下一个,避免等下找不到了 96 | span* cur_span = page_cache::get_instance()->map_obj_to_span(start); 97 | free_list::__next_obj(start) = cur_span->__free_list; 98 | cur_span->__free_list = start; 99 | // 处理usecount 100 | cur_span->__use_count--; 101 | if (cur_span->__use_count == 0) { 102 | // 说明这个span切分出去的所有小块都回来了 103 | // 归还给pagecache 104 | // 1. 把这一页从cc的这个桶的spanlist中拿掉 105 | // #ifdef PROJECT_DEBUG 106 | // LOG(DEBUG) << "***" << std::endl; 107 | // #endif 108 | __span_lists[index].erase(cur_span); // 从桶里面拿走 109 | // #ifdef PROJECT_DEBUG 110 | // LOG(DEBUG) << "after ***" << std::endl; 111 | // #endif 112 | // 2. 此时不用管这个span的freelist了,因为这些内存本来就是span初始地址后面的,然后顺序也是乱的,直接置空即可 113 | // (这里还不太理解) 114 | cur_span->__free_list = nullptr; 115 | cur_span->__next = cur_span->__prev = nullptr; 116 | // 页号,页数是不能动的! 117 | // 3. 解开桶锁 118 | __span_lists[index].__bucket_mtx.unlock(); 119 | // 4. 还给pc 120 | page_cache::get_instance()->__page_mtx.lock(); 121 | page_cache::get_instance()->release_span_to_page(cur_span); 122 | page_cache::get_instance()->__page_mtx.unlock(); 123 | // 5. 恢复桶锁 124 | __span_lists[index].__bucket_mtx.lock(); 125 | } 126 | start = next; 127 | } 128 | __span_lists[index].__bucket_mtx.unlock(); 129 | } -------------------------------------------------------------------------------- /src/page_cache.cc: -------------------------------------------------------------------------------- 1 | 2 | #include "../include/page_cache.hpp" 3 | #include "../include/log.hpp" 4 | 5 | page_cache page_cache::__s_inst; 6 | 7 | // cc向pc获取k页的span 8 | span* page_cache::new_span(size_t k) { 9 | assert(k > 0); 10 | // 处理大内存情况 11 | if (k > PAGES_NUM - 1) { 12 | void* ptr = system_alloc(k); 13 | span* cur_span = __span_pool.new_(); 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 | __id_span_map.set(cur_span->__page_id, cur_span); 19 | return cur_span; 20 | } 21 | // 先检查第k个桶是否有span 22 | // #ifdef PROJECT_DEBUG 23 | // LOG(DEBUG) << "before ***" << std::endl; 24 | // #endif 25 | if (!__span_lists[k].empty()) { 26 | span* s = __span_lists[k].pop_front(); // ? __span_lists->pop_front(); 27 | // 建立id和span的映射,方便central cache回收小块内存时,查找对应的span 28 | for (PAGE_ID i = 0; i < s->__n; ++i) { 29 | // __id_span_map[s->__page_id + i] = s; 30 | __id_span_map.set(s->__page_id + i, s); 31 | } 32 | return s; 33 | } 34 | // #ifdef PROJECT_DEBUG 35 | // LOG(DEBUG) << "after ***" << std::endl; 36 | // #endif 37 | // 第k个桶是空的->去检查后面的桶里面有无span,如果有,可以把它进行切分 38 | for (size_t i = k + 1; i < PAGES_NUM; i++) { 39 | if (!__span_lists[i].empty()) { 40 | // 可以开始切了 41 | // 假设这个页是n页的,需要的是k页的 42 | // 1. 从__span_lists中拿下来 2. 切开 3. 一个返回给cc 4. 另一个挂到 n-k 号桶里面去 43 | span* n_span = __span_lists[i].pop_front(); 44 | span* k_span = __span_pool.new_(); 45 | // 在n_span头部切除k页下来 46 | k_span->__page_id = n_span->__page_id; // <1> 47 | k_span->__n = k; // <2> 48 | n_span->__page_id += k; // <3> 49 | n_span->__n -= k; // <4> 50 | /** 51 | * 这里要好好理解一下 100 ------ 101 ------- 102 ------ 52 | * 假设n_span从100开始,大小是3 53 | * 切出来之后k_span就是从100开始了,所以<1> 54 | * 切出来之后k_span就有k页了,所以 <2> 55 | * 切出来之后n_span就是从102开始了,所以 <3> 56 | * 切出来之后n_span就变成__n-k页了,所以 <4> 57 | */ 58 | // 剩下的挂到相应位置 59 | __span_lists[n_span->__n].push_front(n_span); 60 | // 存储n_span的首尾页号跟n_span的映射,方便pc回收内存时进行合并查找 61 | // __id_span_map[n_span->__page_id] = n_span; 62 | __id_span_map.set(n_span->__page_id, n_span); 63 | // __id_span_map[n_span->__page_id + n_span->__n - 1] = n_span; 64 | __id_span_map.set(n_span->__page_id + n_span->__n - 1, n_span); 65 | // 这里记录映射(简历id和span的映射,方便cc回收小块内存时,查找对应的span) 66 | for (PAGE_ID j = 0; j < k_span->__n; j++) { 67 | // __id_span_map[k_span->__page_id + j] = k_span; 68 | __id_span_map.set(k_span->__page_id + j, k_span); 69 | } 70 | #ifdef PROJECT_DEBUG 71 | LOG(DEBUG) << "page_cache::new_span() have span, return" << std::endl; 72 | #endif 73 | return k_span; 74 | } 75 | } 76 | #ifdef PROJECT_DEBUG 77 | LOG(DEBUG) << "page_cache::new_span() cannot find span, goto os for mem" << std::endl; 78 | #endif 79 | // 走到这里,说明找不到span了:找os要 80 | span* big_span = __span_pool.new_(); 81 | void* ptr = system_alloc(PAGES_NUM - 1); 82 | big_span->__page_id = (PAGE_ID)ptr >> PAGE_SHIFT; 83 | big_span->__n = PAGES_NUM - 1; 84 | // 挂到上面去 85 | __span_lists[PAGES_NUM - 1].push_front(big_span); 86 | return new_span(k); 87 | } 88 | 89 | span* page_cache::map_obj_to_span(void* obj) { 90 | // 先把页号算出来 91 | PAGE_ID id = (PAGE_ID)obj >> PAGE_SHIFT; // 这个理论推导可以自行推导一下 92 | // std::unique_lock lock(__page_mtx); // 用一个RAII的锁 93 | // auto ret = __id_span_map.find(id); 94 | // if (ret != __id_span_map.end()) 95 | // return ret->second; 96 | // LOG(FATAL) << std::endl; 97 | // assert(false); 98 | // return nullptr; 99 | 100 | // 换成radix树之后就不用加锁了 101 | auto ret = (span*)__id_span_map.get(id); 102 | assert(ret != nullptr); // 表示没找到 103 | return ret; 104 | } 105 | 106 | void page_cache::release_span_to_page(span* s, size_t size) { 107 | // 第二个参数是为了linux下一次释放大内存,需要大小 108 | // std::cout << s->__n << std::endl; // 33 109 | if (s->__n >= PAGES_NUM) { 110 | // 处理大内存 111 | void* ptr = (void*)(s->__page_id << PAGE_SHIFT); 112 | system_free(s, size); 113 | // delete s; 114 | __span_pool.delete_(s); 115 | return; 116 | } 117 | // 对span前后对页尝试进行合并,缓解内存碎片问题 118 | while (true) { 119 | PAGE_ID prev_id = s->__page_id - 1; // 前一块span的id一定是当前span的id-1 120 | // 拿到id如何找span: 之前写好的map能拿到吗? 121 | // 找到了,如果isuse是false,就能合并了(向前合并+向后合并) 122 | // 如果遇到了合并大小超过了128页了,也要停止了 123 | // auto ret = __id_span_map.find(prev_id); 124 | // if (ret == __id_span_map.end()) // 前面的页号没有了,不合并了 125 | // break; 126 | auto ret = (span*)__id_span_map.get(prev_id); 127 | if (ret == nullptr) 128 | break; 129 | // span* prev_span = ret->second; 130 | span* prev_span = ret; 131 | if (prev_span->__is_use == true) // 前面相邻页的span在使用,不合并了 132 | break; 133 | if (prev_span->__n + s->__n > PAGES_NUM - 1) // 合并出超过128页的span没办法管理,不合并了 134 | break; 135 | s->__page_id = prev_span->__page_id; 136 | s->__n += prev_span->__n; 137 | __span_lists[prev_span->__n].erase(prev_span); // 防止野指针,删掉 138 | // delete prev_span; // 删掉这个span 139 | __span_pool.delete_(prev_span); // 删掉这个span 140 | } // 向前合并的逻辑 while end; 141 | while (true) { 142 | PAGE_ID next_id = s->__page_id + s->__n; // 注意这里的页号是+n了 143 | // auto ret = __id_span_map.find(next_id); 144 | // if (ret == __id_span_map.end()) // 后面的页号没有了 145 | // break; 146 | auto ret = (span*)__id_span_map.get(next_id); 147 | if (ret == nullptr) 148 | break; 149 | // span* next_span = ret->second; 150 | span* next_span = ret; 151 | if (next_span->__is_use == true) // 后面相邻页的span在使用,不合并了 152 | break; 153 | if (next_span->__n + s->__n > PAGES_NUM - 1) // 合并出超过128页的span没办法管理,不合并了 154 | break; 155 | s->__page_id; // 起始页号不用变了,因为是向后合并 156 | s->__n += next_span->__n; 157 | __span_lists[next_span->__n].erase(next_span); // 防止野指针,删掉 158 | // delete next_span; 159 | __span_pool.delete_(next_span); 160 | } 161 | // 已经合并完成了,把东西挂起来 162 | __span_lists[s->__n].push_front(s); 163 | s->__is_use = false; 164 | // 处理一下映射,方便别人找到我 165 | // __id_span_map[s->__page_id] = s; 166 | // __id_span_map[s->__page_id + s->__n - 1] = s; 167 | __id_span_map.set(s->__page_id, s); 168 | __id_span_map.set(s->__page_id + s->__n - 1, s); 169 | } -------------------------------------------------------------------------------- /src/thread_cache.cc: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include "../include/thread_cache.hpp" 4 | #include "../include/central_cache.hpp" 5 | #include "../include/log.hpp" 6 | 7 | void* thread_cache::allocate(size_t size) { 8 | assert(size <= MAX_BYTES); 9 | size_t align_size = size_class::round_up(size); 10 | size_t bucket_index = size_class::bucket_index(size); 11 | if (!__free_lists[bucket_index].empty()) { 12 | return __free_lists[bucket_index].pop(); 13 | } else { 14 | // 这个桶下面没有内存了!找centralCache找 15 | #ifdef PROJECT_DEBUG 16 | LOG(DEBUG) << "thread_cache::allocate call thread_cache::fetch_from_central_cache" << std::endl; 17 | #endif 18 | return fetch_from_central_cache(bucket_index, align_size); 19 | } 20 | } 21 | 22 | void* thread_cache::fetch_from_central_cache(size_t index, size_t size) { 23 | // 慢开始反馈调节算法 24 | size_t batch_num = std::min(__free_lists[index].max_size(), size_class::num_move_size(size)); 25 | if (__free_lists[index].max_size() == batch_num) 26 | __free_lists[index].max_size() += 1; // 最多增长到512了 27 | // 1. 最开始一次向centralCache要太多,因为太多了可能用不完 28 | // 2. 如果你一直有这个桶size大小的内存,那么后面我可以给你越来越多,直到上限(size_class::num_move_size(size)) 29 | // 这个上限是根据这个桶的内存块大小size来决定的 30 | // 3. size越大,一次向centralcache要的就越小,如果size越小,相反。 31 | // 开始获取内存了 32 | void* start = nullptr; 33 | void* end = nullptr; 34 | #ifdef PROJECT_DEBUG 35 | LOG(DEBUG) << "thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj()" << std::endl; 36 | #endif 37 | size_t actual_n = central_cache::get_instance()->fetch_range_obj(start, end, batch_num, size); 38 | #ifdef PROJECT_DEBUG 39 | LOG(DEBUG) << "actual_n" 40 | << ":" << actual_n << std::endl; 41 | #endif 42 | assert(actual_n >= 1); 43 | if (actual_n == 1) { 44 | assert(start == end); 45 | return start; 46 | } else { 47 | __free_lists[index].push(free_list::__next_obj(start), end, actual_n - 1); 48 | return start; 49 | } 50 | 51 | return nullptr; 52 | } 53 | 54 | void thread_cache::deallocate(void* ptr, size_t size) { 55 | assert(ptr); 56 | assert(size <= MAX_BYTES); 57 | size_t index = size_class::bucket_index(size); 58 | __free_lists[index].push(ptr); 59 | // 当链表长度大于一次批量申请的内存的时候,就开始还一段list给cc 60 | if (__free_lists[index].size() >= __free_lists[index].max_size()) { 61 | #ifdef PROJECT_DEBUG 62 | LOG(DEBUG) << __free_lists[index].size() << ":" << __free_lists[index].max_size() << std::endl; 63 | LOG(DEBUG) << "call list_too_long" << std::endl; 64 | #endif 65 | list_too_long(__free_lists[index], size); 66 | } 67 | } 68 | 69 | void thread_cache::list_too_long(free_list& list, size_t size) { 70 | void* start = nullptr; 71 | void* end = nullptr; 72 | list.pop(start, end, list.max_size()); 73 | #ifdef PROJECT_DEBUG 74 | LOG(DEBUG) << "list pop success -> call release_list_to_spans()" << std::endl; 75 | #endif 76 | central_cache::get_instance()->release_list_to_spans(start, size); 77 | } -------------------------------------------------------------------------------- /test/test1.log: -------------------------------------------------------------------------------- 1 | # 测试: 申请1024次6字节 2 | 6字节对齐成8字节。如果第1025次申请,就会找新的span 3 | 预期:log中只有两个goto os for mem 4 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 5 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 6 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 7 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 8 | [DEBUG][src/central_cache.cc][45] central_cache::get_non_empty_span() cannot find non-null span in cc, goto pc for mem 9 | [DEBUG][src/central_cache.cc][52] central_cache::get_non_empty_span() call page_cache::get_instance()->new_span() 10 | [DEBUG][src/page_cache.cc][43] page_cache::new_span() cannot find span, goto os for mem 11 | [DEBUG][src/page_cache.cc][37] page_cache::new_span() have span, return 12 | [DEBUG][src/central_cache.cc][58] central_cache::get_non_empty_span() get new span success 13 | [DEBUG][src/central_cache.cc][70] central_cache::get_non_empty_span() cut span 14 | [DEBUG][src/thread_cache.cc][47] actual_n:1 15 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 16 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 17 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 18 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 19 | [DEBUG][src/thread_cache.cc][47] actual_n:2 20 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 21 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 22 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 23 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 24 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 25 | [DEBUG][src/thread_cache.cc][47] actual_n:3 26 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 27 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 28 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 29 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 30 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 31 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 32 | [DEBUG][src/thread_cache.cc][47] actual_n:4 33 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 34 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 35 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 36 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 37 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 38 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 39 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 40 | [DEBUG][src/thread_cache.cc][47] actual_n:5 41 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 42 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 43 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 44 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 45 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 46 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 47 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 48 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 49 | [DEBUG][src/thread_cache.cc][47] actual_n:6 50 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 51 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 52 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 53 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 54 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 55 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 56 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 57 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 58 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 59 | [DEBUG][src/thread_cache.cc][47] actual_n:7 60 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 61 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 62 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 63 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 64 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 65 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 66 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 67 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 68 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 69 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 70 | [DEBUG][src/thread_cache.cc][47] actual_n:8 71 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 72 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 73 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 74 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 75 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 76 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 77 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 78 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 79 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 80 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 81 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 82 | [DEBUG][src/thread_cache.cc][47] actual_n:9 83 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 84 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 85 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 86 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 87 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 88 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 89 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 90 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 91 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 92 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 93 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 94 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 95 | [DEBUG][src/thread_cache.cc][47] actual_n:10 96 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 97 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 98 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 99 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 100 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 101 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 102 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 103 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 104 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 105 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 106 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 107 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 108 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 109 | [DEBUG][src/thread_cache.cc][47] actual_n:11 110 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 111 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 112 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 113 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 114 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 115 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 116 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 117 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 118 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 119 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 120 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 121 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 122 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 123 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 124 | [DEBUG][src/thread_cache.cc][47] actual_n:12 125 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 126 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 127 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 128 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 129 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 130 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 131 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 132 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 133 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 134 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 135 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 136 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 137 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 138 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 139 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 140 | [DEBUG][src/thread_cache.cc][47] actual_n:13 141 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 142 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 143 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 144 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 145 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 146 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 147 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 148 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 149 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 150 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 151 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 152 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 153 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 154 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 155 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 156 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 157 | [DEBUG][src/thread_cache.cc][47] actual_n:14 158 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 159 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 160 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 161 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 162 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 163 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 164 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 165 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 166 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 167 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 168 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 169 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 170 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 171 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 172 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 173 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 174 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 175 | [DEBUG][src/thread_cache.cc][47] actual_n:15 176 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 177 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 178 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 179 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 180 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 181 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 182 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 183 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 184 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 185 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 186 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 187 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 188 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 189 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 190 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 191 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 192 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 193 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 194 | [DEBUG][src/thread_cache.cc][47] actual_n:16 195 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 196 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 197 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 198 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 199 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 200 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 201 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 202 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 203 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 204 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 205 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 206 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 207 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 208 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 209 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 210 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 211 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 212 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 213 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 214 | [DEBUG][src/thread_cache.cc][47] actual_n:17 215 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 216 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 217 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 218 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 219 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 220 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 221 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 222 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 223 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 224 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 225 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 226 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 227 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 228 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 229 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 230 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 231 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 232 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 233 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 234 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 235 | [DEBUG][src/thread_cache.cc][47] actual_n:18 236 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 237 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 238 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 239 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 240 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 241 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 242 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 243 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 244 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 245 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 246 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 247 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 248 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 249 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 250 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 251 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 252 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 253 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 254 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 255 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 256 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 257 | [DEBUG][src/thread_cache.cc][47] actual_n:19 258 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 259 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 260 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 261 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 262 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 263 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 264 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 265 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 266 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 267 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 268 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 269 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 270 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 271 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 272 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 273 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 274 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 275 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 276 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 277 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 278 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 279 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 280 | [DEBUG][src/thread_cache.cc][47] actual_n:20 281 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 282 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 283 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 284 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 285 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 286 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 287 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 288 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 289 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 290 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 291 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 292 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 293 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 294 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 295 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 296 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 297 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 298 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 299 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 300 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 301 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 302 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 303 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 304 | [DEBUG][src/thread_cache.cc][47] actual_n:21 305 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 306 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 307 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 308 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 309 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 310 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 311 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 312 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 313 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 314 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 315 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 316 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 317 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 318 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 319 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 320 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 321 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 322 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 323 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 324 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 325 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 326 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 327 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 328 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 329 | [DEBUG][src/thread_cache.cc][47] actual_n:22 330 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 331 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 332 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 333 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 334 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 335 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 336 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 337 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 338 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 339 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 340 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 341 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 342 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 343 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 344 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 345 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 346 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 347 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 348 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 349 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 350 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 351 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 352 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 353 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 354 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 355 | [DEBUG][src/thread_cache.cc][47] actual_n:23 356 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 357 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 358 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 359 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 360 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 361 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 362 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 363 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 364 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 365 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 366 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 367 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 368 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 369 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 370 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 371 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 372 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 373 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 374 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 375 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 376 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 377 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 378 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 379 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 380 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 381 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 382 | [DEBUG][src/thread_cache.cc][47] actual_n:24 383 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 384 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 385 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 386 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 387 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 388 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 389 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 390 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 391 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 392 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 393 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 394 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 395 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 396 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 397 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 398 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 399 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 400 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 401 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 402 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 403 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 404 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 405 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 406 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 407 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 408 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 409 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 410 | [DEBUG][src/thread_cache.cc][47] actual_n:25 411 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 412 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 413 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 414 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 415 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 416 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 417 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 418 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 419 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 420 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 421 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 422 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 423 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 424 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 425 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 426 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 427 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 428 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 429 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 430 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 431 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 432 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 433 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 434 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 435 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 436 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 437 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 438 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 439 | [DEBUG][src/thread_cache.cc][47] actual_n:26 440 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 441 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 442 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 443 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 444 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 445 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 446 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 447 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 448 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 449 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 450 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 451 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 452 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 453 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 454 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 455 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 456 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 457 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 458 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 459 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 460 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 461 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 462 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 463 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 464 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 465 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 466 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 467 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 468 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 469 | [DEBUG][src/thread_cache.cc][47] actual_n:27 470 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 471 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 472 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 473 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 474 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 475 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 476 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 477 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 478 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 479 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 480 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 481 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 482 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 483 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 484 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 485 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 486 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 487 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 488 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 489 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 490 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 491 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 492 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 493 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 494 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 495 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 496 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 497 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 498 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 499 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 500 | [DEBUG][src/thread_cache.cc][47] actual_n:28 501 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 502 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 503 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 504 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 505 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 506 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 507 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 508 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 509 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 510 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 511 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 512 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 513 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 514 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 515 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 516 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 517 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 518 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 519 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 520 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 521 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 522 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 523 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 524 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 525 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 526 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 527 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 528 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 529 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 530 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 531 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 532 | [DEBUG][src/thread_cache.cc][47] actual_n:29 533 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 534 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 535 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 536 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 537 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 538 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 539 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 540 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 541 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 542 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 543 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 544 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 545 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 546 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 547 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 548 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 549 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 550 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 551 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 552 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 553 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 554 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 555 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 556 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 557 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 558 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 559 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 560 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 561 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 562 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 563 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 564 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 565 | [DEBUG][src/thread_cache.cc][47] actual_n:30 566 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 567 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 568 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 569 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 570 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 571 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 572 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 573 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 574 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 575 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 576 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 577 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 578 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 579 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 580 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 581 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 582 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 583 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 584 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 585 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 586 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 587 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 588 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 589 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 590 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 591 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 592 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 593 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 594 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 595 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 596 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 597 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 598 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 599 | [DEBUG][src/thread_cache.cc][47] actual_n:31 600 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 601 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 602 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 603 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 604 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 605 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 606 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 607 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 608 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 609 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 610 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 611 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 612 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 613 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 614 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 615 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 616 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 617 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 618 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 619 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 620 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 621 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 622 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 623 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 624 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 625 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 626 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 627 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 628 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 629 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 630 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 631 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 632 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 633 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 634 | [DEBUG][src/thread_cache.cc][47] actual_n:32 635 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 636 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 637 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 638 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 639 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 640 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 641 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 642 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 643 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 644 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 645 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 646 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 647 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 648 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 649 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 650 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 651 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 652 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 653 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 654 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 655 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 656 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 657 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 658 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 659 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 660 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 661 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 662 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 663 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 664 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 665 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 666 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 667 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 668 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 669 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 670 | [DEBUG][src/thread_cache.cc][47] actual_n:33 671 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 672 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 673 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 674 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 675 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 676 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 677 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 678 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 679 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 680 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 681 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 682 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 683 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 684 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 685 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 686 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 687 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 688 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 689 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 690 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 691 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 692 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 693 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 694 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 695 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 696 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 697 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 698 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 699 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 700 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 701 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 702 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 703 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 704 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 705 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 706 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 707 | [DEBUG][src/thread_cache.cc][47] actual_n:34 708 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 709 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 710 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 711 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 712 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 713 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 714 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 715 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 716 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 717 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 718 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 719 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 720 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 721 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 722 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 723 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 724 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 725 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 726 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 727 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 728 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 729 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 730 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 731 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 732 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 733 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 734 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 735 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 736 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 737 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 738 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 739 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 740 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 741 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 742 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 743 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 744 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 745 | [DEBUG][src/thread_cache.cc][47] actual_n:35 746 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 747 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 748 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 749 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 750 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 751 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 752 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 753 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 754 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 755 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 756 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 757 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 758 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 759 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 760 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 761 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 762 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 763 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 764 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 765 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 766 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 767 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 768 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 769 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 770 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 771 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 772 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 773 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 774 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 775 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 776 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 777 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 778 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 779 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 780 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 781 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 782 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 783 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 784 | [DEBUG][src/thread_cache.cc][47] actual_n:36 785 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 786 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 787 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 788 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 789 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 790 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 791 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 792 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 793 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 794 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 795 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 796 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 797 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 798 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 799 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 800 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 801 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 802 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 803 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 804 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 805 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 806 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 807 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 808 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 809 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 810 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 811 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 812 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 813 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 814 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 815 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 816 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 817 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 818 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 819 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 820 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 821 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 822 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 823 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 824 | [DEBUG][src/thread_cache.cc][47] actual_n:37 825 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 826 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 827 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 828 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 829 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 830 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 831 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 832 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 833 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 834 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 835 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 836 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 837 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 838 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 839 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 840 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 841 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 842 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 843 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 844 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 845 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 846 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 847 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 848 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 849 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 850 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 851 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 852 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 853 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 854 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 855 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 856 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 857 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 858 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 859 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 860 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 861 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 862 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 863 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 864 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 865 | [DEBUG][src/thread_cache.cc][47] actual_n:38 866 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 867 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 868 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 869 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 870 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 871 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 872 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 873 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 874 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 875 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 876 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 877 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 878 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 879 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 880 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 881 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 882 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 883 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 884 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 885 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 886 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 887 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 888 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 889 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 890 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 891 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 892 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 893 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 894 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 895 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 896 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 897 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 898 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 899 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 900 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 901 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 902 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 903 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 904 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 905 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 906 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 907 | [DEBUG][src/thread_cache.cc][47] actual_n:39 908 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 909 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 910 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 911 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 912 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 913 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 914 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 915 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 916 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 917 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 918 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 919 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 920 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 921 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 922 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 923 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 924 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 925 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 926 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 927 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 928 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 929 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 930 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 931 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 932 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 933 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 934 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 935 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 936 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 937 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 938 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 939 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 940 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 941 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 942 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 943 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 944 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 945 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 946 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 947 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 948 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 949 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 950 | [DEBUG][src/thread_cache.cc][47] actual_n:40 951 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 952 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 953 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 954 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 955 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 956 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 957 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 958 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 959 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 960 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 961 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 962 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 963 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 964 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 965 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 966 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 967 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 968 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 969 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 970 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 971 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 972 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 973 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 974 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 975 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 976 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 977 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 978 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 979 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 980 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 981 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 982 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 983 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 984 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 985 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 986 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 987 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 988 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 989 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 990 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 991 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 992 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 993 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 994 | [DEBUG][src/thread_cache.cc][47] actual_n:41 995 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 996 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 997 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 998 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 999 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1000 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1001 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1002 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1003 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1004 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1005 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1006 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1007 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1008 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1009 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1010 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1011 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1012 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1013 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1014 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1015 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1016 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1017 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1018 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1019 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1020 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1021 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1022 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1023 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1024 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1025 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1026 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1027 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1028 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1029 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1030 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1031 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1032 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1033 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1034 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1035 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1036 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 1037 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 1038 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 1039 | [DEBUG][src/thread_cache.cc][47] actual_n:42 1040 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1041 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1042 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1043 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1044 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1045 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1046 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1047 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1048 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1049 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1050 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1051 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1052 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1053 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1054 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1055 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1056 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1057 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1058 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1059 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1060 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1061 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1062 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1063 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1064 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1065 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1066 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1067 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1068 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1069 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1070 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1071 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1072 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1073 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1074 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1075 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1076 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1077 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1078 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1079 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1080 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1081 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1082 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 1083 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 1084 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 1085 | [DEBUG][src/thread_cache.cc][47] actual_n:43 1086 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1087 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1088 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1089 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1090 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1091 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1092 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1093 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1094 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1095 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1096 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1097 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1098 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1099 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1100 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1101 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1102 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1103 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1104 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1105 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1106 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1107 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1108 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1109 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1110 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1111 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1112 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1113 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1114 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1115 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1116 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1117 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1118 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1119 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1120 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1121 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1122 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1123 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1124 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1125 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1126 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1127 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1128 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1129 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 1130 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 1131 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 1132 | [DEBUG][src/thread_cache.cc][47] actual_n:44 1133 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1134 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1135 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1136 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1137 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1138 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1139 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1140 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1141 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1142 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1143 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1144 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1145 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1146 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1147 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1148 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1149 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1150 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1151 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1152 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1153 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1154 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1155 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1156 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1157 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1158 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1159 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1160 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1161 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1162 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1163 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1164 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1165 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1166 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1167 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1168 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1169 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1170 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1171 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1172 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1173 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1174 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1175 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1176 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1177 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 1178 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 1179 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 1180 | [DEBUG][src/thread_cache.cc][47] actual_n:34 1181 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1182 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1183 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1184 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1185 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1186 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1187 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1188 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1189 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1190 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1191 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1192 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1193 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1194 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1195 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1196 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1197 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1198 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1199 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1200 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1201 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1202 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1203 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1204 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1205 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1206 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1207 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1208 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1209 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1210 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1211 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1212 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1213 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1214 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 1215 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 1216 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 1217 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 1218 | [DEBUG][src/central_cache.cc][45] central_cache::get_non_empty_span() cannot find non-null span in cc, goto pc for mem 1219 | [DEBUG][src/central_cache.cc][52] central_cache::get_non_empty_span() call page_cache::get_instance()->new_span() 1220 | [DEBUG][src/page_cache.cc][37] page_cache::new_span() have span, return 1221 | [DEBUG][src/central_cache.cc][58] central_cache::get_non_empty_span() get new span success 1222 | [DEBUG][src/central_cache.cc][70] central_cache::get_non_empty_span() cut span 1223 | [DEBUG][src/thread_cache.cc][47] actual_n:46 1224 | -------------------------------------------------------------------------------- /unit_test.cc: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include "./include/tcmalloc.hpp" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | void alloc1() { 11 | for (size_t i = 0; i < 5; i++) { 12 | void* ptr = tcmalloc(6); 13 | std::cout << "thread id: " << std::this_thread::get_id() << std::endl; 14 | } 15 | } 16 | void tls_test() { 17 | std::thread t1(alloc1); 18 | std::thread t2(alloc1); 19 | t1.join(); 20 | t2.join(); 21 | } 22 | 23 | void test_alloc1() { 24 | std::cout << "call tcmalloc(1)" << std::endl; 25 | void* ptr = tcmalloc(8 * 1024); 26 | std::cout << "call tcmalloc(2)" << std::endl; 27 | ptr = tcmalloc(10); 28 | std::cout << "call tcmalloc(3)" << std::endl; 29 | ptr = tcmalloc(2); 30 | std::cout << "call tcmalloc(4)" << std::endl; 31 | ptr = tcmalloc(1); 32 | std::cout << "call tcmalloc(5)" << std::endl; 33 | ptr = tcmalloc(1); 34 | std::cout << "call tcmalloc(6)" << std::endl; 35 | ptr = tcmalloc(5); 36 | std::cout << "call tcmalloc(7)" << std::endl; 37 | ptr = tcmalloc(1); 38 | } 39 | 40 | void test_alloc2() { 41 | for (size_t i = 0; i < 1024; ++i) { 42 | void* p1 = tcmalloc(6); 43 | } 44 | void* p2 = tcmalloc(6); // 这一次一定会找新的span 45 | } 46 | 47 | void test_dealloc(int alloc_times = 10) { 48 | // 创建随机数生成器 49 | std::random_device rd; 50 | std::mt19937 gen(rd()); // 以随机设备作为种子 51 | std::uniform_int_distribution<> distrib(1, 127 * 100); // 设置随机数分布范围1-127 52 | // 生成并输出随机数 53 | std::map s; 54 | for (int i = 0; i < alloc_times; i++) { 55 | size_t sz = distrib(gen); 56 | std::cout << sz << std::endl; 57 | void* ptr = tcmalloc(sz); 58 | std::cout << "malloc successful" << std::endl; 59 | s.insert({ ptr, sz }); // 申请随机值 60 | } 61 | for (auto& e : s) { 62 | tcfree(e.first /*, e.second*/); 63 | } 64 | } 65 | 66 | void test_multi_thread() { 67 | std::thread t1(std::bind(test_dealloc, 1000)); 68 | // std::thread t2(std::bind(test_dealloc, 20)); 69 | t1.join(); 70 | // t2.join(); 71 | std::cout << "run successful" << std::endl; 72 | } 73 | 74 | void run() { 75 | void* ptr = tcmalloc(5949); 76 | tcfree(ptr); 77 | std::cout << "run successful" << std::endl; 78 | } 79 | 80 | void big_alloc() { 81 | std::thread t1(run); 82 | t1.join(); 83 | } 84 | 85 | int main() { 86 | // std::cout << "haha" << std::endl; 87 | // big_alloc(); 88 | #ifdef __aarch64__ 89 | std::cout << "64" << std::endl; 90 | #elif defined(__arm__) 91 | std::cout << "32" << std::endl; 92 | #else 93 | std::cout << "unknown sys" << std::endl; 94 | #endif 95 | return 0; 96 | } -------------------------------------------------------------------------------- /work.md: -------------------------------------------------------------------------------- 1 | # 项目详细实现 2 | 3 | - [项目详细实现](#项目详细实现) 4 | - [threadCache整体框架](#threadcache整体框架) 5 | - [开始写threadCache代码](#开始写threadcache代码) 6 | - [哈系桶的映射规则](#哈系桶的映射规则) 7 | - [threadCache的tls无锁访问](#threadcache的tls无锁访问) 8 | - [写tcfree的时候的一个遗留问题](#写tcfree的时候的一个遗留问题) 9 | - [central\_cache整体结构](#central_cache整体结构) 10 | - [central\_cache的核心逻辑](#central_cache的核心逻辑) 11 | - [central\_cache里面fetch\_range\_obj的逻辑](#central_cache里面fetch_range_obj的逻辑) 12 | - [page\_cache整体框架](#page_cache整体框架) 13 | - [获取span详解](#获取span详解) 14 | - [关于 new\_span 如何加锁的文字(重要/容易出bug)](#关于-new_span-如何加锁的文字重要容易出bug) 15 | - [内存申请流程联调](#内存申请流程联调) 16 | - [thread\_cache内存释放](#thread_cache内存释放) 17 | - [central\_cache内存释放](#central_cache内存释放) 18 | - [page\_cache内存释放](#page_cache内存释放) 19 | - [大于256k的情况](#大于256k的情况) 20 | - [处理代码中`new`的问题](#处理代码中new的问题) 21 | - [解决free,使其不用传大小](#解决free使其不用传大小) 22 | - [多线程场景下深度测试](#多线程场景下深度测试) 23 | - [分析性能瓶颈](#分析性能瓶颈) 24 | - [用Radix Tree进行优化](#用radix-tree进行优化) 25 | 26 | 27 | ## threadCache整体框架 28 | 29 | **一个重要概念:自由链表,就是把切好的小块内存链接起来,这一块内存的前4个字节是一个指针,指向链表的下一个地方。** 30 | 31 | 如果是定长内存池,那就是一个自由链表就行了,但是现在不是定长的。 32 | 33 | 那是不是1byte一个自由链表,2一个,3一个呢,这也太多了。 34 | 35 | 规定:小于256kb的找threadCache, 大于256kb后面再说。 36 | 37 | 所以如果如果1,2,3,4比特都挂一个链表,那就是二十几万个链表,太大了! 38 | 39 | 所以如图所示,我们就这么设计。 40 | 41 | ![](./assets/2.png) 42 | 43 | 这是一种牺牲和妥协。 44 | 45 | ## 开始写threadCache代码 46 | 47 | 首先肯定要提供这两个接口,不用说的。 48 | 49 | thread_cache.hpp 50 | ```cpp 51 | class thread_cache { 52 | private: 53 | public: 54 | void* allocate(size_t size); 55 | void deallocate(void* ptr, size_t size); 56 | }; 57 | ``` 58 | 59 | 当然我们发现,控制自由链表需要一个类,然后这个类不仅threadCache要用,其他上层的也要用,所以我们写在 common.hpp 里面去。 60 | 61 | ```cpp 62 | class free_list { 63 | private: 64 | void* __free_list_ptr; 65 | public: 66 | void push(void* obj); 67 | void* pop(); 68 | }; 69 | ``` 70 | 71 | push和pop实现很简单,头插和头删就行了。 72 | 73 | ```cpp 74 | void push(void* obj) { 75 | *(void**)obj = __free_list_ptr; 76 | __free_list_ptr = obj; 77 | } 78 | ``` 79 | 80 | 这个`*(void**)obj`需要理解一下,因为我们不知道当前环境下一个指针是4个字节的还是8个字节的,所以要这样才能取到指针的大小。 81 | 82 | 然后我们也可以封装一下 83 | 84 | ```cpp 85 | class free_list { 86 | private: 87 | void* __free_list_ptr; 88 | 89 | public: 90 | void push(void* obj) { 91 | assert(obj); 92 | __next_obj(obj) = __free_list_ptr; 93 | __free_list_ptr = obj; 94 | } 95 | void* pop() { 96 | assert(__free_list_ptr); 97 | void* obj = __free_list_ptr; 98 | __free_list_ptr = __next_obj(obj); 99 | } 100 | 101 | private: 102 | static void*& __next_obj(void* obj) { 103 | return *(void**)obj; 104 | } 105 | }; 106 | ``` 107 | 108 | ## 哈系桶的映射规则 109 | 110 | 我们可以写一个类,去计算对象大小的对齐映射规则。 111 | 112 | tcmalloc里面的映射规则是很复杂的,这里我们进行简化了。 113 | 114 | 映射规则如下: 115 | 116 | 整体控制在最多10%左右的内碎片浪费 117 | 118 | | 申请的字节数量 | 对齐数 | 自由链表里对应的范围 | 119 | |-------|-------|-------| 120 | | [1,128] | 8byte对齐 | freelist[0,16) | 121 | | [128+1,1024] | 16byte对齐 | freelist[16,72) | 122 | | [1024+1,8*1024] | 128byte对齐 | freelist[72,128) | 123 | | [8*1024+1,64*1024] | 1024byte对齐 | freelist[128,184) | 124 | | [64*1024+1,256*1024] | 8*1024byte对齐 | freelist[184,208) | 125 | 126 | **这样我们可以控制最多10%的内碎片浪费,如果你申请的多,那我就允许你浪费的稍微多一点,这个也是很合理的(这个规则是本项目设定的,tcmalloc的更加复杂)** 127 | 128 | 所以我们先确定跟谁对齐,然后再找对齐数。 129 | 130 | common.hpp 131 | ```cpp 132 | // 计算对象大小的对齐映射规则 133 | class size_class { 134 | public: 135 | static inline size_t __round_up(size_t bytes, size_t align_number) { 136 | return (((bytes) + align_number - 1) & ~(align_number - 1)); 137 | } 138 | static inline size_t round_up(size_t size) { 139 | if (size <= 128) 140 | return __round_up(size, 8); 141 | else if (size <= 1024) 142 | return __round_up(size, 16); 143 | else if (size <= 8 * 1024) 144 | return __round_up(size, 128); 145 | else if (size <= 64 * 1024) 146 | return __round_up(size, 1024); 147 | else if (size <= 256 * 1024) 148 | return __round_up(size, 8 * 1024); 149 | else { 150 | assert(false); 151 | return -1; 152 | } 153 | } 154 | }; 155 | ``` 156 | 157 | 如何理解这个代码。 158 | 159 | ```cpp 160 | size_t __round_up(size_t size, size_t align_number) { 161 | return (((bytes) + align_number - 1) & ~(align_number - 1)); 162 | } 163 | ``` 164 | 165 | 大佬想出来的,我们可以测试几个。 166 | 167 | thread_cache.cc 168 | ```cpp 169 | void* thread_cache::allocate(size_t size) { 170 | assert(size <= MAX_BITES); 171 | size_t align_size = size_class::round_up(size); 172 | } 173 | ``` 174 | 现在我们就可以获得对齐之后的大小了!也就是说,你申请size字节,我会给你align_size字节。 175 | 那么是在哪一个桶里面取出来的这一部分内存呢?所以我们还要写方法去找这个桶在哪。 176 | 177 | 178 | ```cpp 179 | // 计算映射的哪一个自由链表桶 180 | static inline size_t __bucket_index(size_t bytes, size_t align_shift) { 181 | return ((bytes + (1 << align_shift) - 1) >> align_shift) - 1; 182 | /* 183 | 这个还是同一道理,bytes不是对齐数的倍数,那就是直接模就行了 184 | 如果是,那就特殊规则一下即可,比如 1~128字节,对齐数字是8 185 | 那就是 bytes / 8 + 1 就是几号桶了 186 | 如果 bytes % 8 == 0 表示没有余数,刚好就是那个桶,就不用+1 187 | 这个也很好理解 188 | */ 189 | } 190 | static inline size_t bucket_index(size_t bytes) { 191 | assert(bytes <= MAX_BYTES); 192 | // 每个区间有多少个链 193 | static int group_array[4] = { 16, 56, 56, 56 }; 194 | if (bytes <= 128) { 195 | return __bucket_index(bytes, 3); 196 | } else if (bytes <= 1024) { 197 | return __bucket_index(bytes - 128, 4) + group_array[0]; 198 | } else if (bytes <= 8 * 1024) { 199 | return __bucket_index(bytes - 1024, 7) + group_array[1] + group_array[0]; 200 | } else if (bytes <= 64 * 1024) { 201 | return __bucket_index(bytes - 8 * 1024, 10) + group_array[2] + group_array[1] 202 | + group_array[0]; 203 | } else if (bytes <= 256 * 1024) { 204 | return __bucket_index(bytes - 64 * 1024, 13) + group_array[3] + group_array[2] + group_array[1] + group_array[0]; 205 | } else { 206 | assert(false); 207 | } 208 | return -1; 209 | } 210 | ``` 211 | 212 | 这个其实也是很好理解的,因为每个对齐区间有多少个桶已经确定了: 213 | 214 | 如果按照8对齐:16个桶 215 | 如果按照16对齐:56个桶 216 | ... 217 | 218 | 然后bucket_index里面,为什么后面要+group_array的数字,因为__bucket_index只算出来了你是这一组的第几个,不是在全部桶里面的第几个。 219 | 220 | > 比如你是按照16对齐的,你的桶的编号肯定是大于16了,因为按照8对齐的已经用了16个桶了,所以你肯定是第17个桶开始。那么__bucket_index可以告诉你,你是按照16对齐的这56个桶里的第一个,在这一组里面你是第一个桶,但是在全部桶里面,你是第17个桶了。 221 | 222 | 然后thread_cache.cc这里面就可以完善了。 223 | 224 | ```cpp 225 | void* thread_cache::allocate(size_t size) { 226 | assert(size <= MAX_BYTES); 227 | size_t align_size = size_class::round_up(size); 228 | size_t bucket_index = size_class::bucket_index(size); 229 | if (!__free_lists[bucket_index].empty()) { 230 | return __free_lists[bucket_index].pop(); 231 | } else { 232 | // 这个桶下面没有内存了!找centralCache找 233 | return fetch_from_central_cache(bucket_index, align_size); 234 | } 235 | } 236 | ``` 237 | 238 | ## threadCache的tls无锁访问 239 | 240 | 首先,如果我们了解过操作系统相关知识,我们就知道,进程里面(包括线程)这些,都是共享的。也就是说,如果我们不加处理,我们创建的threadCache是所有线程都能访问的。 241 | 242 | 这个不是我们想要的,我们需要的是,每一个线程都有自己的threadCache! 243 | 244 | > 线程局部存储(TLS),是一种变量的存储方法,这个变量在它所在的线程内是全局可访问的,但是不能被其他线程访问到,这样就保持了数据的线程独立性。而熟知的全局变量,是所有线程都可以访问的,这样就不可避免需要锁来控制,增加了控制成本和代码复杂度。 245 | 246 | 247 | 然后linux下这样操作就行了。 248 | 249 | thread_cache.hpp 250 | ```cpp 251 | __thread thread_cache* p_tls_thread_cache = nullptr; 252 | ``` 253 | 254 | windows下这样写 255 | 256 | ```cpp 257 | __thread static thread_cache* p_tls_thread_cache = nullptr; 258 | ``` 259 | 260 | 这个也很好理解了,这样声明这个变量之后,这个p_tls_thread_cache变量就会每个线程独享一份。 261 | 262 | 然后我们调用的时候,也不可能让别人直接去调用thread_cache.cc里面的alloc,所以,我们再弄一个文件,提供一个调用的接口。 263 | 264 | tcmalloc.hpp 265 | ```cpp 266 | static void* tcmalloc(size_t size) { 267 | if (p_tls_thread_cache == nullptr) 268 | // 相当于单例 269 | p_tls_thread_cache = new thread_cache; 270 | return p_tls_thread_cache->allocate(size); 271 | } 272 | 273 | static void tcfree(size_t size) { 274 | } 275 | #endif 276 | ``` 277 | 278 | ## 写tcfree的时候的一个遗留问题 279 | 280 | tcmalloc.hpp 281 | ```cpp 282 | static void tcfree(void* ptr, size_t size) { 283 | assert(p_tls_thread_cache); 284 | p_tls_thread_cache->deallocate(ptr, size); 285 | } 286 | ``` 287 | 288 | thread_cache.cc 289 | ```cpp 290 | void thread_cache::deallocate(void* ptr, size_t size) { 291 | assert(ptr); 292 | assert(size <= MAX_BYTES); 293 | size_t index = size_class::bucket_index(size); 294 | __free_lists[index].push(ptr); 295 | } 296 | ``` 297 | 298 | 我这里是要传大小的,但是呢,p_tls_thread_cache->deallocate()需要给size,不然不知道还到哪一个桶上。但是我们的free是不用传size的,这里如何解决? 299 | 300 | 目前解决不了,先保留这个问题,留到后面再解决。 301 | 302 | ## central_cache整体结构 303 | 304 | centralCache也是一个哈希桶结构,他的哈希桶的映射关系跟threadCache是一样的。不同的是他的每个哈希桶位置挂是SpanList链表结构,不过每个映射桶下面的span中的大内存块被按映射关系切成了一个个小内存块对象挂在span的自由链表中。 305 | 306 | 这里是需要加锁的,但是是桶锁。如果不同线程获取不同的桶的东西,就不用加锁。 307 | 308 | ![](./assets/3.png) 309 | 310 | **申请内存:** 311 | 1. 当thread cache中没有内存时,就会批量向central cache申请一些内存对象,这里的批量获取对 象的数量使用了类似网络tcp协议拥塞控制的慢开始算法;central cache也有一个哈希映射的 spanlist,spanlist中挂着span,从span中取出对象给thread cache,这个过程是需要加锁的,不 过这里使用的是一个桶锁,尽可能提高效率。 312 | 2. central cache映射的spanlist中所有span的都没有内存以后,则需要向page cache申请一个新的 span对象,拿到span以后将span管理的内存按大小切好作为自由链表链接到一起。然后从span 中取对象给thread cache。 313 | 3. central cache的中挂的span中use_count记录分配了多少个对象出去,分配一个对象给thread cache,就++use_count 314 | 315 | 316 | **释放内存:** 317 | 318 | 1. 当threadCache过长或者线程销毁,则会将内存释放回centralCache中的,释放回来时--use_count。当use_count减到0时则表示所有对象都回到了span,则将span释放回pageCache,pageCache中会对前后相邻的空闲页进行合并。 319 | 320 | **centralCache里面的小对象是由大对象切出来的,大对象就是Span。** 321 | 322 | span的链表是双向链表。 323 | 324 | span除了central_cache要用,后面的page_cache也要用,所以定义到common里面去吧。 325 | 326 | 然后这里存在一个问题,就是这个size_t,在64位下不够了,需要条件编译处理一下。 327 | 328 | common.hpp 329 | ```cpp 330 | #if defined(_WIN64) || defined(__x86_64__) || defined(__ppc64__) || defined(__aarch64__) 331 | typedef unsigned long long PAGE_ID; 332 | #else 333 | typedef size_t PAGE_ID; 334 | #endif 335 | ``` 336 | 337 | 这里如果在windows下有个坑,win64下是既有win64也有win32的定义的,所以要先判断64的,避免出bug。 338 | 339 | ```cpp 340 | // 管理大块内存 341 | class span { 342 | public: 343 | PAGE_ID __page_id; // 大块内存起始页的页号 344 | size_t __n; // 页的数量 345 | // 双向链表结构 346 | span* __next; 347 | span* __prev; 348 | size_t __use_count; // 切成段小块内存,被分配给threadCache的计数器 349 | void* __free_list; // 切好的小块内存的自由链表 350 | }; 351 | ``` 352 | 353 | 然后就要手撕一个双链表了,十分简单,不多说了。这里面,每一个桶要维护一个锁! 354 | 355 | common.hpp 356 | ```cpp 357 | // 带头双向循环链表 358 | class span_list { 359 | private: 360 | span* __head = nullptr; 361 | std::mutex __bucket_mtx; 362 | public: 363 | span_list() { 364 | __head = new span; 365 | __head->__next = __head; 366 | __head->__prev = __head; 367 | } 368 | void insert(span* pos, span* new_span) { 369 | // 插入的是一个完好的span 370 | assert(pos); 371 | assert(new_span); 372 | span* prev = pos->__prev; 373 | prev->__next = new_span; 374 | new_span->__prev = prev; 375 | new_span->__next = pos; 376 | pos->__prev = new_span; 377 | } 378 | void erase(span* pos) { 379 | assert(pos); 380 | assert(pos != __head); 381 | span* prev = pos->__prev; 382 | span* next = pos->__next; 383 | prev->__next = next; 384 | next->__prev = prev; 385 | } 386 | }; 387 | ``` 388 | 389 | central_cache.hpp 390 | ```cpp 391 | #include "../common.hpp" 392 | 393 | class central_cache { 394 | private: 395 | span_list __span_lists[BUCKETS_NUM]; // 有多少个桶就多少个 396 | public: 397 | 398 | }; 399 | ``` 400 | 401 | 有多少个桶就有多少把锁! 402 | 403 | 404 | ## central_cache的核心逻辑 405 | 406 | **很明显这里是比较适合使用单例模式的。因为每个进程只需要维护一个central_cache。单例模式的详细说明可以见我的博客: [单例模式](https://blog.csdn.net/Yu_Cblog/article/details/131787131)** 407 | 408 | 409 | 然后这里我们用饿汉模式。 410 | 411 | ```cpp 412 | class central_cache { 413 | private: 414 | span_list __span_lists[BUCKETS_NUM]; // 有多少个桶就多少个 415 | private: 416 | static central_cache __s_inst; 417 | central_cache() = default; // 构造函数私有 418 | central_cache(const central_cache&) = delete; // 不允许拷贝 419 | public: 420 | central_cache* get_instance() { return &__s_inst; } 421 | public: 422 | }; 423 | ``` 424 | 425 | 426 | 然后threadCache找你要内存了,你给多少呢? 427 | 428 | 这里用了一个类似tcp的慢开始的反馈算法。我们可以把这个算法写到size_class里面去。 429 | 430 | common.hpp::size_class 431 | ```cpp 432 | // 一次threadCache从centralCache获取多少个内存 433 | static inline size_t num_move_size(size_t size) { 434 | if (size == 0) 435 | return 0; 436 | // [2, 512], 一次批量移动多少个对象的(慢启动)上限制 437 | // 小对象一次批量上限高 438 | // 大对象一次批量上限低 439 | int num = MAX_BYTES / size; 440 | if (num < 2) 441 | num = 2; 442 | if (num > 512) 443 | num = 512; 444 | return num; 445 | } 446 | ``` 447 | 448 | 用这个方法,可以告诉threadCache,本次要从centralCache获取多少内存。 449 | 450 | 然后为了控制慢开始,在free_list里面还需要控制一个max_size,然后这个字段递增,就能控制慢启动了。 451 | 452 | thread_cache.cc 453 | ```cpp 454 | void* thread_cache::fetch_from_central_cache(size_t index, size_t size) { 455 | // 慢开始反馈调节算法 456 | size_t batch_num = std::min(__free_lists[index].max_size(), size_class::num_move_size(size)); 457 | if (__free_lists[index].max_size() == batch_num) 458 | __free_lists[index].max_size() += 1; // 最多增长到512了 459 | // 1. 最开始一次向centralCache要太多,因为太多了可能用不完 460 | // 2. 如果你一直有这个桶size大小的内存,那么后面我可以给你越来越多,直到上限(size_class::num_move_size(size)) 461 | // 这个上限是根据这个桶的内存块大小size来决定的 462 | // 3. size越大,一次向centralcache要的就越小,如果size越小,相反。 463 | return nullptr; 464 | } 465 | ``` 466 | 467 | 468 | 然后去调用这个fetch_range_obj函数。 469 | 470 | 参数的意义:获取一段内存,从start到end个块,一共获取batch_num个,然后每一个块的大小是size,end-start应该等于batch_num。 471 | 472 | 返回值的意义:这里向central_cache中的span获取batch_num个,那么这个span一定有这么多个吗?不一定。span下如果不够,就全部给你。actual_n表示实际获取到了多少个。 1 <= actual_n <= batch_num。 473 | 474 | 475 | 476 | thread_cache.cc 477 | ```cpp 478 | void* thread_cache::fetch_from_central_cache(size_t index, size_t size) { 479 | // 慢开始反馈调节算法 480 | size_t batch_num = std::min(__free_lists[index].max_size(), size_class::num_move_size(size)); 481 | if (__free_lists[index].max_size() == batch_num) 482 | __free_lists[index].max_size() += 1; // 最多增长到512了 483 | // 1. 最开始一次向centralCache要太多,因为太多了可能用不完 484 | // 2. 如果你一直有这个桶size大小的内存,那么后面我可以给你越来越多,直到上限(size_class::num_move_size(size)) 485 | // 这个上限是根据这个桶的内存块大小size来决定的 486 | // 3. size越大,一次向centralcache要的就越小,如果size越小,相反。 487 | 488 | // 开始获取内存了 489 | void* start = nullptr; 490 | void* end = nullptr; 491 | size_t actual_n = central_cache::get_instance()->fetch_range_obj(start, end, batch_num, size); 492 | return nullptr; 493 | } 494 | ``` 495 | 496 | 然后我们获取到从cc(centralCache)里面的内存了,这里分两种情况: 497 | 498 | 1. cc只给了tc一个内存块(actual_n==1时), 此时直接返回就行了。此时thread_cache::allocate会直接把获取到的这一块交给用户,不用经过tc的哈希桶了。 499 | 2. 但是如果cc给我们的是一段(actual_n>=1),只需要给用户其中一块,其他的要插入到tc里面去!所以我们要给free_list提供一个插入一段(好几块size大小内存)的方法,也是头插就行了。 500 | 501 | 可以重载一下。 502 | 503 | common.hpp::free_list 504 | ```cpp 505 | void push(void* obj) { 506 | assert(obj); 507 | __next_obj(obj) = __free_list_ptr; 508 | __free_list_ptr = obj; 509 | } 510 | void push(void* start, void* end) { 511 | __next_obj(end) = __free_list_ptr; 512 | __free_list_ptr = start; 513 | } 514 | ``` 515 | 516 | thread_cache.cc 517 | ```cpp 518 | if (actual_n == 1) { 519 | assert(start == end); 520 | return start; 521 | } else { 522 | __free_lists[index].push(free_list::__next_obj(start), end); 523 | return start; 524 | } 525 | 526 | ``` 527 | 528 | 这里push的是start的下一个位置,start就不用经过tc了,start直接返回给用户,然后start+1到end位置的,插入到tc里面去。 529 | 530 | ## central_cache里面fetch_range_obj的逻辑 531 | 532 | ```cpp 533 | size_t central_cache::fetch_range_obj(void*& start, void*& end, size_t batch_num, size_t size) { 534 | size_t index = size_class::bucket_index(size); // 算出在哪个桶找 535 | 536 | } 537 | ``` 538 | 539 | 算出在哪桶里面找之后,就要分情况了。 540 | 541 | 首先,如果这个桶里面一个span都没挂,那就要找下一层了,找pc要。 542 | 543 | 如果有挂一些span,也要分情况。 544 | 545 | 我们要先找到一个非空的span。 546 | 547 | 所以写一个方法,不过这个方法可以后面再实现。 548 | 549 | 然后这里要注意一下。自由链表是单链表,如果我们取一段出来,最后要记得链表末尾给一个nullptr。 550 | 551 | **注意细节:** 552 | 1. 取batch_num个,end指针只需要走batch_num步(前提是span下面够这么多)! 553 | 2. 如果span下面不够,要特殊处理! 554 | 555 | 556 | ```cpp 557 | size_t central_cache::fetch_range_obj(void*& start, void*& end, size_t batch_num, size_t size) { 558 | size_t index = size_class::bucket_index(size); // 算出在哪个桶找 559 | __span_lists[index].__bucket_mtx.lock(); // 加锁(可以考虑RAII) 560 | span* cur_span = get_non_empty_span(__span_lists[index], size); // 找一个非空的span(有可能找不到) 561 | assert(cur_span); 562 | assert(cur_span->__free_list); // 这个非空的span一定下面挂着内存了,所以断言一下 563 | 564 | start = cur_span->__free_list; 565 | // 这里要画图理解一下 566 | end = start; 567 | // 开始指针遍历,从span中获取对象,如果不够,有多少拿多少 568 | size_t i = 0; 569 | size_t actual_n = 1; 570 | while (i < batch_num - 1 && free_list::__next_obj(end) != nullptr) { 571 | end = free_list::__next_obj(end); 572 | ++i; 573 | ++actual_n; 574 | } 575 | cur_span->__free_list = free_list::__next_obj(end); 576 | free_list::__next_obj(end) = nullptr; 577 | __span_lists[index].__bucket_mtx.unlock(); // 解锁 578 | return actual_n; 579 | } 580 | ``` 581 | 582 | 当然cc到这里还没有完全写完的,但是我们要继续先写pc,才能来完善这里的部分。 583 | 584 | ## page_cache整体框架 585 | 586 | ![](./assets/4.png) 587 | 588 | **申请内存:** 589 | 1. 当central cache向page cache申请内存时,page cache先检查对应位置有没有span,如果没有 则向更大页寻找一个span,如果找到则分裂成两个。比如:申请的是4页page,4页page后面没 有挂span,则向后面寻找更大的span,假设在10页page位置找到一个span,则将10页page span分裂为一个4页page span和一个6页page span。 590 | 2. 如果找到_spanList[128]都没有合适的span,则向系统使用mmap、brk或者是VirtualAlloc等方式 申请128页page span挂在自由链表中,再重复1中的过程。 591 | 3. 需要注意的是central cache和page cache 的核心结构都是spanlist的哈希桶,但是他们是有本质 区别的,central cache中哈希桶,是按跟thread cache一样的大小对齐关系映射的,他的spanlist 中挂的span中的内存都被按映射关系切好链接成小块内存的自由链表。而page cache 中的 spanlist则是按下标桶号映射的,也就是说第i号桶中挂的span都是i页内存。 592 | 593 | 594 | **释放内存:** 595 | 1. 如果central cache释放回一个span,则依次寻找span的前后page id的没有在使用的空闲span,看是否可以合并,如果合并继续向前寻找。这样就可以将切小的内存合并收缩成大的span,减少内存碎片。 596 | 597 | **这里的映射和之前的不一样,这里一共是128个桶,第一个是一页,第二个是两页!** 598 | 599 | **pc只关注cc要多少页!注意,单位是页!** 600 | 601 | page_cache.hpp 602 | ```cpp 603 | class page_cache { 604 | private: 605 | span_list __span_lists[PAGES_NUM]; 606 | std::mutex __page_mtx; 607 | static page_cache __s_inst; 608 | page_cache() = default; 609 | page_cache(const page_cache&) = delete; 610 | 611 | public: 612 | static page_cache* get_instance() { return &__s_inst; } 613 | public: 614 | // 获取一个K页的span 615 | }; 616 | ``` 617 | 618 | 也是要设计成单例模式。 619 | 620 | 然后怎么初始化呢? 621 | 622 | 一开始全部设置为空,然后向OS(heap)要128页的span,然后后面要(假设要两页),那就把这个128页的的切分成126页的和2页的。然后2页的给cc,126页的就挂到126的桶上。 623 | 624 | 然后当cc有内存不要的时候,就还到对应的span里面去。然后pc通过页号,查看前后相邻页是否空闲,是的话就合并,和病除更大的页,解决内存碎片问题。 625 | 626 | ## 获取span详解 627 | 628 | 我们要遍历cc中的span,我们可以在common.hpp里面写一些遍历链表的组建。 629 | 630 | common.hpp 631 | ```cpp 632 | public: 633 | // 遍历相关 634 | span* begin() { return __head->__next; } 635 | span* end() { return __head; } 636 | ``` 637 | 638 | central_cache.cc 639 | ```cpp 640 | span* central_cache::get_non_empty_span(span_list& list, size_t size) { 641 | // 先查看当前的spanlist中是否还有非空的span 642 | span* it = list.begin(); 643 | while (it != list.end()) { 644 | if (it->__free_list != nullptr) // 找到非空的了 645 | return it; 646 | it = it->__next; 647 | } 648 | //如果走到这里,说明没有空闲的span了,就要找pc了 649 | page_cache::get_instance()->new_span(); 650 | return nullptr; 651 | } 652 | ``` 653 | 654 | 问题是,要多少页呢?也是有一个计算方法的,放到size_class里面去! 655 | 656 | common.hpp 657 | ```cpp 658 | static inline size_t num_move_page(size_t size) { 659 | size_t num = num_move_size(size); 660 | size_t npage = num * size; 661 | npage >>= PAGE_SHIFT; // 相当于 /= 8kb 662 | if (npage == 0) 663 | npage = 1; 664 | return npage; 665 | } 666 | ``` 667 | 668 | 所以。 669 | central_cache.cc 670 | ```cpp 671 | span* central_cache::get_non_empty_span(span_list& list, size_t size) { 672 | // 先查看当前的spanlist中是否还有非空的span 673 | span* it = list.begin(); 674 | while (it != list.end()) { 675 | if (it->__free_list != nullptr) // 找到非空的了 676 | return it; 677 | it = it->__next; 678 | } 679 | //如果走到这里,说明没有空闲的span了,就要找pc了 680 | span* cur_span = page_cache::get_instance()->new_span(size_class::num_move_page(size)); 681 | // 切分的逻辑 682 | return nullptr; 683 | } 684 | ``` 685 | 686 | 下面就是切分的逻辑了。 687 | 688 | 怎么找到这个内存的地址呢? 689 | 690 | 如果页号是100。那么页的起始地址就是 `100 << PAGE_SHIFT`。 691 | 692 | central_cache.cc 693 | ```cpp 694 | span* central_cache::get_non_empty_span(span_list& list, size_t size) { 695 | // 先查看当前的spanlist中是否还有非空的span 696 | span* it = list.begin(); 697 | while (it != list.end()) { 698 | if (it->__free_list != nullptr) // 找到非空的了 699 | return it; 700 | it = it->__next; 701 | } 702 | // 如果走到这里,说明没有空闲的span了,就要找pc了 703 | span* cur_span = page_cache::get_instance()->new_span(size_class::num_move_page(size)); 704 | // 切分的逻辑 705 | // 1. 计算span的大块内存的起始地址和大块内存的大小(字节数) 706 | char* addr_start = (char*)(cur_span->__page_id << PAGE_SHIFT); 707 | size_t bytes = cur_span->__n << PAGE_SHIFT; // << PAGE_SHIFT 就是乘8kb的意思 708 | char* addr_end = addr_start + bytes; 709 | // 2. 把大块内存切成自由链表链接起来 710 | cur_span->__free_list = addr_start; // 先切一块下来做头 711 | addr_start += size; 712 | void* tail = cur_span->__free_list; 713 | while(addr_start < addr_end) { 714 | free_list::__next_obj(tail) = addr_start; 715 | tail = free_list::__next_obj(tail); 716 | addr_start += size; 717 | } 718 | list.push_front(cur_span); 719 | return cur_span; 720 | } 721 | ``` 722 | 723 | 注意,这里的切分是指把大块内存切成自由链表。不是pc里面的把大页切成小页。 724 | 725 | 然后写完上面那个,我们既要去写 `span* page_cache::new_span(size_t k)` 了。这里就要把大页切成小页了。 726 | 727 | page_cache.cc 728 | ```cpp 729 | // cc向pc获取k页的span 730 | span* page_cache::new_span(size_t k) { 731 | assert(k > 0 && k < PAGES_NUM); 732 | // 先检查第k个桶是否有span 733 | if (!__span_lists[k].empty()) 734 | return __span_lists->pop_front(); 735 | // 第k个桶是空的->去检查后面的桶里面有无span,如果有,可以把它进行切分 736 | for (size_t i = k + 1; i < PAGES_NUM; i++) { 737 | if (!__span_lists[i].empty()) { 738 | // 可以开始切了 739 | // 假设这个页是n页的,需要的是k页的 740 | // 1. 从__span_lists中拿下来 2. 切开 3. 一个返回给cc 4. 另一个挂到 n-k 号桶里面去 741 | span* n_span = __span_lists[i].pop_front(); 742 | span* k_span = new span; 743 | // 在n_span头部切除k页下来 744 | k_span->__page_id = n_span->__page_id; // <1> 745 | k_span->__n = k; // <2> 746 | n_span->__page_id += k; // <3> 747 | n_span->__n -= k; // <4> 748 | /** 749 | * 这里要好好理解一下 100 ------ 101 ------- 102 ------ 750 | * 假设n_span从100开始,大小是3 751 | * 切出来之后k_span就是从100开始了,所以<1> 752 | * 切出来之后k_span就有k页了,所以 <2> 753 | * 切出来之后n_span就是从102开始了,所以 <3> 754 | * 切出来之后n_span就变成__n-k页了,所以 <4> 755 | */ 756 | // 剩下的挂到相应位置 757 | __span_lists[n_span->__n].push_front(n_span); 758 | return k_span; 759 | } 760 | } 761 | // 走到这里,说明找不到span了:找os要 762 | span* big_span = new span; 763 | big_span = 764 | } 765 | ``` 766 | 767 | 这里切分的逻辑,代码注释里面写的很清楚了! 768 | 769 | 然后如果找到128页的都没找到,直接向系统申请! 770 | 771 | 这里要区分windows和linux。 772 | 773 | common.hpp 774 | ```cpp 775 | inline static void* system_alloc(size_t kpage) { 776 | void* ptr = nullptr; 777 | #if defined(_WIN32) || defined(_WIN64) 778 | #include 779 | *ptr = VirtualAlloc(0, kpage * (1 << 12), MEM_COMMIT | MEM_RESERVE, 780 | PAGE_READWRITE); 781 | #elif defined(__aarch64__) // ... 782 | #include 783 | void* ptr = mmap(NULL, kpage << 13, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 784 | #else 785 | #include 786 | std::cerr << "unknown system" << std::endl; 787 | throw std::bad_alloc(); 788 | #endif 789 | if (ptr == nullptr) 790 | throw std::bad_alloc(); 791 | return ptr; 792 | } 793 | ``` 794 | 795 | 然后new_span最后: 796 | 797 | ```cpp 798 | // 走到这里,说明找不到span了:找os要 799 | span* big_span = new span; 800 | void* ptr = system_alloc(PAGES_NUM - 1); 801 | big_span->__page_id = (PAGE_ID)ptr >> PAGE_SHIFT; 802 | big_span->__n = PAGES_NUM - 1; 803 | // 挂到上面去 804 | __span_lists[PAGES_NUM - 1].push_front(big_span); 805 | return new_span(k); 806 | ``` 807 | 808 | 插入之后,不要重复写切分的逻辑了,递归调用一次自己就行了! 809 | 810 | ### 关于 new_span 如何加锁的文字(重要/容易出bug) 811 | 812 | 然后这里还有最关键的一步。这里整个方法是要加锁的! 813 | 814 | 这里有个关键问题需要思考。 815 | 816 | get_non_empty_span是被fetch_range_obj调用的(在cc.cc)里面。 817 | 818 | 但是get_non_empty_span会去调用pc的new_span。 819 | 现在有个关键问题了,此时,如果我们不做处理,在pc的new_span里面,其实是有cc的桶锁的。 820 | 这个是很不好的。因为这个桶可能有内存需要释放啊!你锁住了,别人就进不去了。 821 | (其实这里我也是一知半解,要再去理解一下) 822 | 823 | 所以,在`span* central_cache::get_non_empty_span(span_list& list, size_t size) {`里面这个`span* cur_span = page_cache::get_instance()->new_span(size_class::num_move_page(size));`这句话前面。我们先把桶锁解掉。 824 | 825 | 然后pc的new_span的全局锁,我们在cc.cc里面的`span* central_cache::get_non_empty_span(span_list& list, size_t size) {` 这里加。 826 | 827 | cc.cc 828 | ```cpp 829 | // 如果走到这里,说明没有空闲的span了,就要找pc了 830 | page_cache::get_instance()->__page_mtx.lock(); 831 | span* cur_span = page_cache::get_instance()->new_span(size_class::num_move_page(size)); 832 | page_cache::get_instance()->__page_mtx.unlock(); 833 | ``` 834 | 835 | **现在问题来了,我们cc拿到这个新的span,后面还要切分的。刚刚在拿new_span之前解锁了,现在需要加上吗?** 836 | 837 | 不需要! 838 | 839 | 因为这个span是从pc拿来的,新的,也还没挂到cc上面去,所以别的线程拿不到这个span!所以不用加锁! 840 | 841 | 但是最后一步 `list.push_front(span)` 要访问cc对象了!就要加锁,我们把锁恢复一下。 842 | 843 | central_cache.cc 844 | ```cpp 845 | span* central_cache::get_non_empty_span(span_list& list, size_t size) { 846 | // 先查看当前的spanlist中是否还有非空的span 847 | span* it = list.begin(); 848 | while (it != list.end()) { 849 | if (it->__free_list != nullptr) // 找到非空的了 850 | return it; 851 | it = it->__next; 852 | } 853 | // 这里先解开桶锁 854 | list.__bucket_mtx.unlock(); 855 | 856 | // 如果走到这里,说明没有空闲的span了,就要找pc了 857 | page_cache::get_instance()->__page_mtx.lock(); 858 | span* cur_span = page_cache::get_instance()->new_span(size_class::num_move_page(size)); 859 | page_cache::get_instance()->__page_mtx.unlock(); 860 | 861 | // 切分的逻辑 862 | // 1. 计算span的大块内存的起始地址和大块内存的大小(字节数) 863 | char* addr_start = (char*)(cur_span->__page_id << PAGE_SHIFT); 864 | size_t bytes = cur_span->__n << PAGE_SHIFT; // << PAGE_SHIFT 就是乘8kb的意思 865 | char* addr_end = addr_start + bytes; 866 | // 2. 把大块内存切成自由链表链接起来 867 | cur_span->__free_list = addr_start; // 先切一块下来做头 868 | addr_start += size; 869 | void* tail = cur_span->__free_list; 870 | while(addr_start < addr_end) { 871 | free_list::__next_obj(tail) = addr_start; 872 | tail = free_list::__next_obj(tail); 873 | addr_start += size; 874 | } 875 | // 恢复锁 876 | list.__bucket_mtx.lock(); 877 | list.push_front(cur_span); 878 | return cur_span; 879 | } 880 | ``` 881 | 882 | ## 内存申请流程联调 883 | 884 | 先给每一步打上日志,看看调用的流程。 885 | 886 | 然后多次调用tcmalloc,看看日志。 887 | 888 | unit_test.cc 889 | ```cpp 890 | void test_alloc() { 891 | std::cout << "call tcmalloc(1)" << std::endl; 892 | void* ptr = tcmalloc(8 * 1024); 893 | std::cout << "call tcmalloc(2)" << std::endl; 894 | ptr = tcmalloc(10); 895 | std::cout << "call tcmalloc(3)" << std::endl; 896 | ptr = tcmalloc(2); 897 | std::cout << "call tcmalloc(4)" << std::endl; 898 | ptr = tcmalloc(1); 899 | std::cout << "call tcmalloc(5)" << std::endl; 900 | ptr = tcmalloc(1); 901 | std::cout << "call tcmalloc(6)" << std::endl; 902 | ptr = tcmalloc(5); 903 | std::cout << "call tcmalloc(7)" << std::endl; 904 | ptr = tcmalloc(1); 905 | } 906 | ``` 907 | 908 | 输出日志: 909 | ```bash 910 | call tcmalloc(1) 911 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 912 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 913 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 914 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 915 | [DEBUG][src/central_cache.cc][45] central_cache::get_non_empty_span() cannot find non-null span in cc, goto pc for mem 916 | [DEBUG][src/central_cache.cc][52] central_cache::get_non_empty_span() call page_cache::get_instance()->new_span() 917 | [DEBUG][src/page_cache.cc][43] page_cache::new_span() cannot find span, goto os for mem 918 | [DEBUG][src/page_cache.cc][37] page_cache::new_span() have span, return 919 | [DEBUG][src/central_cache.cc][58] central_cache::get_non_empty_span() get new span success 920 | [DEBUG][src/central_cache.cc][70] central_cache::get_non_empty_span() cut span 921 | [DEBUG][src/thread_cache.cc][47] actual_n:1 922 | call tcmalloc(2) 923 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 924 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 925 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 926 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 927 | [DEBUG][src/central_cache.cc][45] central_cache::get_non_empty_span() cannot find non-null span in cc, goto pc for mem 928 | [DEBUG][src/central_cache.cc][52] central_cache::get_non_empty_span() call page_cache::get_instance()->new_span() 929 | [DEBUG][src/page_cache.cc][37] page_cache::new_span() have span, return 930 | [DEBUG][src/central_cache.cc][58] central_cache::get_non_empty_span() get new span success 931 | [DEBUG][src/central_cache.cc][70] central_cache::get_non_empty_span() cut span 932 | [DEBUG][src/thread_cache.cc][47] actual_n:1 933 | call tcmalloc(3) 934 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 935 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 936 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 937 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 938 | [DEBUG][src/central_cache.cc][45] central_cache::get_non_empty_span() cannot find non-null span in cc, goto pc for mem 939 | [DEBUG][src/central_cache.cc][52] central_cache::get_non_empty_span() call page_cache::get_instance()->new_span() 940 | [DEBUG][src/page_cache.cc][37] page_cache::new_span() have span, return 941 | [DEBUG][src/central_cache.cc][58] central_cache::get_non_empty_span() get new span success 942 | [DEBUG][src/central_cache.cc][70] central_cache::get_non_empty_span() cut span 943 | [DEBUG][src/thread_cache.cc][47] actual_n:1 944 | call tcmalloc(4) 945 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 946 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 947 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 948 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 949 | [DEBUG][src/thread_cache.cc][47] actual_n:2 950 | call tcmalloc(5) 951 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 952 | call tcmalloc(6) 953 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 954 | [DEBUG][src/thread_cache.cc][16] thread_cache::allocate call thread_cache::fetch_from_central_cache 955 | [DEBUG][src/thread_cache.cc][43] thread_cache::fetch_from_central_cache call central_cache::get_instance()->fetch_range_obj() 956 | [DEBUG][src/central_cache.cc][12] central_cache::fetch_range_obj() call central_cache::get_non_empty_span() 957 | [DEBUG][src/thread_cache.cc][47] actual_n:3 958 | call tcmalloc(7) 959 | [DEBUG][./include/tcmalloc.hpp][14] tcmalloc find tc from mem 960 | ``` 961 | 962 | 963 | 同样,再测一次。 964 | 965 | ```cpp 966 | void test_alloc2() { 967 | for (size_t i = 0; i < 1024; ++i) { 968 | void* p1 = tcmalloc(6); 969 | } 970 | void* p2 = tcmalloc(6); // 这一次一定会找新的span 971 | } 972 | ``` 973 | 974 | 如果申请1024次6字节(对齐后为8字节),第1025次申请,一定会向系统申请新的span了,之前都不需要的!所以预期输出只有两个`goto os for mem`。 975 | 976 | 输出日志放在了 `./test/test1.log` 。 977 | 978 | 979 | ## thread_cache内存释放 980 | 981 | 当链表长度大于一次批量申请的内存的时候,就开始还一段list给cc 982 | 983 | thread_cache.cc 984 | ```cpp 985 | void thread_cache::deallocate(void* ptr, size_t size) { 986 | assert(ptr); 987 | assert(size <= MAX_BYTES); 988 | size_t index = size_class::bucket_index(size); 989 | __free_lists[index].push(ptr); 990 | // 当链表长度大于一次批量申请的内存的时候,就开始还一段list给cc 991 | if (__free_lists[index].size() >= __free_lists[index].max_size()) { 992 | list_too_long(__free_lists[index], size); 993 | } 994 | } 995 | ``` 996 | 997 | thread_cache.cc 998 | ```cpp 999 | void thread_cache::list_too_long(free_list& list, size_t size) { 1000 | void* start = nullptr; 1001 | void* end = nullptr; 1002 | list.pop(start, end, list.max_size()); 1003 | central_cache::get_instance()->release_list_to_spans(start, size); 1004 | } 1005 | ``` 1006 | 1007 | tcmalloc的规则更复杂,可能还会控制内存大小,超过...就会释放等。 1008 | 1009 | 1010 | ## central_cache内存释放 1011 | 1012 | ```cpp 1013 | void central_cache::release_list_to_spans(void* start, size_t size) { 1014 | size_t index = size_class::bucket_index(size); // 先算一下在哪一个桶里面 1015 | __span_lists[index].__bucket_mtx.lock(); 1016 | // 这里要注意,一个桶挂了多个span,这些内存块挂到哪一个span是不确定的 1017 | 1018 | __span_lists[index].__bucket_mtx.unlock(); 1019 | } 1020 | ``` 1021 | 1022 | **这里的问题是:如何确定每一块内存块应该到哪一个span里面去。** 1023 | 1024 | 1025 | 现在要判断,这些内存块,是来自哪个span的,然后span是从page切出来的,page是有地址的,span也是有地址的。 1026 | 1027 | 所以最好在page里面的时候,先让pageid和span的地址映射起来先。 1028 | 1029 | 在pc.hpp里面增加。 1030 | ```cpp 1031 | std::unordered_map __id_span_map; 1032 | ``` 1033 | 1034 | **然后在new_span里面,把新的span分给cc的时候,记录一下映射。** 1035 | 1036 | 然后pc里面提供一个方法,获取对象到span映射。 1037 | 1038 | page_cache.cc 1039 | ```cpp 1040 | span* page_cache::map_obj_to_span(void* obj) { 1041 | // 先把页号算出来 1042 | PAGE_ID id = (PAGE_ID)obj >> PAGE_SHIFT; // 这个理论推导可以自行推导一下 1043 | auto ret = __id_span_map.find(id); 1044 | if (ret != __id_span_map.end()) 1045 | return ret->second; 1046 | LOG(FATAL); 1047 | assert(false); 1048 | return nullptr; 1049 | } 1050 | ``` 1051 | 1052 | 此时就可以通过一个对象,获取到对应是哪一个span了。 1053 | 1054 | 此时就可以继续写`release_list_to_spans`了。 1055 | 1056 | ```cpp 1057 | void central_cache::release_list_to_spans(void* start, size_t size) { 1058 | size_t index = size_class::bucket_index(size); // 先算一下在哪一个桶里面 1059 | __span_lists[index].__bucket_mtx.lock(); 1060 | // 这里要注意,一个桶挂了多个span,这些内存块挂到哪一个span是不确定的 1061 | while (start) { 1062 | // 遍历这个链表 1063 | void* next = free_list::__next_obj(start); // 先记录一下下一个,避免等下找不到了 1064 | span* cur_span = page_cache::get_instance()->map_obj_to_span(start); 1065 | free_list::__next_obj(start) = cur_span->__free_list; 1066 | cur_span->__free_list = start; 1067 | // 处理usecount 1068 | cur_span->__use_count--; 1069 | if (cur_span->__use_count == 0) { 1070 | // 说明这个span切分出去的所有小块都回来了 1071 | // 归还给pagecache 1072 | // 1. 把这一页从cc的这个桶的spanlist中拿掉 1073 | __span_lists[index].erase(cur_span); // 从桶里面拿走 1074 | // 2. 此时不用管这个span的freelist了,因为这些内存本来就是span初始地址后面的,然后顺序也是乱的,直接置空即可 1075 | // (这里还不太理解) 1076 | cur_span->__free_list = nullptr; 1077 | cur_span->__next = cur_span->__prev = nullptr; 1078 | // 页号,页数是不能动的! 1079 | // 3. 解开桶锁 1080 | __span_lists[index].__bucket_mtx.unlock(); 1081 | // 4. 还给pc 1082 | page_cache::get_instance()->__page_mtx.lock(); 1083 | page_cache::get_instance()->release_span_to_page(cur_span); 1084 | page_cache::get_instance()->__page_mtx.unlock(); 1085 | // 5. 恢复桶锁 1086 | __span_lists[index].__bucket_mtx.lock(); 1087 | } 1088 | start = next; 1089 | } 1090 | __span_lists[index].__bucket_mtx.unlock(); 1091 | } 1092 | ``` 1093 | 1094 | 细节在注释里面写的很清楚了。 1095 | 1096 | 要注意,调用pc的接口的时候,就记得把桶锁解掉。 1097 | 1098 | ## page_cache内存释放 1099 | 1100 | 就是这个函数。 1101 | 1102 | ```cpp 1103 | void page_cache::release_span_to_page(span* s) { 1104 | // 对span前后对页尝试进行合并,缓解内存碎片问题 1105 | } 1106 | ``` 1107 | 1108 | 然后刚才的map可以帮助我们查找前后的page。 1109 | 1110 | 然后我们前后找的时候,要区分这个页是不是在centralCache上的,如果在cc上,那就不能合并。 1111 | 1112 | 然后这个判断不能用use_count==0这个判断条件。有可能这个span刚从pc拿过来,还没给别人的时候,use_count就是0,这个span,pc是不能回收合并的。 1113 | 1114 | 所以可以给span添加一个参数is_use就行了。 1115 | 1116 | ```cpp 1117 | // 管理大块内存 1118 | class span { 1119 | public: 1120 | PAGE_ID __page_id; // 大块内存起始页的页号 1121 | size_t __n = 0; // 页的数量 1122 | // 双向链表结构 1123 | span* __next = nullptr; 1124 | span* __prev = nullptr; 1125 | size_t __use_count = 0; // 切成段小块内存,被分配给threadCache的计数器 1126 | void* __free_list = nullptr; // 切好的小块内存的自由链表 1127 | bool is_use = false; // 是否在被使用 1128 | }; 1129 | ``` 1130 | 1131 | 然后cc.cc这里改一下,拿到之后改成true就行。 1132 | 1133 | cc.cc 1134 | ```cpp 1135 | page_cache::get_instance()->__page_mtx.lock(); 1136 | span* cur_span = page_cache::get_instance()->new_span(size_class::num_move_page(size)); 1137 | cur_span->is_use = true; // 表示已经被使用 1138 | page_cache::get_instance()->__page_mtx.unlock(); 1139 | ``` 1140 | 1141 | 1142 | 然后继续写这个逻辑: 1143 | 1144 | page_cache.cc 1145 | ```cpp 1146 | void page_cache::release_span_to_page(span* s) { 1147 | // 对span前后对页尝试进行合并,缓解内存碎片问题 1148 | PAGE_ID prev_id = s->__page_id - 1; // 前一块span的id一定是当前span的id-1 1149 | // 拿到id如何找span: 之前写好的map能拿到吗? 1150 | } 1151 | ``` 1152 | 1153 | 现在的问题是,之前的map能拿到吗?还拿不到,因为我们之前的map只记录了分给cc的span的映射,没有存留在pc那些,没有分出去的映射。 1154 | 所以我们要在`span* page_cache::new_span(size_t k) {`里面添加一下,留在pagecache那些块的映射。 1155 | 1156 | ```cpp 1157 | // 存储n_span的首尾页号跟n_span的映射,方便pc回收内存时进行合并查找 1158 | __id_span_map[n_span->__page_id] = n_span; 1159 | __id_span_map[n_span->__page_id + n_span->__n - 1] = n_span; 1160 | ``` 1161 | 1162 | 为什么这里不用循环存储呢? 1163 | 1164 | 因为这里的pc的内存只是被span挂起来啊,不会被切啊,所以知道地址就了啊! 1165 | 给cc的那些,会被切开变成很多固定大小的内存块啊!所以这里不用循环存。 1166 | 1167 | ## 大于256k的情况 1168 | 1169 | 1. <=256kb -> 按照前面三层缓存的情况进行操作 1170 | 2. \>256kb的情况 1171 | a. 128\*8k > size > 32\*8k这个情况: 还可以找pagecache 1172 | b. 否则直接找系统 1173 | 1174 | 然后这一部分就是有多处要改,不过都很简单很容易找到,大家可以直接看代码。处理完之后,测试一下申请大内存就行。 1175 | 1176 | 1177 | ## 处理代码中`new`的问题 1178 | 1179 | 代码中有些地方用了`new span`。这个就很不对。我们弄这个tcmalloc是用来替代malloc的,既然是替代,那我们的代码里面怎么能有`new`,`new`也是调用`malloc`的,所以我们要改一下。 1180 | 1181 | 然后之前是写了一个定长内存池的,可以用来代替new。 1182 | 1183 | **博客地址:[内存池是什么原理?|内存池简易模拟实现|为学习高并发内存池tcmalloc做准备](https://blog.csdn.net/Yu_Cblog/article/details/131741601)** 1184 | 1185 | page_cache.hpp 1186 | ```cpp 1187 | class page_cache { 1188 | private: 1189 | span_list __span_lists[PAGES_NUM]; 1190 | static page_cache __s_inst; 1191 | page_cache() = default; 1192 | page_cache(const page_cache&) = delete; 1193 | std::unordered_map __id_span_map; 1194 | object_pool __span_pool; 1195 | ``` 1196 | 多加一个`object_pool __span_pool;`对象。 1197 | 1198 | 然后,`new span`的地方都替换掉。`delete`的地方也换掉就行。 1199 | 1200 | 然后这里面也改一下。 1201 | 1202 | tcmalloc.hpp 1203 | ```cpp 1204 | static void* tcmalloc(size_t size) { 1205 | if (size > MAX_BYTES) { 1206 | // 处理申请大内存的情况 1207 | size_t align_size = size_class::round_up(size); 1208 | size_t k_page = align_size >> PAGE_SHIFT; 1209 | page_cache::get_instance()->__page_mtx.lock(); 1210 | span* cur_span = page_cache::get_instance()->new_span(k_page); // 直接找pc 1211 | page_cache::get_instance()->__page_mtx.unlock(); 1212 | void* ptr = (void*)(cur_span->__page_id << PAGE_SHIFT); // span转化成地址 1213 | return ptr; 1214 | } 1215 | if (p_tls_thread_cache == nullptr) { 1216 | // 相当于单例 1217 | // p_tls_thread_cache = new thread_cache; 1218 | static object_pool tc_pool; 1219 | p_tls_thread_cache = tc_pool.new_(); 1220 | } 1221 | #ifdef PROJECT_DEBUG 1222 | LOG(DEBUG) << "tcmalloc find tc from mem" << std::endl; 1223 | #endif 1224 | return p_tls_thread_cache->allocate(size); 1225 | } 1226 | ``` 1227 | 1228 | ## 解决free,使其不用传大小 1229 | 1230 | 因为我们已经有页号到span的映射了。所以我们在span里面增加一个字段,obj_size就行。 1231 | 1232 | ## 多线程场景下深度测试 1233 | 1234 | **首先要明确一点,我们不是去造一个轮子,我们要和malloc对比,不是说要比malloc快多少,因为我们在很多细节上,和tcmalloc差的还是很远的。** 1235 | 1236 | 测试代码可以见bench\_mark.cc。 1237 | 1238 | 结果 1239 | ```bash 1240 | parallels@ubuntu-linux-22-04-desktop:~/Project/Google-tcmalloc-simulation-implementation$ ./out 1241 | ========================================================== 1242 | 4个线程并发执行10轮次,每轮次concurrent alloc 1000次: 花费:27877 ms 1243 | 4个线程并发执行10轮次,每轮次concurrent dealloc 1000次: 花费:52190 ms 1244 | 4个线程并发concurrent alloc&dealloc 40000次,总计花费:80067 ms 1245 | 1246 | 1247 | 4个线程并发执行10次,每轮次malloc 1000次: 花费:2227ms 1248 | 4个线程并发执行10轮次,每轮次free 1000次: 花费:1385 ms 1249 | 4个线程并发malloc&free 40000次,总计花费:3612 ms 1250 | ========================================================== 1251 | parallels@ubuntu-linux-22-04-desktop:~/Project/Google-tcmalloc-simulation-implementation$ 1252 | ``` 1253 | 1254 | 比malloc差。 1255 | 1256 | ## 分析性能瓶颈 1257 | 1258 | linux和windows(VS STUDIO)下都有很多性能分析的工具,可以检测哪里调用的时间多。 1259 | 1260 | 在这里直接出结论:锁用了很多时间。 1261 | 1262 | 可以用基数树进行优化。 1263 | 1264 | ## 用Radix Tree进行优化 1265 | 1266 | radix tree 我们可以直接用tcmalloc源码里面的。`page_map.hpp`。 1267 | 1268 | --------------------------------------------------------------------------------