1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
|
/* -*- 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.
------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------
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:
continously 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 varaibles:
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
#include <stdlib.h>
namespace LAMMPS_NS {
template<class T>
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;
}
// 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);
}
}
// 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]];
}
// 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]];
}
// 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;
}
private:
int minchunk; // min # of datums per chunk
int maxchunk; // max # of datums per chunk
int nbin; // # of bins to split min-to-max into
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
T **pages; // list of allocated pages
int *whichbin; // which bin each page belongs to
int npage; // # of allocated pages
int *freelist; // each chunk points to next unused chunk in same bin
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;
pages[i] = (T *) malloc(chunkperpage*chunksize[ibin]*sizeof(T));
size += chunkperpage*chunksize[ibin];
if (!pages[i]) errorflag = 2;
}
// 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;
}
};
}
#endif
|