为什么 Eigen 对混叠做出不一致的默认假设?

问题描述 投票:0回答:1

作为 Eigen 的新人,有一些事情我正在努力接受。

使用矩阵乘法,Eigen 默认创建一个临时值以避免混叠问题:

matA = matA * matA; // works fine (Eigen creates a temporary before assigning)

如果可以安全地假设没有锯齿,我们可以使用

.noalias()
来进行优化:

matB = matA * matA; // sub-optimal (due to unnecessary temporary)
matB.noalias() = matA * matA; // more efficient

因此,Eigen 默认情况下会避免做出不安全的假设,除非明确告知假设没有别名是安全的。到目前为止一切顺利。

但是,对于许多其他表达式,例如

a = a.transpose()
Eigen 默认情况下会做出不安全的假设(不存在别名问题),并且需要显式干预来避免这种不安全的假设:

a = a.transpose().eval(); // or alternatively: a.transposeInPlace()

因此,如果我们关心效率,那么仅在存在潜在混叠时小心是不够的,而在不存在潜在混叠时仅小心也是不够的。当存在潜在的混叠时,我们必须小心both,当没有潜在的混叠时,我们必须小心and,并根据表达式是否涉及矩阵乘法来决定是否需要特殊干预。 Eigen 界面中的这种“默认期望混合”是否有一些设计原理?

c++ eigen eigen3
1个回答
1
投票

评论给出了问题的答案,总结如下:

Eigen 文档有效地提到您不应该这样做

a = a.transpose()
,但他们也提到 Eigen 使用运行时断言来检测这一点并退出并显示一条消息。问题在于,正如他们所说,一般来说,在编译时无法检测到别名,并且他们更注重效率。在创建临时对象时执行
b = a.transpose()
效率非常低。

本质上这是一种设计选择。一般来说,本征更喜欢效率,但矩阵乘积是意外混叠的常见来源(并且额外的副本通常比乘积便宜得多)。为了更好地转置,请使用

a.transposeInPlace();
。大多数其他表达式几乎没有别名问题,例如,在添加两个矩阵时,只有访问重叠(但不相等)的内存区域才会出现问题。

理由很简单,产品比临时创建 + O(n^2) 复制要贵得多。是的,Eigen 就像任何其他 BLAS 库一样,使用 O(n^3) 矩阵乘积算法。理论上,更高效的算法可以带来巨大的乘积(n>3000 甚至更多),更重要的是,它们会在浮点运算(浮点/双精度)时产生巨大且不可接受的舍入误差。

这个决定是在 Eigen 开发的早期阶段做出的,10 年后我宁愿无条件假设没有混叠以保持一致性。

© www.soinside.com 2019 - 2024. All rights reserved.