What I want to do: I need to store cell data in block-wise form, that is
*cell_member1[cell0] .. cell_member1[cellN] … cell_memberM[cell0] .. cell_memberM[cellN]*
Then I need to access this data efficiently and, if possible, using a nice syntax. It would be great if I could define the data to be stored easily, i.e. by defining an object with members as the data that I want to store and passing it to some “magic” that does everything for me.
Motivation: why I need to do it this way? Cache trashing. In some inner loops only some members of the objects are accessed. Wasting half of a cache-line with unused memory is not an option for my application. I could store pointers in the objects that point to some sequential memory region. This wastes memory and forces me to use a different syntax in this regions.
How I’m currently doing it: I have a container of the form:
template<class T> struct Container {
char* data;
Container(const int n) {
data = new char[n*T::spaceRequirements()]; //< Data stored "block-wise"
new(data) typename T::Flags[n]; //< Flags stored "cell-wise"
}
/// Destructor ommited for briefness.
};
in which I store data for some cells of type T. I need some flags per cell and right now I’m using std::bitset to store them which means that I need to store this bitsets in cell-wise form:
*cell_member1[cell0] … cell_memberM[cell0] … cell_member1[cellN] .. cell_memberM[cellN]*
I am describing how much data per cell needs to be stored in the following class, which also provides access to the data:
template<int nd> struct CellAccessor {
/// Cell flags are stored cell-wise:
typedef std::bitset<64> Flags;
enum { DELETE = 0, ///< Cell marked for deletion
REFINE = 1 ///< Cell marked for refinement
//...
}; ///< Enum for the flags.
static inline Flags& flags(const int cellId) {
return *reinterpret_cast<Flags*>(data + sizeof(Flags)*cellId); }
template<int pId> static inline Flags::reference flags(const int cellId) {
return flags(cellId)[pId]; } //< Cell-wise access to the properties
/// The rest of the data is stored block-wise:
static inline int& order(const int cellId) { ///< One int field.
return *reinterpret_cast<int*>
(data + maxNoCells*sizeof(Flags) + sizeof(int)*cellId);}
/// Coordinate vector with nd components:
static inline double& coordinates(const int cellId, const int i) {
return *reinterpret_cast<double*>
(data + maxNoCells*(sizeof(Flags)+sizeof(int))
+ maxNoCells*i*sizeof(double) + sizeof(double)*cellId); }
template<int i> static inline double& coordinates(const int cellId) {
return *reinterpret_cast<double*>
(data +maxNoCells*(sizeof(Flags)+sizeof(int)+i*sizeof(double))
+ sizeof(double)*cellId); }
/// Total amount of memory to allocate per cell: (used by Container)
static inline int spaceRequirements() { return
sizeof(Flags) // Flags
+ sizeof(int) // order
+ nd*sizeof(double) // coordinates
;}
/// Constructor gets pointer to the beginning of the container
/// and the offset for the member variables:
CellAccessor(char* d, int n){data = d; maxNoCells = n;}
private:
static char* data; ///< Pointer to the beginning of the container.
static int maxNoCells; ///< Cell offset for the member variables.
};
template<int nd> char* CellAccessor<nd>::data = nullptr;
template<int nd> int CellAccessor<nd>::maxNoCells = 0;
And I use it like this:
int main() {
int maxNoCells = 10000; ///< Maximum number of cells (=cell offset).
typedef CellAccessor<2> A;
Container< A > cellData(maxNoCells); ///< Allocate cell data.
A cells(cellData.data,maxNoCells); ///< Provides access to cell data.
for(int i = 0; i < maxNoCells; ++i){
cells.flags<A::DELETE>(i) = i%2==0 ? true : false;
cells.flags<A::REFINE>(i) = i%2==0 ? false : true;
cells.coordinates(i,0) = i;
cells.coordinates<1>(i) = -((double)i);
cells.order(i) = 2;
}
}
Pros:
-
The data is in block-wise form, which is what I needed.
-
The syntax is ok.
Problems:
-
My classes are doing too much: providing access to the data for the users, providing how much data needs to be stored for the containers, providing how the data should be moved/copied/swaped for my data structures (which are trees…)…
-
I can’t use STL algorithms without iterators. I’ve implemented iterators by making them store the cell index and reimplementing the CellAccessor class inside them (bad! DRY!).
-
Bitset is still being stored in cell-wise form. I could re-implement bitset for my block-wise data structure…
-
data and maxNoCells are static variables, but I could make them normal member variables if required.
Question: is there any efficient way to store “objects” (or what we conceptually understand by objects) in block-wise form and access them as if they were stored in a std container such as vector?
What you want is a style of a “COLUMN BASED” memory access
You can easily implement it using
std::vectoras your column type or create your own “column” type with your own underlying memory management – butstd::vectorshould work just fineNow once you have your column type you create your “TABLE” type.
In a way your table cab be just a vector of vectors. You can of course wrap it up in order to get better looking accessors (If you want to access by row (object) first and column (property) after.
This is I think the best general approach.
Even In your specific case – since you want to save memory using bit length flags, as mentioned by Bart van Ingen Schenau you can use a
vector<bool>so the general approach stands