small updates to MyPage and convert MyPoolChunk similar to MyPage

This commit is contained in:
Axel Kohlmeyer 2020-09-08 15:13:16 -04:00
parent a3cc7581b1
commit 9f7e309f07
No known key found for this signature in database
GPG Key ID: D9B44E93BF0C375A
7 changed files with 275 additions and 172 deletions

View File

@ -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

View File

@ -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:

View File

@ -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++) {

View File

@ -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

View File

@ -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 {

244
src/my_pool_chunk.cpp Normal file
View File

@ -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 <cstdlib>
#include <cstdio>
#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 <class T>
MyPoolChunk<T>::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 <class T>
MyPoolChunk<T>::~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 <class T>
T *MyPoolChunk<T>::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 <class T>
T *MyPoolChunk<T>::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 <class T>
void MyPoolChunk<T>::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 <class T>
void MyPoolChunk<T>::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 <class T>
double MyPoolChunk<T>::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<int>;
template class MyPoolChunk<double>;
}

View File

@ -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 <cstdlib>
namespace LAMMPS_NS {
template<class T>
@ -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