来自原始缓冲区的 Eigen::Map' 矩阵给出 OBJECT_ALLOCATED_ON_STACK_IS_TOO_BIG

Eigen::Map'd matrix from raw buffer gives OBJECT_ALLOCATED_ON_STACK_IS_TOO_BIG

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

#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.如果我做一个简单的 C += A*B 一切都按预期工作。

在示例中,我从 Eigen 分配的矩阵映射原始缓冲区,但原则上它可以是任何东西(例如 std::vector)的原始缓冲区。

知道这里发生了什么吗?据我所知,这里的所有内容都应该是堆分配的,即使不是,为什么 C += A*B 可以使用映射内存矩阵而 C+= (alpha*A)*(beta*B) 不能?

干杯,

尼克

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

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

改为使用动态大小的 Maps:

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 的大小,只是表明临时文件最终是如何分配的。

对于如此大的矩阵,最好使用 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>) 在编译时是已知的.好难过