Files
lammps/src/my_page.cpp
2022-10-24 11:08:26 -04:00

189 lines
5.8 KiB
C++

/* -*- 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 <class T>
MyPage<T>::MyPage() :
ndatum(0), nchunk(0), pages(nullptr), page(nullptr), npage(0), ipage(-1), index(-1),
maxchunk(-1), pagesize(-1), pagedelta(1), errorflag(0){};
template <class T> MyPage<T>::~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 <class T> int MyPage<T>::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 <class T> T *MyPage<T>::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 <class T> void MyPage<T>::reset()
{
ndatum = nchunk = 0;
index = ipage = 0;
page = (pages != nullptr) ? pages[ipage] : nullptr;
errorflag = 0;
}
/* ---------------------------------------------------------------------- */
template <class T> void MyPage<T>::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 <class T> void MyPage<T>::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<int>;
template class MyPage<long>;
template class MyPage<long long>;
template class MyPage<double>;
template class MyPage<HyperOneCoeff>;
} // namespace LAMMPS_NS