来自原始缓冲区的特征::映射矩阵给出了OBJECT_ALLOCATED_ON_STACK_IS_TOO_BIG

时间:2019-02-17 23:03:53

标签: c++ memory-management eigen eigen3

最近,我一直在处理从原始缓冲区派生的本征矩阵,我注意到这种奇怪的情况:

#include <eigen3/Eigen/Dense>

int main(int argc, char const *argv[]) {
    /* code */
    const int M = 320;
    const int N = 640;
    const int K = 320;
    const int alpha = 2;
    const int beta = 1;
    Eigen::Matrix<int32_t, Eigen::Dynamic,Eigen::Dynamic> A = Eigen::Matrix<int32_t, Eigen::Dynamic,Eigen::Dynamic>::Random(M,K);
    Eigen::Matrix<int32_t, Eigen::Dynamic,Eigen::Dynamic> B = Eigen::Matrix<int32_t, Eigen::Dynamic,Eigen::Dynamic>::Random(K,N);
    Eigen::Matrix<int32_t, Eigen::Dynamic,Eigen::Dynamic> C = Eigen::Matrix<int32_t, Eigen::Dynamic,Eigen::Dynamic>::Random(M,N);

    //Following http://eigen.tuxfamily.org/dox/TopicWritingEfficientProductExpression.html

    C.noalias() += (A*alpha)*(B*beta); //WORKS

    C.noalias() += A*B;

    Eigen::Map<Eigen::Matrix<int32_t, M, K, Eigen::ColMajor> > map_a(A.data());
    Eigen::Map<Eigen::Matrix<int32_t, K, N, Eigen::ColMajor> > map_b(B.data());
    Eigen::Map<Eigen::Matrix<int32_t, M, N, Eigen::ColMajor> > map_c(C.data());

    map_c.noalias() += map_a*map_b; //WORKS

    map_c.noalias() += (map_a*alpha)*(map_b*beta); //COMPILE ERROR HERE

    return 0;
}

如果矩阵尺寸较大,则无法在堆栈上进行分配,否则会得到OBJECT_ALLOCATED_ON_STACK_IS_TOO_BIG,因此我使用Eigen动态分配器。

但是,似乎如果我有一个原始缓冲区并将其映射到矩阵,由于编译错误,我将无法执行像gemm乘法(C+= (alpha*A)*(beta*B))这样的BLAS 3: 。如果我做一个简单的OBJECT_ALLOCATED_ON_STACK_IS_TOO_BIG,一切都会按预期进行。

在示例情况下,我映射了Eigen分配的矩阵中的原始缓冲区,但原则上它可以是任何东西(例如C += A*B)中的原始缓冲区。

有什么想法吗?据我所知,这里的所有内容都应该是堆分配的,即使没有分配,为什么std::vector不能与映射的内存矩阵一起工作,而C += A*B却不能呢?

干杯

尼克

2 个答案:

答案 0 :(得分:3)

对于大矩阵,最好使用Avi Ginsburg的答案中的运行时大小。话虽如此,我现在将解释Eigen内部的情况。问题是在矩阵产品实现中,我们有一个类似的分支(简化):

if(<too small>)
  lazyproduct::eval(dst, lhs, rhs);
else
  gemm::eval(dst,lhs, rhs);

如果乘积太小,则无需调用繁琐的“ gemm”例程,而是返回到基于系数的实现,在您的情况下:

map_c.noalias() += (map_a*alpha).lazyProduct(map_b*beta);

此路径不会将表达式重写为(alpha*beta)*(map_a*map_b),因此,为了避免多次重新计算map_a*alphamap_b*beta,该策略应支持它们在临时范围内...因此导致编译错误。

当然,在您的情况下,此路径将永远不会采用,并且如果您增加EIGEN_STACK_ALLOCATION_LIMIT,甚至会被编译器完全删除,因为条件if(<too small>)在编译时是已知的。真伤心。

答案 1 :(得分:1)

您的Map正在包装静态大小的矩阵,例如:

Eigen::Map<Eigen::Matrix<int32_t, M, K, Eigen::ColMajor> > 
                                  ^  ^

使用动态尺寸的Map代替:

Eigen::Map<Eigen::Matrix<int32_t, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor> > map_a(A.data(), M, K);
Eigen::Map<Eigen::Matrix<int32_t, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor> > map_b(B.data(), K, N);
Eigen::Map<Eigen::Matrix<int32_t, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor> > map_c(C.data(), M, N);

这并不意味着您可以更改Map的大小,而只是表明临时的最终分配方式。