1/***************************************************************************** 2 3 Licensed to Accellera Systems Initiative Inc. (Accellera) under one or 4 more contributor license agreements. See the NOTICE file distributed 5 with this work for additional information regarding copyright ownership. 6 Accellera licenses this file to you under the Apache License, Version 2.0 7 (the "License"); you may not use this file except in compliance with the 8 License. You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12 Unless required by applicable law or agreed to in writing, software 13 distributed under the License is distributed on an "AS IS" BASIS, 14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 15 implied. See the License for the specific language governing 16 permissions and limitations under the License. 17 18 *****************************************************************************/ 19 20/***************************************************************************** 21 22 sc_mempool.cpp - Memory pools for small objects. 23 24 Original Author: Stan Y. Liao, Synopsys, Inc. 25 26 CHANGE LOG AT END OF FILE 27 *****************************************************************************/ 28 29 30 31 32// <sc_mempool> is a class that manages the memory for small objects, 33// of sizes <increment>, 2 * <increment>, ..., <num_pools> * 34// <increment>. When a memory request of <k> bytes is made through 35// the memory pool, the smallest pool <j> such that <j> * <increment> 36// >= <k> is used. The default values of <increment> and <num_pools> 37// are 8 and 8, respectively. Each pool has an allocator, that 38// simply keeps a free list of cells, and allocate new blocks 39// whenever necessary. We are relying on malloc() to return a 40// properly aligned memory blocks. Note that the memory blocks 41// allocated by the mempool are never freed. Thus, if purify is 42// used, we may get MIU (memory-in-use) warnings. To disable this, 43// set the environment variable SYSTEMC_MEMPOOL_DONT_USE to 1. 44 45 46static const char* dont_use_envstring = "SYSTEMC_MEMPOOL_DONT_USE"; 47static bool use_default_new = false; 48 49 50#include <stdio.h> 51#include <stdlib.h> // duplicate (c)stdlib.h headers for Solaris 52#include <cstdlib> 53#include "sysc/utils/sc_mempool.h" 54 55namespace sc_core { 56 57// An allocator is one that handles a particular size. It keeps a 58// <free_list> from which a cell may be allocated quickly if there 59// is one available. If no cell is available from <free_list>, then 60// the allocator tries to find whether space is available from the 61// most-recently-allocated block, as pointed to by <next_avail>. If 62// so, then the cell pointed to by <next_avail> is returned, while 63// <next_avail> is advanced. If <next_avail> now points beyond 64// the current block, then it's reset to 0. On the other hand, 65// if <next_avail> was 0 when a request to the block is made, then 66// a new block is allocated by calling system malloc(), and the new 67// block becomes the head of <block_list>. 68 69 70class sc_allocator { 71 friend class sc_mempool; 72 73public: 74 sc_allocator( int blksz, int cellsz ); 75 ~sc_allocator(); 76 void* allocate(); 77 void release(void* p); 78 79 void display_statistics(); 80 81private: 82 union link { 83 link* next; 84 double align; // alignment required. 85 }; 86 87 int block_size; // size of each block in bytes, 88 // including the link 89 int cell_size; // size of each cell in bytes 90 91 char* block_list; 92 link* free_list; 93 char* next_avail; 94 95 int total_alloc; 96 int total_freed; 97 int free_list_alloc; 98}; 99 100sc_allocator::sc_allocator( int blksz, int cellsz ) 101 : block_size(sizeof(link) + (((blksz - 1) / cellsz) + 1) * cellsz), 102 cell_size(cellsz), block_list(0), free_list(0), next_avail(0), 103 total_alloc(0), total_freed(0), free_list_alloc(0) 104{} 105 106sc_allocator::~sc_allocator() 107{ 108 // Shouldn't free the block_list, since global objects that use 109 // the memory pool may not have been destroyed yet ... 110 // Let it leak, let it leak, let it leak ... 111} 112 113void* 114sc_allocator::allocate() 115{ 116 void* result = 0; 117 total_alloc++; 118 if (free_list != 0) { 119 free_list_alloc++; 120 result = free_list; 121 free_list = free_list->next; 122 return result; 123 } 124 else if (next_avail != 0) { 125 result = next_avail; 126 next_avail += cell_size; 127 // next_avail goes beyond the block 128 if (next_avail >= block_list + block_size) 129 next_avail = 0; 130 return result; 131 } 132 else { // (next_avail == 0) 133 link* new_block = (link*) malloc(block_size); // need alignment? 134 new_block->next = (link*) block_list; 135 block_list = (char*) new_block; 136 result = (block_list + sizeof(link)); 137 // Assume that the block will hold more than one cell ... why 138 // wouldn't it? 139 next_avail = ((char*) result) + cell_size; 140 return result; 141 } 142} 143 144void 145sc_allocator::release(void* p) 146{ 147 total_freed++; 148 ((link*) p)->next = free_list; 149 free_list = (link*) p; 150} 151 152void 153sc_allocator::display_statistics() 154{ 155 int nblocks = 0; 156 for (link* b = (link*) block_list; b != 0; b = b->next) 157 nblocks++; 158 printf("size %3d: %2d block(s), %3d requests (%3d from free list), %3d freed.\n", 159 cell_size, nblocks, total_alloc, free_list_alloc, total_freed); 160} 161 162 163static const int cell_sizes[] = { 164/* 0 */ 0, 165/* 1 */ 8, 166/* 2 */ 16, 167/* 3 */ 24, 168/* 4 */ 32, 169/* 5 */ 48, 170/* 6 */ 64, 171/* 7 */ 80, 172/* 8 */ 96, 173/* 9 */ 128 174}; 175 176static const int cell_size_to_allocator[] = { 177/* 0 */ 0, 178/* 1 */ 1, 179/* 2 */ 2, 180/* 3 */ 3, 181/* 4 */ 4, 182/* 5 */ 5, 183/* 6 */ 5, 184/* 7 */ 6, 185/* 8 */ 6, 186/* 9 */ 7, 187/* 10 */ 7, 188/* 11 */ 8, 189/* 12 */ 8, 190/* 13 */ 9, 191/* 14 */ 9, 192/* 15 */ 9, 193/* 16 */ 9 194}; 195 196 197class sc_mempool_int { 198 friend class sc_mempool; 199 200public: 201 sc_mempool_int(int blksz, int npools, int incr); 202 ~sc_mempool_int(); 203 void* do_allocate(std::size_t); 204 void do_release(void*, std::size_t); 205 206 void display_statistics(); 207 208private: 209 sc_allocator** allocators; 210 int num_pools; 211 int increment; 212 int max_size; 213}; 214 215 216static bool 217compute_use_default_new() 218{ 219 const char* e = getenv(dont_use_envstring); 220 return (e != 0) && (atoi(e) != 0); 221} 222 223sc_mempool_int::sc_mempool_int(int blksz, int npools, int incr) : 224 allocators(0), num_pools(0), increment(0), max_size(0) 225{ 226 use_default_new = compute_use_default_new(); 227 if (! use_default_new) { 228 num_pools = npools; 229 increment = incr; 230 max_size = cell_sizes[sizeof(cell_sizes)/sizeof(cell_sizes[0]) - 1]; 231 allocators = new sc_allocator*[npools + 1]; 232 for (int i = 1; i <= npools; ++i) 233 allocators[i] = new sc_allocator(blksz, cell_sizes[i]); 234 allocators[0] = allocators[1]; 235 } 236} 237 238sc_mempool_int::~sc_mempool_int() 239{ 240 for (int i = 1; i <= num_pools; ++i) 241 delete allocators[i]; 242 delete[] allocators; 243} 244 245static sc_mempool_int* the_mempool = 0; 246 247void* 248sc_mempool_int::do_allocate(std::size_t sz) 249{ 250 int which_allocator = cell_size_to_allocator[(sz - 1) / increment + 1]; 251 void* p = allocators[which_allocator]->allocate(); 252 return p; 253} 254 255void 256sc_mempool_int::do_release(void* p, std::size_t sz) 257{ 258 int which_allocator = cell_size_to_allocator[(sz - 1) / increment + 1]; 259 allocators[which_allocator]->release(p); 260} 261 262void 263sc_mempool_int::display_statistics() 264{ 265 printf("*** Memory Pool Statistics ***\n"); 266 for (int i = 1; i <= num_pools; ++i) 267 allocators[i]->display_statistics(); 268} 269 270/****************************************************************************/ 271 272void* 273sc_mempool::allocate(std::size_t sz) 274{ 275 if (use_default_new) 276 return ::operator new(sz); 277 278 if (the_mempool == 0) { 279 use_default_new = compute_use_default_new(); 280 if (use_default_new) 281 return ::operator new(sz); 282 283 // Note that the_mempool is never freed. This is going to cause 284 // memory leaks when the program exits. 285 the_mempool = new sc_mempool_int( 1984, sizeof(cell_sizes)/sizeof(cell_sizes[0]) - 1, 8 ); 286 } 287 288 if (sz > (unsigned) the_mempool->max_size) 289 return ::operator new(sz); 290 291 return the_mempool->do_allocate(sz); 292} 293 294void 295sc_mempool::release(void* p, std::size_t sz) 296{ 297 if (p) { 298 299 if (use_default_new || sz > (unsigned) the_mempool->max_size) { 300 ::operator delete(p); 301 return; 302 } 303 304 the_mempool->do_release(p, sz); 305 } 306} 307 308void 309sc_mempool::display_statistics() 310{ 311 if (the_mempool && !use_default_new) { 312 the_mempool->display_statistics(); 313 } else { 314 printf("SystemC info: no memory allocation was done through the memory pool.\n"); 315 } 316} 317 318} // namespace sc_core 319 320// $Log: sc_mempool.cpp,v $ 321// Revision 1.4 2011/08/26 20:46:18 acg 322// Andy Goodrich: moved the modification log to the end of the file to 323// eliminate source line number skew when check-ins are done. 324// 325// Revision 1.3 2011/08/24 22:05:56 acg 326// Torsten Maehne: initialization changes to remove warnings. 327// 328// Revision 1.2 2011/02/18 20:38:44 acg 329// Andy Goodrich: Updated Copyright notice. 330// 331// Revision 1.1.1.1 2006/12/15 20:20:06 acg 332// SystemC 2.3 333// 334// Revision 1.3 2006/01/13 18:53:10 acg 335// Andy Goodrich: Added $Log command so that CVS comments are reproduced in 336// the source. 337 338// taf 339