[Eigen中文文档] 深入了解 Eigen - 惰性求值与混叠(Aliasing)

文档总目录

英文原文(Lazy Evaluation and Aliasing)

执行摘要:Eigen具有智能的编译时机制,可以实现惰性求值并在适当的情况下删除临时变量。它会自动处理大多数情况下的混叠问题,例如矩阵乘积。自动行为可以通过使用MatrixBase::eval()MatrixBase::noalias()方法手动覆盖。

当你编写涉及复杂表达式的代码时,例如:

mat1 = mat2 + mat3 * (mat4 + mat5);

Eigen会自动为每个子表达式确定是否将其计算为临时变量。确实,在某些情况下,将子表达式计算为临时变量更好,而在其他情况下则最好避免这样做。

没有表达式模板的传统数学库总是将所有子表达式计算为临时变量。因此,在以下代码中,

vec1 = vec2 + vec3;

传统的库会将vec2 + vec3计算为一个临时变量vec4,然后将vec4复制到vec1中。这显然是低效的:数组被遍历两次,有很多无用的加载/存储操作。

基于表达式模板的库可以避免将子表达式计算为临时变量,这在许多情况下会使速度显著提高。这被称为延迟/惰性求值,因为表达式会尽可能晚地进行求值。在Eigen中,所有表达式都是延迟求值的。更准确地说,一旦表达式分配给矩阵,它便开始被求值。在此之前,除了构建抽象表达式树外,什么也不会发生。然而,与大多数其他基于表达式模板的库不同,Eigen可能会选择将某些子表达式计算为临时变量。这有两个原因:首先,纯延迟求值并不总是性能的最佳选择;其次,纯延迟求值可能会非常危险,例如矩阵乘法:如果将mat = mat * mat直接在目标矩阵中求值矩阵乘积,则会得到错误的结果,因为矩阵乘积的方式不同。

出于这些原因,Eigen具有智能的编译时机制,可以自动确定哪些子表达式应计算为临时变量。如下示例中:

mat1 = mat2 + mat3;

Eigen选择不引入任何临时变量。因此,数组只被遍历一次,产生了优化的代码。如果你真的想强制立即求值,可以使用eval()函数,如下:

mat1 = (mat2 + mat3).eval();

如下是一个更复杂的示例:

mat1 = -mat2 + mat3 + 5 * mat4;

在这个例子中,Eigen也不会引入任何临时变量,从而产生一个单一的融合计算循环,这显然是正确的选择。

哪些子表达式将被计算为临时变量?

默认的计算策略是将操作融合到一个循环中,除了少数情况下,Eigen默认选择该策略。

第一种情况

Eigen选择计算子表达式的第一种情况是当它看到一个赋值操作a = b;,且表达式b带有 在赋值之前计算 的标志,最重要的这样的表达式是矩阵乘积表达式。例如,当执行以下操作时:

mat = mat * mat;

Eigen会将mat * mat计算成一个临时矩阵,然后将其复制到原始的mat中。这可以保证正确的结果,因为我们之前看到,惰性计算在矩阵乘积中会产生错误的结果。这也不会花费太多的计算代价,因为矩阵乘积本身的代价要高得多。请注意,这个临时矩阵仅在计算时引入,也就是在此示例中的=中。表达式mat * mat仍然返回一个抽象的乘积类型。

如果你明确计算结果不会与乘积的操作发生混叠现象,并想强制使用惰性计算,则可以使用.noalias()。下面是一个例子:

mat1.noalias() = mat2 * mat2;

在这里,由于我们知道mat2不是与mat1相同的矩阵,因此我们知道惰性求值不会有危险,可以强制使用惰性求值。具体来说,noalias()的作用是绕过evaluate-before-assigning标志。

第二种情况

Eigen选择评估子表达式的第二种情况是当它看到嵌套表达式,例如a + b时,其中b已经是一个具有evaluate-before-nesting标志的表达式。同样,这类表达式中最重要的例子是矩阵乘积表达式。例如,当执行以下操作时:

mat1 = mat2 * mat3 + mat4 * mat5;

在这个例子中,矩阵乘积mat2 * mat3mat4 * mat5会被分别求值为临时矩阵,然后在mat1中进行求和。实际上,为了有效地计算矩阵乘积,需要在手头有一个目标矩阵内进行求值,而不是像简单的 点积 那样。然而,对于小矩阵,你可能希望使用lazyProduct()来强制执行基于 点积 的惰性求值。再次强调,重要的是要理解,这些临时矩阵仅在计算时创建,即在operator=中。请参见TopicPitfalls_auto_keyword了解与此说明相关的常见陷阱。

第三种情况

第三种情况是,当Eigen的成本模型显示,如果将子表达式求值为临时变量,操作的总成本将会减少时,Eigen会选择求值该子表达式。实际上,在某些情况下,如果一个中间结果的计算成本足够高,而且被重复使用的次数足够多,则该中间结果值得被 “缓存”。以下是一个例子:

mat1 = mat2 * (mat3 + mat4);

在这里,假设矩阵至少有2行2列,表达式mat3 + mat4的每个系数在矩阵乘积中会被使用多次。与每次计算总和相比,一次计算并将其存储在一个临时变量中要好得多。Eigen理解这一点,并在求值乘积之前将mat3 + mat4求值为一个临时变量。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Eigen3Config.cmake和eigen3-config.cmake是用于配置Eigen3库的包配置文件。这些文件包含了关于Eigen3库的信息,如库的路径、头文件路径和编译选项等。这些文件通常由Eigen3库的安装程序生成,并用于帮助CMake在编译时找到并链接Eigen3库。引用 当你在编译使用Eigen3库的项目时,如果编译器无法找到这些文件,就会出现报错信息。这可能是由于Eigen3库的版本与你的项目所需的版本不兼容,或者是库的安装位置或配置有问题。引用 如果你想解决这个问题,可以首先确保你已经正确地安装了Eigen3库,并且安装路径是正确的。然后,你可以尝试在CMakeLists.txt文件中添加以下内容,以帮助CMake找到Eigen3库的位置: ``` find_package(Eigen3 3.1 REQUIRED) ``` 这将告诉CMake在编译时查找版本为3.1的Eigen3库。如果你的Eigen3库版本不是3.1,可以根据实际情况修改这个版本号。引用 如果你仍然遇到问题,你可以尝试手动设置Eigen3库的路径,方法是使用cmake-gui或在CMakeLists.txt中添加以下内容: ``` set(Eigen3_DIR /path/to/eigen3) ``` 将`/path/to/eigen3`替换为你Eigen3库的实际路径。引用 综上所述,Eigen3Config.cmake和eigen3-config.cmake是用于配置Eigen3库的包配置文件。如果编译时无法找到这些文件,你可以尝试检查库的版本兼容性、安装路径和配置,并根据需要进行相应的修改。<span class="em">1</span><span class="em">2</span><span class="em">3</span><span class="em">4</span>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

万俟淋曦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值