やりたいこと:セル データをブロック形式で保存する必要があります。
*cell_member1[cell0] .. cell_member1[cellN] ... cell_memberM[cell0] .. cell_memberM[cellN]*
次に、このデータに効率的にアクセスする必要があり、可能であれば適切な構文を使用する必要があります。データを簡単に保存できるように定義できれば素晴らしいと思います。つまり、メンバーを持つオブジェクトを保存したいデータとして定義し、それをすべてを実行する「魔法」に渡すことです。
動機:なぜこのようにする必要があるのか? キャッシュの破棄。一部の内部ループでは、オブジェクトの一部のメンバーのみがアクセスされます。未使用のメモリでキャッシュラインの半分を浪費することは、私のアプリケーションのオプションではありません。いくつかのシーケンシャル メモリ領域を指すオブジェクトにポインターを格納できます。これはメモリを浪費し、このリージョンでは別の構文を使用せざるを得なくなります。
私は現在それをどのように行っていますか:私はフォームのコンテナを持っています:
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.
};
タイプ T のいくつかのセルのデータを保存します。セルごとにいくつかのフラグが必要で、現在 std::bitset を使用してそれらを保存しています。つまり、このビットセットをセル単位の形式で保存する必要があります。
*cell_member1[cell0] ... cell_memberM[cell0] ... cell_member1[cellN] .. cell_memberM[cellN]*
データへのアクセスも提供する次のクラスに、セルごとにどれだけのデータを格納する必要があるかを説明しています。
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;
そして、私はそれを次のように使用します:
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;
}
}
長所:
データはブロック単位の形式であり、これは私が必要としていたものです。
構文は問題ありません。
問題:
私のクラスはやりすぎです: ユーザーにデータへのアクセスを提供し、コンテナーに保存する必要があるデータの量を提供し、データ構造 (ツリー) のためにデータを移動/コピー/交換する方法を提供します... )...
イテレータなしでは STL アルゴリズムを使用できません。イテレータにセル インデックスを格納させ、その中に CellAccessor クラスを再実装することでイテレータを実装しました (悪い! DRY!)。
ビットセットはまだセル単位の形式で保存されています。ブロック単位のデータ構造のビットセットを再実装できます...
data と maxNoCells は静的変数ですが、必要に応じて通常のメンバー変数にすることもできます。
質問:「オブジェクト」(またはオブジェクトによって概念的に理解されているもの) をブロック単位の形式で保存し、ベクトルなどの std コンテナーに保存されているかのようにアクセスする効率的な方法はありますか?