/* -*- c++ -*- ---------------------------------------------------------- LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator https://www.lammps.org/, Sandia National Laboratories LAMMPS development team: developers@lammps.org Copyright (2003) Sandia Corporation. Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains certain rights in this software. This software is distributed under the GNU General Public License. See the README file in the top-level LAMMPS directory. ------------------------------------------------------------------------- */ #include "my_page.h" #if defined(LMP_INTEL) && !defined(LAMMPS_MEMALIGN) && !defined(_WIN32) #define LAMMPS_MEMALIGN 64 #endif using namespace LAMMPS_NS; /** \class LAMMPS_NS::MyPage * \brief Templated class for storing chunks of datums in pages. * * The size of the chunk may vary from call to call, but must be * less or equal than the *maxchunk* setting. * The chunks are not returnable like with malloc() (i.e. you cannot * call free() on them individually). One can only reset and start over. * The purpose of this class is to replace many small memory allocations * via malloc() with a few large ones. Since the pages are never freed * until the class is re-initialized, they can be re-used without having * to re-allocate them by calling the reset() method. * * The settings *maxchunk*, *pagesize*, and *pagedelta* control * the memory allocation strategy. The *maxchunk* value represents * the expected largest number of items per chunk. If there is * less space left on the current page, a new page is allocated * for the next chunk. The *pagesize* value represents how many * items can fit on a single page. It should have space for multiple * chunks of size *maxchunk*. The combination of these two * parameters determines how much memory is wasted by either switching * to the next page too soon or allocating too large pages that never * get properly used. It is an error, if a requested chunk is larger * than *maxchunk*. The *pagedelta* parameter determines how many * pages are allocated in one go. In combination with the *pagesize* * setting, this determines how often blocks of memory get allocated * (fewer allocations will result in faster execution). * * \note * This is a template class with explicit instantiation. If the class * is used with a new data type a new explicit instantiation may need to * be added at the end of the file ``src/my_page.cpp`` to avoid symbol * lookup errors. */ /** Create a class instance * * Need to call init() before use to define allocation settings */ template MyPage::MyPage() : ndatum(0), nchunk(0), pages(nullptr), page(nullptr), npage(0), ipage(-1), index(-1), maxchunk(-1), pagesize(-1), pagedelta(1), errorflag(0){}; template MyPage::~MyPage() { deallocate(); } /** (Re-)initialize the set of pages and allocation parameters. * * This also frees all previously allocated storage and allocates * the first page(s). * * \param user_maxchunk Expected maximum number of items for one chunk * \param user_pagesize Number of items on a single memory page * \param user_pagedelta Number of pages to allocate with one malloc * \return 1 if there were invalid parameters, 2 if there was an allocation error or 0 if successful */ template int MyPage::init(int user_maxchunk, int user_pagesize, int user_pagedelta) { maxchunk = user_maxchunk; pagesize = user_pagesize; pagedelta = user_pagedelta; if (maxchunk <= 0 || pagesize <= 0 || pagedelta <= 0) return 1; if (maxchunk > pagesize) return 1; // free storage if re-initialized deallocate(); // initial page allocation allocate(); if (errorflag) return 2; reset(); return 0; } /** Pointer to location that can store N items. * * This will allocate more pages as needed. * If the parameter *N* is larger than the *maxchunk* * setting an error is flagged. * * \param n number of items for which storage is requested * \return memory location or null pointer, if error or allocation failed */ template T *MyPage::get(int n) { if (n > maxchunk) { errorflag = 1; return nullptr; } ndatum += n; nchunk++; // return pointer from current page if (index + n <= pagesize) { int start = index; index += n; return &page[start]; } // allocate new page ipage++; if (ipage == npage) { allocate(); if (errorflag) return nullptr; } page = pages[ipage]; index = n; return &page[0]; } /** Reset state of memory pool without freeing any memory */ template void MyPage::reset() { ndatum = nchunk = 0; index = ipage = 0; page = (pages != nullptr) ? pages[ipage] : nullptr; errorflag = 0; } /* ---------------------------------------------------------------------- */ template void MyPage::allocate() { npage += pagedelta; pages = (T **) realloc(pages, npage * sizeof(T *)); if (!pages) { errorflag = 2; return; } for (int i = npage - pagedelta; i < npage; i++) { #if defined(LAMMPS_MEMALIGN) void *ptr; if (posix_memalign(&ptr, LAMMPS_MEMALIGN, pagesize * sizeof(T))) errorflag = 2; pages[i] = (T *) ptr; #else pages[i] = (T *) malloc(pagesize * sizeof(T)); if (!pages[i]) errorflag = 2; #endif } } /** Free all allocated pages of this class instance */ template void MyPage::deallocate() { reset(); for (int i = 0; i < npage; i++) free(pages[i]); free(pages); pages = nullptr; npage = 0; } // explicit instantiations namespace LAMMPS_NS { template class MyPage; template class MyPage; template class MyPage; template class MyPage; template class MyPage; } // namespace LAMMPS_NS