diff --git a/doc/doxygen/Doxyfile.in b/doc/doxygen/Doxyfile.in index e87e849013..67d5ebd20a 100644 --- a/doc/doxygen/Doxyfile.in +++ b/doc/doxygen/Doxyfile.in @@ -430,6 +430,7 @@ INPUT = @LAMMPS_SOURCE_DIR@/utils.cpp \ @LAMMPS_SOURCE_DIR@/potential_file_reader.h \ @LAMMPS_SOURCE_DIR@/my_page.cpp \ @LAMMPS_SOURCE_DIR@/my_page.h \ + @LAMMPS_SOURCE_DIR@/my_pool_chunk.cpp \ @LAMMPS_SOURCE_DIR@/my_pool_chunk.h \ # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or diff --git a/doc/src/pg_developer.rst b/doc/src/pg_developer.rst index ba65758bfc..ce14a0c436 100644 --- a/doc/src/pg_developer.rst +++ b/doc/src/pg_developer.rst @@ -1162,3 +1162,7 @@ its size is registered later with :cpp:func:`vgot() .. doxygenclass:: LAMMPS_NS::MyPage :project: progguide :members: + +.. doxygenclass:: LAMMPS_NS::MyPoolChunk + :project: progguide + :members: diff --git a/src/atom_vec_body.cpp b/src/atom_vec_body.cpp index 608c803e20..e9044519c9 100644 --- a/src/atom_vec_body.cpp +++ b/src/atom_vec_body.cpp @@ -555,7 +555,7 @@ bigint AtomVecBody::memory_usage_bonus() { bigint bytes = 0; bytes += nmax_bonus*sizeof(Bonus); - bytes += icp->size + dcp->size; + bytes += icp->size() + dcp->size(); int nall = nlocal_bonus + nghost_bonus; for (int i = 0; i < nall; i++) { diff --git a/src/my_page.cpp b/src/my_page.cpp index ea69f23c40..356eb25aa6 100644 --- a/src/my_page.cpp +++ b/src/my_page.cpp @@ -29,8 +29,9 @@ using namespace LAMMPS_NS; * 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, - * they can be re-used without having to re-allocate them. + * 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 diff --git a/src/my_page.h b/src/my_page.h index 44c57068c4..98ecfd8c95 100644 --- a/src/my_page.h +++ b/src/my_page.h @@ -18,10 +18,6 @@ #ifndef LAMMPS_MY_PAGE_H #define LAMMPS_MY_PAGE_H -#if defined(LMP_USER_INTEL) && !defined(LAMMPS_MEMALIGN) && !defined(_WIN32) -#define LAMMPS_MEMALIGN 64 -#endif - #include "lmptype.h" namespace LAMMPS_NS { diff --git a/src/my_pool_chunk.cpp b/src/my_pool_chunk.cpp new file mode 100644 index 0000000000..cf914e41aa --- /dev/null +++ b/src/my_pool_chunk.cpp @@ -0,0 +1,244 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + http://lammps.sandia.gov, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + 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_pool_chunk.h" + +#include +#include + +#if defined(LMP_USER_INTEL) && !defined(LAMMPS_MEMALIGN) && !defined(_WIN32) +#define LAMMPS_MEMALIGN 64 +#endif + +using namespace LAMMPS_NS; + +/** \class LAMMPS_NS::MyPoolChunk + * \brief Templated class for storing chunks of datums in pages + * + * The size of the chunk may vary from call to call between the + * *minchunk* and *maxchunk* setting. Chunks may be returned + * to the pool for re-use. Chunks can be reserved in *nbin* + * different sizes between *minchunk* and *maxchunk*. + * The *chunksperpage* setting specifies how many chunks are stored + * on any page and the *pagedelta* setting determines how many + * pages are allocated in one go. Pages are never freed, so they + * can be re-used without re-allocation. + * + * \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_pool_chunk.cpp`` to + * avoid symbol lookup errors. */ + +/* ---------------------------------------------------------------------- +MyPoolChunk = templated class for storing chunks of datums in pages + chunks can be returned to pool for reuse + chunks come in nbin different fixed sizes so can reuse + replaces many small mallocs with a few large mallocs + pages are never freed, so can reuse w/out reallocs +usage: + continuously get() and put() chunks as needed + NOTE: could add a clear() if retain info on mapping of pages to bins +inputs: + template T = one datum, e.g. int, double, struct + minchunk = min # of datums in one chunk, def = 1 + maxchunk = max # of datums in one chunk, def = 1 + nbin = # of bins between minchunk and maxchunk + chunkperpage = # of chunks in one page, def = 1024 + pagedelta = # of pages to allocate at a time, def = 1 +methods: + T *get(index) = return ptr/index to unused chunk of size maxchunk + T *get(N,index) = return ptr/index to unused chunk of size N + minchunk <= N <= maxchunk required + put(index) = return indexed chunk to pool (same index returned by get) + int size() = return total size of allocated pages in bytes +public variables: + ndatum = total # of stored datums + nchunk = total # of stored chunks + size = total size of all allocated pages in daums + errorflag = flag for various error conditions +------------------------------------------------------------------------- */ + +/** Create a class instance and set memory pool parameters */ +template +MyPoolChunk::MyPoolChunk(int user_minchunk, int user_maxchunk, int user_nbin, + int user_chunkperpage, int user_pagedelta) { + minchunk = user_minchunk; + maxchunk = user_maxchunk; + nbin = user_nbin; + chunkperpage = user_chunkperpage; + pagedelta = user_pagedelta; + + errorflag = 0; + if (minchunk <= 0 || minchunk > maxchunk) errorflag = 1; + if (user_nbin <= 0 || chunkperpage <= 0 || pagedelta <= 0) errorflag = 1; + + freehead = new int[nbin]; + chunksize = new int[nbin]; + if (!freehead || !chunksize) errorflag = 1; + if (errorflag) return; + + // insure nbin*binsize spans minchunk to maxchunk inclusive + + binsize = (maxchunk-minchunk+1) / nbin; + if (minchunk + nbin*binsize <= maxchunk) binsize++; + + freelist = nullptr; + for (int ibin = 0; ibin < nbin; ibin++) { + freehead[ibin] = -1; + chunksize[ibin] = minchunk + (ibin+1)*binsize - 1; + if (chunksize[ibin] > maxchunk) chunksize[ibin] = maxchunk; + } + + ndatum = nchunk = 0; + pages = nullptr; + whichbin = nullptr; + npage = 0; +} + +/** Destroy class instance and free all allocated memory */ +template +MyPoolChunk::~MyPoolChunk() { + delete [] freehead; + delete [] chunksize; + if (npage) { + free(freelist); + for (int i = 0; i < npage; i++) free(pages[i]); + free(pages); + free(whichbin); + } +} + +/** Return pointer/index of unused chunk of size maxchunk + * + * \param index Index of chunk in memory pool + * \return Pointer to requested chunk of storage */ + +template +T *MyPoolChunk::get(int &index) { + int ibin = nbin-1; + if (freehead[ibin] < 0) { + allocate(ibin); + if (errorflag) return nullptr; + } + + ndatum += maxchunk; + nchunk++; + index = freehead[ibin]; + int ipage = index/chunkperpage; + int ientry = index % chunkperpage; + freehead[ibin] = freelist[index]; + return &pages[ipage][ientry*chunksize[ibin]]; +} + +/** Return pointer/index of unused chunk of size N + * + * \param n Size of chunk + * \param index Index of chunk in memory pool + * \return Pointer to requested chunk of storage */ + +template +T *MyPoolChunk::get(int n, int &index) { + if (n < minchunk || n > maxchunk) { + errorflag = 3; + return nullptr; + } + + int ibin = (n-minchunk) / binsize; + if (freehead[ibin] < 0) { + allocate(ibin); + if (errorflag) return nullptr; + } + + ndatum += n; + nchunk++; + index = freehead[ibin]; + int ipage = index/chunkperpage; + int ientry = index % chunkperpage; + freehead[ibin] = freelist[index]; + return &pages[ipage][ientry*chunksize[ibin]]; +} + +/** Put indexed chunk back into memory pool via free list + */ + // index = -1 if no allocated chunk + +template +void MyPoolChunk::put(int index) { + if (index < 0) return; + int ipage = index/chunkperpage; + int ibin = whichbin[ipage]; + nchunk--; + ndatum -= chunksize[ibin]; + freelist[index] = freehead[ibin]; + freehead[ibin] = index; + } + + +template +void MyPoolChunk::allocate(int ibin) { + int oldpage = npage; + npage += pagedelta; + freelist = (int *) realloc(freelist,npage*chunkperpage*sizeof(int)); + pages = (T **) realloc(pages,npage*sizeof(T *)); + whichbin = (int *) realloc(whichbin,npage*sizeof(int)); + if (!freelist || !pages) { + errorflag = 2; + return; + } + + // allocate pages with appropriate chunksize for ibin + + for (int i = oldpage; i < npage; i++) { + whichbin[i] = ibin; +#if defined(LAMMPS_MEMALIGN) + void *ptr; + if (posix_memalign(&ptr, LAMMPS_MEMALIGN, + chunkperpage*chunksize[ibin]*sizeof(T))) + errorflag = 2; + pages[i] = (T *) ptr; +#else + pages[i] = (T *) malloc(chunkperpage*chunksize[ibin]*sizeof(T)); + size += chunkperpage*chunksize[ibin]; + if (!pages[i]) errorflag = 2; +#endif + } + + // reset free list for unused chunks on new pages + + freehead[ibin] = oldpage*chunkperpage; + for (int i = freehead[ibin]; i < npage*chunkperpage; i++) freelist[i] = i+1; + freelist[npage*chunkperpage-1] = -1; +} + +/** Return total size of allocated pages + * + * \return total storage used in bytes */ + +template +double MyPoolChunk::size() const { + double bytes = npage*chunkperpage*sizeof(int); + bytes += npage*sizeof(T *); + bytes += npage*sizeof(int); + for (int i=0; i < npage; ++i) + bytes += chunkperpage*chunksize[i]*sizeof(T); + + return bytes; +} + +// explicit instantiations + +namespace LAMMPS_NS { + template class MyPoolChunk; + template class MyPoolChunk; +} diff --git a/src/my_pool_chunk.h b/src/my_pool_chunk.h index da196f5ec9..f53bb46b1c 100644 --- a/src/my_pool_chunk.h +++ b/src/my_pool_chunk.h @@ -9,46 +9,11 @@ the GNU General Public License. See the README file in the top-level LAMMPS directory. -------------------------------------------------------------------------- */ - -/* ---------------------------------------------------------------------- -MyPoolChunk = templated class for storing chunks of datums in pages - chunks can be returned to pool for reuse - chunks come in nbin different fixed sizes so can reuse - replaces many small mallocs with a few large mallocs - pages are never freed, so can reuse w/out reallocs -usage: - continuously get() and put() chunks as needed - NOTE: could add a clear() if retain info on mapping of pages to bins -inputs: - template T = one datum, e.g. int, double, struct - minchunk = min # of datums in one chunk, def = 1 - maxchunk = max # of datums in one chunk, def = 1 - nbin = # of bins between minchunk and maxchunk - chunkperpage = # of chunks in one page, def = 1024 - pagedelta = # of pages to allocate at a time, def = 1 -methods: - T *get(index) = return ptr/index to unused chunk of size maxchunk - T *get(N,index) = return ptr/index to unused chunk of size N - minchunk <= N <= maxchunk required - put(index) = return indexed chunk to pool (same index returned by get) - int size() = return total size of allocated pages in bytes -public variables: - ndatum = total # of stored datums - nchunk = total # of stored chunks - size = total size of all allocated pages in daums - errorflag = flag for various error conditions -------------------------------------------------------------------------- */ + ------------------------------------------------------------------------- */ #ifndef LAMMPS_MY_POOL_CHUNK_H #define LAMMPS_MY_POOL_CHUNK_H -#if defined(LMP_USER_INTEL) && !defined(LAMMPS_MEMALIGN) && !defined(_WIN32) -#define LAMMPS_MEMALIGN 64 -#endif - -#include - namespace LAMMPS_NS { template @@ -56,113 +21,36 @@ class MyPoolChunk { public: int ndatum; // total # of stored datums int nchunk; // total # of stored chunks - int size; // total size of all allocated pages in datums - int errorflag; // flag > 1 if error has occurred - // 1 = invalid inputs - // 2 = memory allocation error - // 3 = chunk size exceeded maxchunk MyPoolChunk(int user_minchunk = 1, int user_maxchunk = 1, int user_nbin = 1, - int user_chunkperpage = 1024, int user_pagedelta = 1) { - minchunk = user_minchunk; - maxchunk = user_maxchunk; - nbin = user_nbin; - chunkperpage = user_chunkperpage; - pagedelta = user_pagedelta; - - errorflag = 0; - if (minchunk <= 0 || minchunk > maxchunk) errorflag = 1; - if (user_nbin <= 0 || chunkperpage <= 0 || pagedelta <= 0) errorflag = 1; - - freehead = new int[nbin]; - chunksize = new int[nbin]; - if (!freehead || !chunksize) errorflag = 1; - if (errorflag) return; - - // insure nbin*binsize spans minchunk to maxchunk inclusive - - binsize = (maxchunk-minchunk+1) / nbin; - if (minchunk + nbin*binsize <= maxchunk) binsize++; - - freelist = NULL; - for (int ibin = 0; ibin < nbin; ibin++) { - freehead[ibin] = -1; - chunksize[ibin] = minchunk + (ibin+1)*binsize - 1; - if (chunksize[ibin] > maxchunk) chunksize[ibin] = maxchunk; - } - - ndatum = nchunk = size = 0; - pages = NULL; - whichbin = NULL; - npage = 0; - } + int user_chunkperpage = 1024, int user_pagedelta = 1); // free all allocated memory - ~MyPoolChunk() { - delete [] freehead; - delete [] chunksize; - if (npage) { - free(freelist); - for (int i = 0; i < npage; i++) free(pages[i]); - free(pages); - free(whichbin); - } - } + ~MyPoolChunk(); // return pointer/index of unused chunk of size maxchunk - T *get(int &index) { - int ibin = nbin-1; - if (freehead[ibin] < 0) { - allocate(ibin); - if (errorflag) return NULL; - } - - ndatum += maxchunk; - nchunk++; - index = freehead[ibin]; - int ipage = index/chunkperpage; - int ientry = index % chunkperpage; - freehead[ibin] = freelist[index]; - return &pages[ipage][ientry*chunksize[ibin]]; - } + T *get(int &index); // return pointer/index of unused chunk of size N - T *get(int n, int &index) { - if (n < minchunk || n > maxchunk) { - errorflag = 3; - return NULL; - } - - int ibin = (n-minchunk) / binsize; - if (freehead[ibin] < 0) { - allocate(ibin); - if (errorflag) return NULL; - } - - ndatum += n; - nchunk++; - index = freehead[ibin]; - int ipage = index/chunkperpage; - int ientry = index % chunkperpage; - freehead[ibin] = freelist[index]; - return &pages[ipage][ientry*chunksize[ibin]]; - } - + T *get(int n, int &index); + // return indexed chunk to pool via free list // index = -1 if no allocated chunk - void put(int index) { - if (index < 0) return; - int ipage = index/chunkperpage; - int ibin = whichbin[ipage]; - nchunk--; - ndatum -= chunksize[ibin]; - freelist[index] = freehead[ibin]; - freehead[ibin] = index; - } + void put(int index); + + // total memory used in bytes + + double size() const; + + /** Return error status + * + * \return 0 if no error, 1 if invalid input, 2 if malloc() failed, 3 if chunk > maxchunk */ + + int status() const { return errorflag; } private: int minchunk; // min # of datums per chunk @@ -171,6 +59,10 @@ class MyPoolChunk { int chunkperpage; // # of chunks on every page, regardless of which bin int pagedelta; // # of pages to allocate at once, default = 1 int binsize; // delta in chunk sizes between adjacent bins + int errorflag; // flag > 0 if error has occurred + // 1 = invalid inputs + // 2 = memory allocation error + // 3 = chunk size exceeded maxchunk T **pages; // list of allocated pages int *whichbin; // which bin each page belongs to @@ -179,42 +71,7 @@ class MyPoolChunk { int *freehead; // index of first unused chunk in each bin int *chunksize; // size of chunks in each bin - void allocate(int ibin) { - int oldpage = npage; - npage += pagedelta; - freelist = (int *) realloc(freelist,npage*chunkperpage*sizeof(int)); - pages = (T **) realloc(pages,npage*sizeof(T *)); - whichbin = (int *) realloc(whichbin,npage*sizeof(int)); - if (!freelist || !pages) { - errorflag = 2; - return; - } - - // allocate pages with appropriate chunksize for ibin - - for (int i = oldpage; i < npage; i++) { - whichbin[i] = ibin; -#if defined(LAMMPS_MEMALIGN) - void *ptr; - if (posix_memalign(&ptr, LAMMPS_MEMALIGN, - chunkperpage*chunksize[ibin]*sizeof(T))) - errorflag = 2; - pages[i] = (T *) ptr; -#else - pages[i] = (T *) malloc(chunkperpage*chunksize[ibin]*sizeof(T)); - size += chunkperpage*chunksize[ibin]; - if (!pages[i]) errorflag = 2; -#endif - } - - // reset free list for unused chunks on new pages - - freehead[ibin] = oldpage*chunkperpage; - for (int i = freehead[ibin]; i < npage*chunkperpage; i++) freelist[i] = i+1; - freelist[npage*chunkperpage-1] = -1; - } + void allocate(int ibin); }; - } - #endif