我正在编写模板化矩阵类,当从运算符返回值时我得到堆栈溢出:+, - ,*用于更大的矩阵。我宁愿以某种方式通过引用返回来缓解堆栈并避免额外复制,但是,我将不得不返回使用 new 构造的对象并打破“使用删除”的一般规则适用于每个新“。由于复制开销和堆栈限制问题,我无法按值返回,而且由于内存泄漏,我也无法通过引用返回,那么我应该做什么呢?
这是我的产品功能(Matrix包含2D阵列元素):
template<typename T, unsigned int n, unsigned int m> template<unsigned int m2>
Matrix<T,n,m2> Matrix<T,n,m>::operator*(Matrix<T,m,m2>& M) {
T prod[n][m2];
if(n*m < GPUAccelerationThreshold)
for(int i = 0; i < n; i++)
for(int j = 0; j < m2; j++) {
prod[i][j] = elems[i][0] * M(0, j);
for(int p = 1; p < m; p++)
prod[i][j] += elems[i][p] * M(p, j);
}
else {
array_view<T, 2> product(n, m2, *prod);
array_view<T, 2> a(n, m, *elems);
array_view<T, 2> b(m, m2, M.elems[0]);
parallel_for_each(
product.extent,
[=](index<2> idx) restrict(amp) {
int row = idx[0];
int col = idx[1];
for (int inner = 0; inner < m; inner++) {
product[idx] += a(row, inner) * b(inner, col);
}
}
);
product.synchronize();
}
return Matrix<T,n,m2>(prod);
}
我正在写这个课,因为我想在GPU上增加一些矩阵运算(使用MS放大器)。我已经搜索了一个现有的解决方案,找到了GPU加速线性代数库,但我在其中找不到的是带有+, - ,*运算符的简单矩阵类。也许有人可以推荐我吗?
答案 0 :(得分:3)
三个快速评论:
Matrix
类使用了动态
分配。您没有显示Matrix
课程,但是如果您的课程
数据是:
T myData[n][m];您可能希望将其更改为:
std::vector myData;,将其初始化为构造函数中的
n * m
大小,并且
计算operator[]
中的单个索引(应该是。{
如果要进行任何边界检查,则返回代理)。
或者,您可以使用operator()( int i, int j )
访问元素:是myMatrix( i, j )
还是。{
myMatrix[i][j]
优先考虑访问取决于您的要求。
虽然这个解决方案稍微增加了总内存使用量(但是
非常轻微),它将堆栈占用空间减少到几个
十几个字节,无论矩阵的大小。
T
类型的所有矩阵都具有
相同的类型。因此,您可以访问矩阵的内部
从你的会员功能返回,你不再需要了
中间T prod[n][m2]
。当然,你可以做所有
Matrix
朋友的实例化,或者只是使用访问权限
用于设置值的函数。无论如何,你不想要一个
中间T prod[n][m2]
;这不仅需要很多
堆栈内存,这意味着你必须复制结果。
operator*
不返回矩阵,而是返回帮助器
类,顺序如下:
模板
classMaxMultiply
{
L const * myLhs;
R const * myRhs;
上市:
typedef T value_type;
MatrixMultiply(L const&amp; lhs,R const&amp; rhs)
:myLhs(&amp; lhs)
,myRhs(&amp; rhs)
{
}
int getX()const
{
return myLhs-&gt; getX();
}
int getY()const
{
return myRhs-&gt; getY();
}
T get(int i,int j)const
{
return calculateIJ(myLhs,myRhs);
}
};
然后,您提供模板化构造函数和赋值运算符
它使用getX()
,getY()
和get( i, j )
。您的
operator*
也是一个模板,它返回一个
MatrixMultiply
:
模板
MatrixMultiply函数
operator *(L const&amp; lhs,R const&amp; rhs)
{
返回MatrixMultiply(lhs,rhs);
}
(请注意,L::value_type
和R::value_type
不是。{
相同,这将无法编译。这是你想要的,除了
错误信息将远非明确。)
结果是你从未真正构建中间体,
临时矩阵。
可以想象,上述解决方案大大简化了。
你需要额外的代码来处理错误,但我没有
认为并行化是微不足道的。但它避免了
构建所有中间矩阵,即使是复杂的
表达式。
(使用抽象基类可以使用相同的技术,
说MatrixAccessor
,使用纯虚拟吸气剂,并导出
Matrix
以及MatrixMultiply
等所有帮助者
它。恕我直言,这是更可读,和错误消息
从编译器肯定会更容易理解。结果将是
只要编译器实际内联所有成员就一样
功能。但那是一个很大的 if ,因为可以
重要功能嵌套。)
答案 1 :(得分:1)
没有简单的方法可以解决这个问题。您不能将堆栈局部变量作为参考返回,因为返回时变量后面的内存将消失。所以你必须在某处拥有一些专用存储。它不一定来自new / delete,但在复制数据时你需要有某种存储空间。
一种解决方案当然要有三操作数操作,所以代替:
a = b + c;
你使用一个函数:
添加(a,b,c);
其中a,b和c是引用。
它确实使代码变得更加混乱,但我想不出任何更明显的方法来解决问题 - 如果你不想编写自己的分配器/删除函数(或垃圾收集器)。
答案 2 :(得分:1)
实际上我无法完全理解你的想法......二元运算符需要两个参数并创建结果。实际上,您可以看到您正在返回一个新创建的对象。所以这是编写程序的唯一方法:分配内存,使用它,然后删除它。实际上我甚至不明白你的构造函数是做什么的。如果它只是将指针复制到“prod”,那么当你从函数返回时结果矩阵会被破坏,因为一旦函数返回(因为它是在堆栈上创建的),“prod”内存将被删除。所以你也不能通过引用返回它。
我看到解决方案在矩阵构造函数中分配内存的方式。如果您根据矩阵大小将其作为模板,则可以从模板参数中获知矩阵的大小(我发现制作以矩阵大小为参数的模板非常奇怪。这有什么意义?)。因此,您在构造函数中使用“new”分配内存,并在析构函数中使用“delete”将其删除。所以这个内存将根据在OOP中运行良好的RAII方法进行分配。然后实现setElement(i,j,value)等方法,并在二元运算符中设置新创建矩阵中的元素并将其返回。
但是我想要你照顾一些问题。复制构造函数必须真正复制矩阵而不仅仅是一个指针(或者几个析构函数会尝试销毁相同的内存),或者你可以编写实际上在更改时复制矩阵的“lazy copy”模型(参见wiki)。或者您可以将复制构造函数设置为私有而不实现(以防止复制)。 如果您不允许创建诸如“setElement”之类的方法,因为您不希望库的用户更改矩阵值,则可以访问私有数据和方法(即使在我们作为参数或新创建的对象中) )在这样的运算符中因为你在类方法中。
如果您需要将原始指针传递给其他计算函数,就像在“else”部分中完成一样,您可以从只复制指针的指针创建构造函数(但这是危险的方法。如果您传递指针,不能无处访问它,因为矩阵类现在是老板)或完全复制数据(虽然速度较慢但你可以从堆栈或指针传递指针,你需要控制之后),这取决于你的愿望,析构函数将清除它矩阵破坏。或者您甚至可以创建私有方法,例如“getRawMatrix()”,它将返回来自矩阵的数据的原始指针,并将此指针传递给您的计算函数,甚至只需获取原始数据指针,因为您位于矩阵类的方法中
通常你在构造函数中分配内存并制作“lazy copy”模型,因为矩阵可能很大。允许在类内部访问私有数据和成员,这就是所创建的类。我希望它会有用..