| /***************************************************************************** | |
| Licensed to Accellera Systems Initiative Inc. (Accellera) under one or | |
| more contributor license agreements. See the NOTICE file distributed | |
| with this work for additional information regarding copyright ownership. | |
| Accellera licenses this file to you under the Apache License, Version 2.0 | |
| (the "License"); you may not use this file except in compliance with the | |
| License. You may obtain a copy of the License at | |
| http://www.apache.org/licenses/LICENSE-2.0 | |
| Unless required by applicable law or agreed to in writing, software | |
| distributed under the License is distributed on an "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| implied. See the License for the specific language governing | |
| permissions and limitations under the License. | |
| *****************************************************************************/ | |
| /***************************************************************************** | |
| sc_mempool.cpp - Memory pools for small objects. | |
| Original Author: Stan Y. Liao, Synopsys, Inc. | |
| CHANGE LOG AT END OF FILE | |
| *****************************************************************************/ | |
| // <sc_mempool> is a class that manages the memory for small objects, | |
| // of sizes <increment>, 2 * <increment>, ..., <num_pools> * | |
| // <increment>. When a memory request of <k> bytes is made through | |
| // the memory pool, the smallest pool <j> such that <j> * <increment> | |
| // >= <k> is used. The default values of <increment> and <num_pools> | |
| // are 8 and 8, respectively. Each pool has an allocator, that | |
| // simply keeps a free list of cells, and allocate new blocks | |
| // whenever necessary. We are relying on malloc() to return a | |
| // properly aligned memory blocks. Note that the memory blocks | |
| // allocated by the mempool are never freed. Thus, if purify is | |
| // used, we may get MIU (memory-in-use) warnings. To disable this, | |
| // set the environment variable SYSTEMC_MEMPOOL_DONT_USE to 1. | |
| static const char* dont_use_envstring = "SYSTEMC_MEMPOOL_DONT_USE"; | |
| static bool use_default_new = false; | |
| #include <stdio.h> | |
| #include <stdlib.h> // duplicate (c)stdlib.h headers for Solaris | |
| #include <cstdlib> | |
| #include "sysc/utils/sc_mempool.h" | |
| namespace sc_core { | |
| // An allocator is one that handles a particular size. It keeps a | |
| // <free_list> from which a cell may be allocated quickly if there | |
| // is one available. If no cell is available from <free_list>, then | |
| // the allocator tries to find whether space is available from the | |
| // most-recently-allocated block, as pointed to by <next_avail>. If | |
| // so, then the cell pointed to by <next_avail> is returned, while | |
| // <next_avail> is advanced. If <next_avail> now points beyond | |
| // the current block, then it's reset to 0. On the other hand, | |
| // if <next_avail> was 0 when a request to the block is made, then | |
| // a new block is allocated by calling system malloc(), and the new | |
| // block becomes the head of <block_list>. | |
| class sc_allocator { | |
| friend class sc_mempool; | |
| public: | |
| sc_allocator( int blksz, int cellsz ); | |
| ~sc_allocator(); | |
| void* allocate(); | |
| void release(void* p); | |
| void display_statistics(); | |
| private: | |
| union link { | |
| link* next; | |
| double align; // alignment required. | |
| }; | |
| int block_size; // size of each block in bytes, | |
| // including the link | |
| int cell_size; // size of each cell in bytes | |
| char* block_list; | |
| link* free_list; | |
| char* next_avail; | |
| int total_alloc; | |
| int total_freed; | |
| int free_list_alloc; | |
| }; | |
| sc_allocator::sc_allocator( int blksz, int cellsz ) | |
| : block_size(sizeof(link) + (((blksz - 1) / cellsz) + 1) * cellsz), | |
| cell_size(cellsz), block_list(0), free_list(0), next_avail(0), | |
| total_alloc(0), total_freed(0), free_list_alloc(0) | |
| {} | |
| sc_allocator::~sc_allocator() | |
| { | |
| // Shouldn't free the block_list, since global objects that use | |
| // the memory pool may not have been destroyed yet ... | |
| // Let it leak, let it leak, let it leak ... | |
| } | |
| void* | |
| sc_allocator::allocate() | |
| { | |
| void* result = 0; | |
| total_alloc++; | |
| if (free_list != 0) { | |
| free_list_alloc++; | |
| result = free_list; | |
| free_list = free_list->next; | |
| return result; | |
| } | |
| else if (next_avail != 0) { | |
| result = next_avail; | |
| next_avail += cell_size; | |
| // next_avail goes beyond the block | |
| if (next_avail >= block_list + block_size) | |
| next_avail = 0; | |
| return result; | |
| } | |
| else { // (next_avail == 0) | |
| link* new_block = (link*) malloc(block_size); // need alignment? | |
| new_block->next = (link*) block_list; | |
| block_list = (char*) new_block; | |
| result = (block_list + sizeof(link)); | |
| // Assume that the block will hold more than one cell ... why | |
| // wouldn't it? | |
| next_avail = ((char*) result) + cell_size; | |
| return result; | |
| } | |
| } | |
| void | |
| sc_allocator::release(void* p) | |
| { | |
| total_freed++; | |
| ((link*) p)->next = free_list; | |
| free_list = (link*) p; | |
| } | |
| void | |
| sc_allocator::display_statistics() | |
| { | |
| int nblocks = 0; | |
| for (link* b = (link*) block_list; b != 0; b = b->next) | |
| nblocks++; | |
| printf("size %3d: %2d block(s), %3d requests (%3d from free list), %3d freed.\n", | |
| cell_size, nblocks, total_alloc, free_list_alloc, total_freed); | |
| } | |
| static const int cell_sizes[] = { | |
| /* 0 */ 0, | |
| /* 1 */ 8, | |
| /* 2 */ 16, | |
| /* 3 */ 24, | |
| /* 4 */ 32, | |
| /* 5 */ 48, | |
| /* 6 */ 64, | |
| /* 7 */ 80, | |
| /* 8 */ 96, | |
| /* 9 */ 128 | |
| }; | |
| static const int cell_size_to_allocator[] = { | |
| /* 0 */ 0, | |
| /* 1 */ 1, | |
| /* 2 */ 2, | |
| /* 3 */ 3, | |
| /* 4 */ 4, | |
| /* 5 */ 5, | |
| /* 6 */ 5, | |
| /* 7 */ 6, | |
| /* 8 */ 6, | |
| /* 9 */ 7, | |
| /* 10 */ 7, | |
| /* 11 */ 8, | |
| /* 12 */ 8, | |
| /* 13 */ 9, | |
| /* 14 */ 9, | |
| /* 15 */ 9, | |
| /* 16 */ 9 | |
| }; | |
| class sc_mempool_int { | |
| friend class sc_mempool; | |
| public: | |
| sc_mempool_int(int blksz, int npools, int incr); | |
| ~sc_mempool_int(); | |
| void* do_allocate(std::size_t); | |
| void do_release(void*, std::size_t); | |
| void display_statistics(); | |
| private: | |
| sc_allocator** allocators; | |
| int num_pools; | |
| int increment; | |
| int max_size; | |
| }; | |
| static bool | |
| compute_use_default_new() | |
| { | |
| const char* e = getenv(dont_use_envstring); | |
| return (e != 0) && (atoi(e) != 0); | |
| } | |
| sc_mempool_int::sc_mempool_int(int blksz, int npools, int incr) : | |
| allocators(0), num_pools(0), increment(0), max_size(0) | |
| { | |
| use_default_new = compute_use_default_new(); | |
| if (! use_default_new) { | |
| num_pools = npools; | |
| increment = incr; | |
| max_size = cell_sizes[sizeof(cell_sizes)/sizeof(cell_sizes[0]) - 1]; | |
| allocators = new sc_allocator*[npools + 1]; | |
| for (int i = 1; i <= npools; ++i) | |
| allocators[i] = new sc_allocator(blksz, cell_sizes[i]); | |
| allocators[0] = allocators[1]; | |
| } | |
| } | |
| sc_mempool_int::~sc_mempool_int() | |
| { | |
| for (int i = 1; i <= num_pools; ++i) | |
| delete allocators[i]; | |
| delete[] allocators; | |
| } | |
| static sc_mempool_int* the_mempool = 0; | |
| void* | |
| sc_mempool_int::do_allocate(std::size_t sz) | |
| { | |
| int which_allocator = cell_size_to_allocator[(sz - 1) / increment + 1]; | |
| void* p = allocators[which_allocator]->allocate(); | |
| return p; | |
| } | |
| void | |
| sc_mempool_int::do_release(void* p, std::size_t sz) | |
| { | |
| int which_allocator = cell_size_to_allocator[(sz - 1) / increment + 1]; | |
| allocators[which_allocator]->release(p); | |
| } | |
| void | |
| sc_mempool_int::display_statistics() | |
| { | |
| printf("*** Memory Pool Statistics ***\n"); | |
| for (int i = 1; i <= num_pools; ++i) | |
| allocators[i]->display_statistics(); | |
| } | |
| /****************************************************************************/ | |
| void* | |
| sc_mempool::allocate(std::size_t sz) | |
| { | |
| if (use_default_new) | |
| return ::operator new(sz); | |
| if (the_mempool == 0) { | |
| use_default_new = compute_use_default_new(); | |
| if (use_default_new) | |
| return ::operator new(sz); | |
| // Note that the_mempool is never freed. This is going to cause | |
| // memory leaks when the program exits. | |
| the_mempool = new sc_mempool_int( 1984, sizeof(cell_sizes)/sizeof(cell_sizes[0]) - 1, 8 ); | |
| } | |
| if (sz > (unsigned) the_mempool->max_size) | |
| return ::operator new(sz); | |
| return the_mempool->do_allocate(sz); | |
| } | |
| void | |
| sc_mempool::release(void* p, std::size_t sz) | |
| { | |
| if (p) { | |
| if (use_default_new || sz > (unsigned) the_mempool->max_size) { | |
| ::operator delete(p); | |
| return; | |
| } | |
| the_mempool->do_release(p, sz); | |
| } | |
| } | |
| void | |
| sc_mempool::display_statistics() | |
| { | |
| if (the_mempool && !use_default_new) { | |
| the_mempool->display_statistics(); | |
| } else { | |
| printf("SystemC info: no memory allocation was done through the memory pool.\n"); | |
| } | |
| } | |
| } // namespace sc_core | |
| // $Log: sc_mempool.cpp,v $ | |
| // Revision 1.4 2011/08/26 20:46:18 acg | |
| // Andy Goodrich: moved the modification log to the end of the file to | |
| // eliminate source line number skew when check-ins are done. | |
| // | |
| // Revision 1.3 2011/08/24 22:05:56 acg | |
| // Torsten Maehne: initialization changes to remove warnings. | |
| // | |
| // Revision 1.2 2011/02/18 20:38:44 acg | |
| // Andy Goodrich: Updated Copyright notice. | |
| // | |
| // Revision 1.1.1.1 2006/12/15 20:20:06 acg | |
| // SystemC 2.3 | |
| // | |
| // Revision 1.3 2006/01/13 18:53:10 acg | |
| // Andy Goodrich: Added $Log command so that CVS comments are reproduced in | |
| // the source. | |
| // taf |