[Eigen中文文档] 深入了解 Eigen - Eigen内部发生了什么(一)

这篇具有很好参考价值的文章主要介绍了[Eigen中文文档] 深入了解 Eigen - Eigen内部发生了什么(一)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

文档总目录

英文原文(What happens inside Eigen, on a simple example)

考虑以下示例程序:

#include<Eigen/Core>
 
int main()
{
    int size = 50;
    // VectorXf is a vector of floats, with dynamic size.
    Eigen::VectorXf u(size), v(size), w(size);
    u = v + w;
}

本页的目标是了解 Eigen 如何编译,假设启用了 SSE2 矢量化(GCC 选项 -msse2)。

为什么讨论这个问题

也许你认为上面的示例程序很简单,编译它应该不涉及任何非常有趣的事情。在开始之前,让我们解释一下编译它时的一些不普通的部分——也就是生成优化代码——以便我们在这里解释Eigen的复杂性真正有用。

看一下这行代码:

u = v + w;   //   (*)

编译它的第一件重要的事情是数组应该只遍历一次,就像:

for(int i = 0; i < size; i++) u[i] = v[i] + w[i];

问题是,如果我们创建一个简单的 C++ 库,其中 VectorXf 类有一个返回 VectorXf 的运算符+,那么代码行 (*) 将相当于:

VectorXf tmp = v + w;
VectorXf u = tmp;

显然,这里引入临时tmp是没有用的。它对性能有非常糟糕的影响,首先是因为 tmp 的创建需要在此上下文中进行动态内存分配,其次是因为现在有两个 for 循环:

for(int i = 0; i < size; i++) tmp[i] = v[i] + w[i];
for(int i = 0; i < size; i++) u[i] = tmp[i];

如果我们需要进行两次数组遍历而不是一次,这对性能来说非常糟糕,因为这意味着我们需要做很多冗余的内存访问。

编译上述程序的第二个重要方面是正确使用SSE2指令。请注意,Eigen还支持AltiVec,我们在这里讨论的所有内容也适用于AltiVec

AltiVec一样,SSE2是一组指令,允许一次处理128位数据。由于float占32位,这意味着SSE2指令可以一次处理4个float。这意味着,如果正确使用,它们可以使我们的计算速度提高4倍。

然而,在上面的程序中,我们选择了size=50,因此我们的向量由50个float组成,而50不是4的倍数。这意味着我们不能指望使用SSE2指令来执行所有的计算。我们应该着眼于次优解,即使用SSE2指令处理前48个系数,因为48是小于50的最大4的倍数,然后单独处理第49个和第50个系数。类似这样:

for(int i = 0; i < 4*(size/4); i+=4) u.packet(i)  = v.packet(i) + w.packet(i);
for(int i = 4*(size/4); i < size; i++) u[i] = v[i] + w[i];

因此,让我们逐行查看示例程序,然后跟随 Eigen 进行编译。

构建向量

我们分析一下第一行代码:

Eigen::VectorXf u(size), v(size), w(size);

首先,VectorXftypedef 定义类型:

typedef Matrix<float, Dynamic, 1> VectorXf;

类模板Matrixsrc/Core/util/ForwardDeclarations.h中声明,有6个模板参数,但最后3个参数由前3个自动确定,所以暂时不解释它们。在这里,Matrix<float,Dynamic,1>表示一个浮点数矩阵,具有1列和动态的行数。

Matrix 类继承了基类 MatrixBase。可以说 MatrixBase 统一了矩阵/向量和所有表达式类型,下面将详细介绍。

当只执行 Eigen::VectorXf u(size); 时,调用的构造函数是 Matrix::Matrix(int),位于 src/Core/Matrix.h 中。除了一些断言之外,它所做的只是构造 m_storage 成员,其类型为 DenseStorage<float, Dynamic, Dynamic, 1>

你可能会想,把存储放在一个单独的类中是不是设计过度了?原因是Matrix类模板涵盖了各种矩阵和向量:固定大小和动态大小。这两种情况下的存储方法不同。对于固定大小的矩阵,矩阵系数被存储为一个普通的成员数组。对于动态大小的矩阵,系数将被存储为指向动态分配数组的指针。因此,需要将存储从Matrix类中抽象出来。这就是DenseStorage的作用。

让我们看看在 src/Core/DenseStorage.h 中的这个构造函数。这里有许多DenseStorages的部分模板特化,单独处理维度为Dynamic或在编译时固定的情况。我们要看的部分特化是:

template<typename T, int Cols_> class DenseStorage<T, Dynamic, Dynamic, Cols_>

这里,调用的构造函数是 DenseStorage::DenseStorage(int size, int rows, int columns),其中 size=50rows=50columns=1

这个构造函数如下:

inline DenseStorage(int size, int rows, int) : m_data(internal::aligned_new<T>(size)), m_rows(rows) {}

这里,m_data成员是矩阵系数的实际数组,它是动态分配的。与其调用new[]malloc(),Eigen 定义了自己的internal::aligned_new,它位于 src/Core/util/Memory.h中。它的作用是,如果启用了向量化,那么它会使用平台特定的调用来分配一个128位对齐的数组,因为对于使用SSE2AltiVec进行向量化非常有用。如果未启用向量化,则相当于标准的new[]

构造函数还将m_rows成员设置为size。请注意,这里没有m_columns成员:实际上,在DenseStorage的这个部分特化中,列数是编译时确定的,因为Cols_模板参数与Dynamic不同。在这种情况下,Cols_1,这意味着我们的向量只是一个只有1列的矩阵。因此,没有必要将列数存储为运行时变量。

当调用VectorXf::data()获取系数数组的指针时,它会返回DenseStorage::data(),后者返回m_data成员。

当调用VectorXf::size()获取向量的大小时,实际上是调用了基类MatrixBase中的一个方法。它确定向量是一个列向量,因为ColsAtCompileTime==1(这来自于typedef VectorXf中的模板参数)。它推断出大小就是行数,所以返回VectorXf::rows(),后者返回DenseStorage::rows(),后者返回构造函数设置的m_rows成员。文章来源地址https://www.toymoban.com/news/detail-544066.html

到了这里,关于[Eigen中文文档] 深入了解 Eigen - Eigen内部发生了什么(一)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • [Eigen中文文档] Reshape操作

    文档总目录 英文原文(Reshape) 从 Eigen3.4 开始,Eigen 发布了将矩阵或向量重塑为不同大小的便捷方法。所有的操作可以通过 DenseBase::reshaped(NRowsType,NColsType) 和 DenseBase::reshaped() 两个函数完成。这些函数并不直接改变原有的变量,而是返回一个重塑后的变量副本。 二维Reshape 更一

    2023年04月17日
    浏览(27)
  • [Eigen中文文档] 线性代数与分解

    文档总目录 英文原文(Linear algebra and decomposition) 本节说明如何求解线性系统,计算各种分解,如 LU 、 QR 、 SVD 、 特征分解 …… 求解基本线性系统 问题 :有一个方程组,写成矩阵方程如下: A x = b Ax = b A x = b 其中 A A A 和 b b b 是矩阵(作为一种特殊情况, b b b 也可以是一个

    2024年02月07日
    浏览(32)
  • “深入剖析JVM内部机制:了解Java虚拟机的工作原理“

    标题:深入剖析JVM内部机制:了解Java虚拟机的工作原理 摘要:本文将深入剖析JVM内部机制,详细介绍Java虚拟机的工作原理。我们将探讨JVM的组成部分、类加载过程、内存管理、垃圾回收以及即时编译等关键概念。此外,还将提供示例代码来帮助读者更好地理解JVM的内部机制

    2024年02月11日
    浏览(32)
  • 深入了解HTTP 500内部服务器错误的原因及解决方法

    HTTP 状态代码提供有关在线请求是否成功的信息,如果不成功,则错误是什么。 但是错误消息并不总是很清楚。 “500 内部服务器错误”尤其如此。 此消息表示在连接到服务器期间发生错误,并且无法访问所请求的页面。 但是,它不会告诉你为什么会这样。 幸运的是,有不

    2024年02月08日
    浏览(49)
  • 深入 Hyperf:HTTP 服务启动时发生了什么?

    当我们创建 Hyperf 项目之后,只需要在终端执行 php bin/hyperf.php start 启动命令,等上几秒钟,就可以看到终端输出的 Worker 进程已启动,HTTP 服务监听在 9501 端口的日志信息。 打开浏览器访问 http://127.0.0.1:9501 ,不出意外的话,页面会显示 Hello Hyperf ,说明 HTTP 服务已经在工作了

    2024年02月05日
    浏览(22)
  • MySQL 篇-深入了解索引的内部结构(哈希表、红黑树与 B+ 树)

    🔥博客主页: 【 小扳_-CSDN博客】 ❤感谢大家点赞👍收藏⭐评论✍    文章目录         1.0 索引概述         2.0 索引内部结构特点         2.1 那么哪些数据结构,能够加快查询速度呢?         2.2 二叉搜索树、AVL 树存储结构特点         2.3 红黑树存储结构

    2024年03月14日
    浏览(48)
  • 记录--让我们来深入了解一下前端“三清”是什么

    在前端开发中,我们经常听到关于“三清”的说法,即 window 、 document 、 Object 。这三者分别代表了 BOM(浏览器对象模型)、DOM(文档对象模型)以及 JS 的顶层对象。在这个体系中,我们通过 JavaScript 与浏览器进行深度交互,构建出丰富多彩的网页应用。同时,JS 类也是前端

    2024年02月05日
    浏览(46)
  • 什么是云仓?如何深入了解云仓?云仓运营模式是怎样?

    云仓简单来说就是一种较为先进的第三方仓储,与传统的电商仓库不同的是“云”。“云”就是最近几年特别流行的“云计算”,云仓说白了是在软件上利用云计算以及现代管理方式,硬件上依托仓储设施进行货物流通的一个第三方仓储物流。 云仓的优势近近几年都已经被体

    2024年02月13日
    浏览(33)
  • 矩阵分解及其Eigen实现

    主要是用来记录自己的学习过程,内容也主要来自于网上的各种资料,然后自己总结而来,参考的资料都以注明,感谢这些作者的分享。如果内容有误,请大家指点。 定义        将矩阵等价为两个矩阵 L L L 和 U U U 的乘积 ,其中 L L L 和 U U U 分别是单位下三角矩阵和上三角

    2024年02月03日
    浏览(33)
  • Eigen-高级矩阵初始化

    Eigen提供了一个逗号初始化语法,允许用户轻松设置矩阵、向量或数组的所有系数。简单地列出系数,从左上角开始,从左到右,从上到下。对象的大小需要事先指定。如果你列出的系数太少或太多,Eigen就会报错。 此外,初始化列表的元素本身可以是向量或矩阵。 一个常见

    2024年04月27日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包