我试图找出为任意数量的维度编写通用容器(向量,矩阵,高维对象)的最佳方法。每个维度的维度数量和元素数量应在编译时指定,如下所示:
3
给出了一个包含3个元素的向量10, 10
提供包含100个元素的矩阵7, 5, 3
给出了包含105个元素的二阶张量应该能够以简单的方式循环遍历所有元素以进行简单的操作:将所有(double
)元素乘以标量double
,在元素方面添加两个兼容的容器等。此外,一个人应该能够知道每个维度各自的索引,例如从(0, 0, 0)
到(6, 4, 2)
获取张量。
我认为可变参数模板参数是递归连接每个维度的迭代器的好工具。
template<
int N, // the number of elements to iterate for this dimension
int... otherN // the other dimension's lengths
> class dimIterator;
为了存储指向第一个元素的指针,我记得boxIterator
,它只是dimIterator
的包装
template<
typename dataType,
int... allN
> class boxIterator : public dimIterator<allN...> { // tried as member or inheritance
protected:
dataType* data;
public:
boxIterator(dataType* data) :
dimIterator<allN...>(),
data(data)
{
//cout << "constructing box iterator." << endl;
}
int getIndex() const {
int result = dimIterator<allN...>::getIndex();
return result;
}
dataType* getPtr() {
dataType* result = data + this->getIndex();
return result;
}
const dataType* getPtr() const {
dataType* result = data + this->getIndex();
return result;
}
bool isDone() const {return dimIterator<allN...>::isDone();}
boxIterator<dataType, allN...>& operator ++() {
dimIterator<allN...>::operator++();
return *this;
}
dataType& operator *() {return *(this->getPtr());}
const dataType& operator *() const {return *(this->getPtr());}
};
template<
int N, // the number of elements to iterate for this dimension
int... otherN // the other dimension's lengths
> class dimIterator : public dimIterator<otherN...> { // tried as member or inheritance
protected:
int i;
public:
dimIterator() :
dimIterator<otherN...>(),
i(0)
{
//cout << "constructing level with " << N << " gridpoints." << endl;
}
int getIndex() const {
int result = i + N*dimIterator<otherN...>::getIndex();
return result;
}
bool isDone() const {return dimIterator<otherN...>::isDone();}
dimIterator<N, otherN...>& operator ++() {
if(i<N-1) {
++i;
}
else {
i = 0;
dimIterator<otherN...>::operator++();
}
return *this;
}
};
template<int N> // stop recursion if only one dimension left
class dimIterator<N> {
protected:
int i;
public:
dimIterator() :
i(0)
{
//cout << "constructing last level with " << N << " gridpoints." << endl;
}
int getIndex() const {
int result = i;
return result;
}
bool isDone() const {return ( i>= N );}
dimIterator<N>& operator ++() {
++i;
return *this;
}
};
最初,我对这种方法很满意,因为它允许为任意数量的维度编写相同的高级迭代器循环。可以很容易地获得每个维度的索引和类似的东西,例如特定维度中给定框的间距。
然而,尽管我尝试使用模板和内联函数来实现迭代器逻辑,但编译器并没有将这些东西优化为与执行嵌套for
循环一样快的东西。我进行了一次测试,其中包含未初始化的双倍空乘和可重复的获取
for
循环for
循环的等价物?与g++ -O3 -std=c++11
一起编译。版本为g++ (GCC) 4.8.1
。
完整代码(从上面部分重复):
#include <iostream>
using namespace std;
template<int first, int... other>
class integerPack {
public:
constexpr static int length = 1 + integerPack<other...>::length;
constexpr static int product = first*integerPack<other...>::product;
};
template<int only>
class integerPack<only> {
public:
constexpr static int length = 1;
constexpr static int product = only;
};
template<
int N, // the number of elements to iterate for this dimension
int... otherN // the other dimension's lengths
> class dimIterator;
template<
typename dataType,
int... allN
> class boxIterator : public dimIterator<allN...> { // tried as member or inheritance
protected:
dataType* data;
public:
boxIterator(dataType* data) :
dimIterator<allN...>(),
data(data)
{
//cout << "constructing box iterator." << endl;
}
int getIndex() const {
int result = dimIterator<allN...>::getIndex();
return result;
}
dataType* getPtr() {
dataType* result = data + this->getIndex();
return result;
}
const dataType* getPtr() const {
dataType* result = data + this->getIndex();
return result;
}
bool isDone() const {return dimIterator<allN...>::isDone();}
boxIterator<dataType, allN...>& operator ++() {
dimIterator<allN...>::operator++();
return *this;
}
dataType& operator *() {return *(this->getPtr());}
const dataType& operator *() const {return *(this->getPtr());}
};
template<
int N, // the number of elements to iterate for this dimension
int... otherN // the other dimension's lengths
> class dimIterator : public dimIterator<otherN...> { // tried as member or inheritance
protected:
int i;
public:
dimIterator() :
dimIterator<otherN...>(),
i(0)
{
//cout << "constructing level with " << N << " gridpoints." << endl;
}
int getIndex() const {
int result = i + N*dimIterator<otherN...>::getIndex();
return result;
}
bool isDone() const {return dimIterator<otherN...>::isDone();}
dimIterator<N, otherN...>& operator ++() {
if(i<N-1) {
++i;
}
else {
i = 0;
dimIterator<otherN...>::operator++();
}
return *this;
}
};
template<int N> // stop recursion if only one dimension left
class dimIterator<N> {
protected:
int i;
public:
dimIterator() :
i(0)
{
//cout << "constructing last level with " << N << " gridpoints." << endl;
}
int getIndex() const {
int result = i;
return result;
}
bool isDone() const {return ( i>= N );}
dimIterator<N>& operator ++() {
++i;
return *this;
}
};
template<
int... allN
> class box {
public:
constexpr static int dimension = integerPack<allN...>::length;
constexpr static int NN = integerPack<allN...>::product;
template<typename dataType>
using iterator = boxIterator<dataType, allN...>;
};
template<typename dataType, typename boxType>
class boxQuantity {
public:
typedef typename boxType::template iterator<dataType> iterator;
constexpr static int dimension = boxType::dimension;
constexpr static int NN = boxType::NN;
typedef boxQuantity<dataType, boxType> thisClass;
protected:
boxType mybox;
dataType* data;
iterator myit;
public:
boxQuantity(
const boxType& mybox
) :
mybox(mybox),
data(new dataType[NN]),
myit(data)
{
cout << "I am a quantity of dimension " << dimension
<< " with " << NN << " gridpoints." << endl;
}
virtual ~boxQuantity() {
delete[] data;
}
iterator begin() const {
iterator it(data);
return it;
}
dataType& operator [] (int i) {return data[i];}
const dataType& operator [] (int i) const {return data[i];}
// iterator syntax with recursive for-like logic: 5 s
virtual thisClass& operator *= (const thisClass& rhs) {
thisClass& lhs = *this;
for(iterator it=lhs.begin(); !it.isDone(); ++it) {
lhs[myit.getIndex()] *= rhs[myit.getIndex()];
}
return *this;
}
// plain nested native for loops: 2.5 s
/*virtual thisClass& operator *= (const thisClass& rhs) {
thisClass& lhs = *this;
for(int yindex=0; yindex<1000; ++yindex) {
for(int xindex=0; xindex<1000; ++xindex) {
lhs[yindex*1000 + xindex] *= rhs[yindex*1000 + xindex];
}
}
return *this;
}*/
};
typedef box<1000, 1000> boxType;
typedef boxQuantity<double, boxType> qType;
int main() {
boxType qBox;
qType q1(qBox);
qType q2(qBox);
for(int i=0; i<2000; ++i) {
q1 *= q2;
}
return 0;
}
virtual
关键字的感谢您的回答。代码是通过一个更大的程序来组装的,其中virtual
并不像我的例子那样明显无用。我预计virtual
主要影响调用函数/运算符的时间,但不会影响执行它的时间。据我所知,执行时间的主要部分是在循环中使用,我忘了删除virtual
。但是,在我的情况下,删除virtual
并不会带来显着的性能优势? (我在你的建议后对它进行了测试。)
我可能错过了要指出的是,我想迭代连续的内存块和中的所有元素,以便能够获得每个元素的n维索引。此外,我希望能够在给定维度的方向上访问邻居元素。也许删除这些功能可能是一个坏主意,例如(在dimIterator
内)
template<int dindex>
int getCoord() const {
if(1 == dindex) {
return i;
}
return otherDims.template getCoord<dindex-1>();
}
我之前的想法是
但是,我更喜欢上面的一般解决方案。我无法想象它必须慢得多,因为我没有看到与嵌套循环相比的有效差异。
答案 0 :(得分:2)
boxQuantity
的{{1}}将不会内联,这会阻止大量优化。在您的示例中,您不是从此类派生的。删除此virtual operator*=()
运算符可以使代码内联并使用SIMD指令:
virtual