Eigen 运算符如何在内部解释为 MKL 函数?
How Eigen operators are interpreted as MKL functions internally?
Eigen 是一个非常方便的库,可以用简洁和人类直观的方式表达数学公式。我知道 Eigen 有惰性求值概念,可以表示表达式 类 中的一系列操作,并在必要时高效地求值。我也知道 Eigen 可以与 MKL 一起使用。但是,我很好奇可以将什么样的表达式转移到 MKL cblas 调用中。什么情况下不能转让?是否有任何一般规则可以帮助我弄清楚什么是可转让的?
通常,我对以下情况感到好奇:
MatrixXd A, B, C;
VectorXd a, b, c;
double w1, w2, w3;
b += w1 * A * a; // can be done through dgemv
b += w1 * A.transpose() * a; // can be done through dgemv
C += w2 * A * B; // can be done through dgemm
C += w2 * A.transpose() * B; // can be done through dgemm
C += w2 * A. topRows(5).transpose() * B; // can be done through dgemm
D = A * B * C; // cannot be done in one func call through cblas
注:注释不是Eigen迁移结果。相反,这是理想的结果。不知道Eigen能不能转过来
随之而来的还有一个问题:Eigen什么时候会在这些操作链中分配临时内存?是否有任何一般规则可以帮助我确定是否发生了任何分配?
首先,为了避免在你的例子中临时出现,你需要写
b.noalias() += w1 * A * a;
这是因为 Eigen 在编译时无法判断 b
、A
和 a
没有别名,因此将乘积计算为临时值。
See here for details.
Godbolt-demo:https://godbolt.org/g/VSfekp(注意-DEIGEN_USE_BLAS
本质上等同于-DEIGEN_USE_MKL_ALL
,但godbolt目前不支持MKL)
对于您的实际问题:要确定操作是否需要临时,您可以使用 -DEIGEN_RUNTIME_NO_MALLOC
进行编译,并包围您认为不应由
分配的部分
Eigen::set_is_malloc_allowed(false); // mallocs after this cause assertions
// some code
Eigen::set_is_malloc_allowed(true); // mallocs are allowed again
当然,这要求结果对象在进入该部分之前具有正确的大小。
此外,这对固定大小的表达式没有帮助,因为它们永远不会分配堆内存,但对于固定大小的表达式,使用 MKL 通常不如 Eigen(根据大小,将完全展开必要的操作) ).
为了完成 chtz 的回答,你所有的假设都是正确的(前提是你添加了 .noalias()
装饰器),更一般地说,任何看起来像 gemv/gemm 的表达式或子表达式都将变成一个单一的call,包括transpose,blocks,conjugate等。你可以在这个unit test中找例子计算临时的数量,所以0
意味着一个单一的blas-like调用,1
意味着将在类似 blas 的调用之前或之后创建一个临时文件等
Eigen 是一个非常方便的库,可以用简洁和人类直观的方式表达数学公式。我知道 Eigen 有惰性求值概念,可以表示表达式 类 中的一系列操作,并在必要时高效地求值。我也知道 Eigen 可以与 MKL 一起使用。但是,我很好奇可以将什么样的表达式转移到 MKL cblas 调用中。什么情况下不能转让?是否有任何一般规则可以帮助我弄清楚什么是可转让的?
通常,我对以下情况感到好奇:
MatrixXd A, B, C;
VectorXd a, b, c;
double w1, w2, w3;
b += w1 * A * a; // can be done through dgemv
b += w1 * A.transpose() * a; // can be done through dgemv
C += w2 * A * B; // can be done through dgemm
C += w2 * A.transpose() * B; // can be done through dgemm
C += w2 * A. topRows(5).transpose() * B; // can be done through dgemm
D = A * B * C; // cannot be done in one func call through cblas
注:注释不是Eigen迁移结果。相反,这是理想的结果。不知道Eigen能不能转过来
随之而来的还有一个问题:Eigen什么时候会在这些操作链中分配临时内存?是否有任何一般规则可以帮助我确定是否发生了任何分配?
首先,为了避免在你的例子中临时出现,你需要写
b.noalias() += w1 * A * a;
这是因为 Eigen 在编译时无法判断 b
、A
和 a
没有别名,因此将乘积计算为临时值。
See here for details.
Godbolt-demo:https://godbolt.org/g/VSfekp(注意-DEIGEN_USE_BLAS
本质上等同于-DEIGEN_USE_MKL_ALL
,但godbolt目前不支持MKL)
对于您的实际问题:要确定操作是否需要临时,您可以使用 -DEIGEN_RUNTIME_NO_MALLOC
进行编译,并包围您认为不应由
Eigen::set_is_malloc_allowed(false); // mallocs after this cause assertions
// some code
Eigen::set_is_malloc_allowed(true); // mallocs are allowed again
当然,这要求结果对象在进入该部分之前具有正确的大小。
此外,这对固定大小的表达式没有帮助,因为它们永远不会分配堆内存,但对于固定大小的表达式,使用 MKL 通常不如 Eigen(根据大小,将完全展开必要的操作) ).
为了完成 chtz 的回答,你所有的假设都是正确的(前提是你添加了 .noalias()
装饰器),更一般地说,任何看起来像 gemv/gemm 的表达式或子表达式都将变成一个单一的call,包括transpose,blocks,conjugate等。你可以在这个unit test中找例子计算临时的数量,所以0
意味着一个单一的blas-like调用,1
意味着将在类似 blas 的调用之前或之后创建一个临时文件等