diff --git a/doc/doxygen/Doxyfile.in b/doc/doxygen/Doxyfile.in index 9acd994a68..e87e849013 100644 --- a/doc/doxygen/Doxyfile.in +++ b/doc/doxygen/Doxyfile.in @@ -410,24 +410,27 @@ WARN_LOGFILE = "../doxygen-warn.log" # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = @LAMMPS_SOURCE_DIR@/utils.cpp \ - @LAMMPS_SOURCE_DIR@/utils.h \ - @LAMMPS_SOURCE_DIR@/library.cpp \ - @LAMMPS_SOURCE_DIR@/library.h \ - @LAMMPS_SOURCE_DIR@/lammps.cpp \ - @LAMMPS_SOURCE_DIR@/lammps.h \ - @LAMMPS_SOURCE_DIR@/lmptype.h \ - @LAMMPS_SOURCE_DIR@/pointers.h \ - @LAMMPS_SOURCE_DIR@/atom.cpp \ - @LAMMPS_SOURCE_DIR@/atom.h \ - @LAMMPS_SOURCE_DIR@/input.cpp \ - @LAMMPS_SOURCE_DIR@/input.h \ - @LAMMPS_SOURCE_DIR@/tokenizer.cpp \ - @LAMMPS_SOURCE_DIR@/tokenizer.h \ - @LAMMPS_SOURCE_DIR@/text_file_reader.cpp \ - @LAMMPS_SOURCE_DIR@/text_file_reader.h \ - @LAMMPS_SOURCE_DIR@/potential_file_reader.cpp \ - @LAMMPS_SOURCE_DIR@/potential_file_reader.h \ +INPUT = @LAMMPS_SOURCE_DIR@/utils.cpp \ + @LAMMPS_SOURCE_DIR@/utils.h \ + @LAMMPS_SOURCE_DIR@/library.cpp \ + @LAMMPS_SOURCE_DIR@/library.h \ + @LAMMPS_SOURCE_DIR@/lammps.cpp \ + @LAMMPS_SOURCE_DIR@/lammps.h \ + @LAMMPS_SOURCE_DIR@/lmptype.h \ + @LAMMPS_SOURCE_DIR@/pointers.h \ + @LAMMPS_SOURCE_DIR@/atom.cpp \ + @LAMMPS_SOURCE_DIR@/atom.h \ + @LAMMPS_SOURCE_DIR@/input.cpp \ + @LAMMPS_SOURCE_DIR@/input.h \ + @LAMMPS_SOURCE_DIR@/tokenizer.cpp \ + @LAMMPS_SOURCE_DIR@/tokenizer.h \ + @LAMMPS_SOURCE_DIR@/text_file_reader.cpp \ + @LAMMPS_SOURCE_DIR@/text_file_reader.h \ + @LAMMPS_SOURCE_DIR@/potential_file_reader.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.h \ # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded diff --git a/doc/src/pg_developer.rst b/doc/src/pg_developer.rst index b8510b5b70..b989b097e8 100644 --- a/doc/src/pg_developer.rst +++ b/doc/src/pg_developer.rst @@ -53,7 +53,7 @@ command, e.g. ``src/force.cpp`` and the :cpp:class:`LAMMPS_NS::Force` class. They are discussed in the next section. A small number of C++ classes and utility functions are implemented with -only a ``.h`` file. Examples are the Pointer class and the mergesort function. +only a ``.h`` file. Examples are the Pointer class or the MyPool class. LAMMPS class topology ===================== @@ -1102,3 +1102,12 @@ A file that would be parsed by the reader code fragment looks like this: :project: progguide :members: + +---------- + +Memory pool classes +=================== + +.. doxygenclass:: LAMMPS_NS::MyPage + :project: progguide + :members: diff --git a/doc/utils/sphinx-config/false_positives.txt b/doc/utils/sphinx-config/false_positives.txt index 586e14da88..99493c160c 100644 --- a/doc/utils/sphinx-config/false_positives.txt +++ b/doc/utils/sphinx-config/false_positives.txt @@ -1975,6 +1975,7 @@ MxN myCompute myIndex mylammps +MyPool mysocket myTemp myVec diff --git a/src/my_page.cpp b/src/my_page.cpp new file mode 100644 index 0000000000..0812781e75 --- /dev/null +++ b/src/my_page.cpp @@ -0,0 +1,267 @@ +/* -*- 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. +------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- +usage: + request one datum at a time, repeat, clear + request chunks of datums in each get() or vget(), repeat, clear + chunk size can vary from request to request + chunk size can be known in advance or registered after usage via vgot() +inputs: + template T = one datum, e.g. int, double, struct, int[3] + for int[3], access datum as ivec[i][2] +methods: + T *get() = return ptr to one datum + T *get(N) = return ptr to N datums, N < maxchunk required + T *vget() = return ptr to maxchunk datums, use as needed, then call vgot() + all gets return NULL if error encountered + vgot(N) = used N datums of previous vget(), N < maxchunk required + void init(maxchunk, pagesize, pagedelta) + define allocation params and allocate first page(s) + call right after constructor + can call again to reset allocation params and free previous pages + maxchunk = max # of datums in one chunk, default = 1 + pagesize = # of datums in one page, default = 1024 + should be big enough to store multiple chunks + pagedelta = # of pages to allocate at a time, default = 1 + return 1 if bad params + void reset() = clear pages w/out freeing + int size() = return total size of allocated pages in bytes + int status() = return error status + 0 = ok, 1 = chunksize > maxchunk, 2 = allocation error +------------------------------------------------------------------------- */ + +#include "my_page.h" + +#include + +#if defined(LMP_USER_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 chunks are not returnable (i.e. cannot be freed individually). + * One can only reset and start over. The purpose of this + * class is to replace many small mallocs with a few large + * mallocs. Since the pages are never freed, they can be re-used + * without having to re-allocate them. + * + * The settings *maxchunk*, *pagesize*, and *pagedelta* contol + * 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). + * + * This class is the "workhorse" for the memory management of + * neighbor lists. */ + +/** 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) {}; + +/** Free all allocated pages of this class instance */ + +template +MyPage::~MyPage() { + for (int i = 0; i < npage; i++) free(pages[i]); + free(pages); +} + +/** (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_page_delta Number of pages to allocate with one malloc + * \return 1 if there was an 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 any previously allocated pages + + for (int i = 0; i < npage; i++) free(pages[i]); + free(pages); + + // initial page allocation + + ndatum = nchunk = 0; + pages = NULL; + npage = 0; + allocate(); + if (errorflag) return 2; + ipage = index = 0; + page = pages[ipage]; + return 0; + } + +/** Pointer to location that can store one item. + * + * This will allocate more pages as needed. + * + * \return memory location or null pointer, if memory allocation failed */ + +template +T *MyPage::get() { + ndatum++; + nchunk++; + if (index < pagesize) return &page[index++]; + ipage++; + if (ipage == npage) { + allocate(); + if (errorflag) return NULL; + } + page = pages[ipage]; + index = 0; + return &page[index++]; +} + +/** 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 NULL; + } + ndatum += n; + nchunk++; + if (index+n <= pagesize) { + int start = index; + index += n; + return &page[start]; + } + ipage++; + if (ipage == npage) { + allocate(); + if (errorflag) return NULL; + } + page = pages[ipage]; + index = n; + return &page[0]; +} + +/** Get pointer to location that can store *maxchunk* items. + * + * This will return the same pointer as the previous call to + * this function unless vgot() is called afterwards to record + * how many items of the chunk were actually used. + * + * \return pointer to chunk of memory or null pointer if run out of memory */ + +template +T *MyPage::vget() { + if (index+maxchunk <= pagesize) return &page[index]; + ipage++; + if (ipage == npage) { + allocate(); + if (errorflag) return NULL; + } + page = pages[ipage]; + index = 0; + return &page[index]; +} + +/** Mark *N* items as used of the chunk reserved with a preceding call to vget(). + * + * This will advance the internal pointer inside the current memory page. + * It is not necessary to call this function for *N* = 0, that is the reserved + * storage was not used. A following call to vget() will then reserve the + * same location again. It is an error if *N* > *maxchunk*. + * + * \param n Number of iterms used in previously reserved chunk */ + +template +void MyPage::vgot(int n) { + if (n > maxchunk) errorflag = 1; + ndatum += n; + nchunk++; + index += n; +} + +/** Reset state of memory pool without freeing any memory */ + +template +void MyPage::reset() { + ndatum = nchunk = 0; + index = ipage = 0; + page = pages[ipage]; +} + +/* ---------------------------------------------------------------------- */ + +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 + } +} + +// explicit instantiations +#include "REPLICA/fix_hyper_local.h" +namespace LAMMPS_NS { + template class MyPage; + template class MyPage; + template class MyPage; + template class MyPage; + template class MyPage; +} diff --git a/src/my_page.h b/src/my_page.h index 01542a9174..66708ac18f 100644 --- a/src/my_page.h +++ b/src/my_page.h @@ -12,37 +12,7 @@ ------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- -MyPage = templated class for storing chunks of datums in pages - chunks are not returnable, can only reset and start over - replaces many small mallocs with a few large mallocs - pages are never freed, so can reuse w/out reallocs -usage: - request one datum at a time, repeat, clear - request chunks of datums in each get() or vget(), repeat, clear - chunk size can vary from request to request - chunk size can be known in advance or registered after usage via vgot() -inputs: - template T = one datum, e.g. int, double, struct, int[3] - for int[3], access datum as ivec[i][2] -methods: - T *get() = return ptr to one datum - T *get(N) = return ptr to N datums, N < maxchunk required - T *vget() = return ptr to maxchunk datums, use as needed, then call vgot() - all gets return NULL if error encountered - vgot(N) = used N datums of previous vget(), N < maxchunk required - void init(maxchunk, pagesize, pagedelta) - define allocation params and allocate first page(s) - call right after constructor - can call again to reset allocation params and free previous pages - maxchunk = max # of datums in one chunk, default = 1 - pagesize = # of datums in one page, default = 1024 - should be big enough to store multiple chunks - pagedelta = # of pages to allocate at a time, default = 1 - return 1 if bad params - void reset() = clear pages w/out freeing - int size() = return total size of allocated pages in bytes - int status() = return error status - 0 = ok, 1 = chunksize > maxchunk, 2 = allocation error + templated class for storing chunks of datums in pages ------------------------------------------------------------------------- */ #ifndef LAMMPS_MY_PAGE_H @@ -52,7 +22,6 @@ methods: #define LAMMPS_MEMALIGN 64 #endif -#include namespace LAMMPS_NS { template @@ -60,139 +29,33 @@ class MyPage { public: int ndatum; // total # of stored datums int nchunk; // total # of stored chunks - - MyPage() { - ndatum = nchunk = 0; - pages = NULL; - npage = 0; - errorflag = 0; - } - - // (re)initialize allocation params - // also allocate first page(s) + MyPage(); + virtual ~MyPage(); int init(int user_maxchunk = 1, int user_pagesize = 1024, - int user_pagedelta = 1) { - maxchunk = user_maxchunk; - pagesize = user_pagesize; - pagedelta = user_pagedelta; + int user_pagedelta = 1); + + T *get(); + T *get(int n); - if (maxchunk <= 0 || pagesize <= 0 || pagedelta <= 0) return 1; - if (maxchunk > pagesize) return 1; + T *vget(); + void vgot(int n); - // free any previously allocated pages + void reset(); - for (int i = 0; i < npage; i++) free(pages[i]); - free(pages); - - // initial page allocation - - ndatum = nchunk = 0; - pages = NULL; - npage = 0; - allocate(); - if (errorflag) return 2; - ipage = index = 0; - page = pages[ipage]; - return 0; - } - - // free all allocated pages - - ~MyPage() { - for (int i = 0; i < npage; i++) free(pages[i]); - free(pages); - } - - // get ptr to one datum - // return NULL if run out of memory - - T *get() { - ndatum++; - nchunk++; - if (index < pagesize) return &page[index++]; - ipage++; - if (ipage == npage) { - allocate(); - if (errorflag) return NULL; - } - page = pages[ipage]; - index = 0; - return &page[index++]; - } - - // get ptr to location that can store N datums - // error if N > maxchunk - // return NULL if run out of memory - - T *get(int n) { - if (n > maxchunk) { - errorflag = 1; - return NULL; - } - ndatum += n; - nchunk++; - if (index+n <= pagesize) { - int start = index; - index += n; - return &page[start]; - } - ipage++; - if (ipage == npage) { - allocate(); - if (errorflag) return NULL; - } - page = pages[ipage]; - index = n; - return &page[0]; - } - - // get ptr to location that can store maxchunk datums - // will return same ptr as previous call if vgot() not called - // return NULL if run out of memory - - T *vget() { - if (index+maxchunk <= pagesize) return &page[index]; - ipage++; - if (ipage == npage) { - allocate(); - if (errorflag) return NULL; - } - page = pages[ipage]; - index = 0; - return &page[index]; - } - - // increment by N = # of values stored in loc returned by vget() - // OK to not call if vget() ptr was not used - // error if N > maxchunk - - void vgot(int n) { - if (n > maxchunk) errorflag = 1; - ndatum += n; - nchunk++; - index += n; - } - - // clear all pages, without freeing any memory - - void reset() { - ndatum = nchunk = 0; - index = ipage = 0; - page = pages[ipage]; - } - - // return total size of allocated pages + /** Return total size of allocated pages + * + * \return total storage used in bytes */ int size() const { return npage*pagesize*sizeof(T); } - // return error status + /** Return error status + * + * \return 0 if no error, 1 requested chunk size > maxchunk, 2 if malloc failed */ - int status() const { - return errorflag; - } + int status() const { return errorflag; } private: T **pages; // list of allocated pages @@ -208,27 +71,7 @@ class MyPage { int errorflag; // flag > 0 if error has occurred // 1 = chunk size exceeded maxchunk // 2 = memory allocation error - - void 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 - } - } + void allocate(); }; }