LLVM中矩阵Matrix的实现分析

news2024/11/19 17:30:00

1 背景说明

Clang提供了C/C++语言对矩阵的扩展支持,以方便用户使用可变大小的二维数据类型来实现计算,目前该特性还是实验版,设计和实现都在变化中。LLVM目前设计为支持小型列矩阵(column major),其对矩阵的设计基于向量。为用户提供向量代码生成功能,能够减少不必要的内存访问,并且提供用户友好的接口。本文基于LLVM13以Intel X86架构为平台,来实验和分析LLVM编译器对矩阵运算的基础支持。
LLVM目前支持的矩阵间运算包括矩阵转置、矩阵加减法和矩阵乘法,矩阵除法只支持矩阵除以标量,不支持矩阵与矩阵相除。

2 功能实现

2.1 数据实现

矩阵支持的元素类型有整型、单精、双精、半精,用户可以通过matrix_type属性来定义各种不同元素类型不同行列数的矩阵。
在这里插入图片描述

2.2 内建支持

2.2.1 Clang

Clang前端提供了3个内建接口供用户使用,如下表所示,目前矩阵的load和store都是按列操作,未来对行矩阵(row major)的支持也在计划中。

接口名称功能参数返回值
__builtin_matrix_column_major_load按列加载矩阵T *ptr:加载起始地址 size_t row:矩阵行数 size_t col:矩阵列数 size_t columnStride:跨步,指加载完一列数据后ptr自增的元素个数row行col列T元素类型矩阵
__builtin_matrix_column_major_store按列存储矩阵dx4x4_t matrix:待存储的矩阵 T *ptr:存储起始地址 size_t columnStride:跨步,指ptr存储完一列数据后自增的元素个数
__builtin_matrix_transpose矩阵转置dx4x4_t matrix:待转置的矩阵转置后的矩阵

当从一个基地址*ptr加载矩阵时,列矩阵(column major)是一列一列依次赋值的。下图展示了跨步为5时, 列矩阵(column major)的load和store操作。
在这里插入图片描述

2.2.1 LLVM

LLVM后端提供了int_matrix_multiply内建函数,该接口并未在Clang前端暴露,用户在编码矩阵乘计算时,只需要使用乘法符号(*),LLVM在创建中间表示(IR)的时候会自动创建对该内建函数的调用。

2.3 算法流程

通过clang -cc1 -fenable-matrix -emit-llvm matrix_load.c编译代码,生成的IR中包含对内建接口的调用。
在这里插入图片描述
在这里插入图片描述
Clang中的3个内建接口是用户编码过程中可以直接调用的,所以它们的IR的生成过程是走的内建解析,矩阵乘在用户编码时并不是函数调用,所以走的是标量乘法解析,在其中增加了矩阵类型的分支。
在这里插入图片描述
以下是CreateColumnMajorLoad函数的详细代码。
在这里插入图片描述
实际上LLVM X86中矩阵操作最终都是转成向量来做的,所以这一步生成的IR还需要进一步降级,使用向量指令替换内建接口的调用指令,并且对已经替换的接口指令做了缓存,下次再次调用同类矩阵接口指令,可以直接从缓存中取向量指令,而不需要再去执行矩阵转向量的操作,从而提高性能。通过clang -fenable-matrix -emit-llvm –S matrix_load.c可以生成最终的IR,该命令行在Clang.cpp中给后端传递了-enable-matrix选项,该选项会在PassBuilder.cpp中添加LowerMatrixIntrinsicsPass优化,该优化将上述IR中的矩阵操作转换成向量操作。
在这里插入图片描述
假设一个4x4的矩阵如下所示,现在需要以11为基址,从中计算一个2x3的子矩阵,LLVM提供了computeVectorAddr函数来计算子矩阵的地址。BasePtr指矩阵(向量)起始元素地址,VecIdx指当前向量在子矩阵中的index,Stride指加载时的跨步,NumElements指每个向量中的元素个数,EltType指元素类型。函数中VecStart变量通过VecIdx跟Stride相乘得到,该变量表示当前计算的向量的起始位置,比如示例矩阵中13对应的VecStart = 2 * 4 = 8;如果VecStart = 0,则代表是第0列向量,此时将BasePtr赋给VecStart作为新向量的起始地址,如果VecStart != 0,则通过Builder.CreateGEP()创建一个IR操作getelementptr,取对应列首地址,以此类推;最后通过Builder.CreatePointerCast()把上述列的首地址转成VecPtrType对应的向量地址,这样新向量就生成了。
在这里插入图片描述
在这里插入图片描述
有了以上computeVectorAddr函数,只要给定相应的参数,就可以计算出一个向量(子矩阵)的地址了,调用过程如下所示。LLVM中在load矩阵时,便是根据矩阵的列数(行矩阵根据行数)来依次生成原始矩阵对应的向量,比如一个int 5x4的矩阵会被load为4个<5 x i32>向量,而一个int 4x5的矩阵会被load为5个<4 x i32>向量。
在这里插入图片描述
矩阵的加减法是对应位置元素加减,所以在拆成向量后,即可以复用向量的加减操作,乘法与加减法的运算规则不同,所以需要对拆出来的向量进一步处理。假设矩阵A乘矩阵B等于矩阵C,元素类型都是int,如下所示。
在这里插入图片描述
load的时候矩阵A被拆成了4个<5 x i32>向量,矩阵B被拆成了5个<4 x i32>向量,LLVM会根据拆分后的向量的地址把向量进一步整合成适合目标架构宽度乘法运算的新向量,具体是通过目标架构寄存器宽度和当前向量元素类型宽度的比值得出要整合的向量的大小。当前x86架构这里的VF返回的是4,也就是说以4个元素为一组向量整合矩阵A和矩阵B,整合完成后进行向量运算,如下所示。

<A00,A10,A20,A30>x<B00,B00,B00,B00>+
<A01,A11,A21,A31>x<B10,B10,B10,B10>+
<A02,A12,A22,A32>x<B20,B20,B20,B20>+
<A03,A13,A23,A33>x<B30,B30,B30,B30>=
<C00,C10,C20,C30>

可以看到,C40还没有被计算出来,此时就剩1个元素需要计算了,于是把矩阵A每一列最后1个元素和矩阵B第一列每个元素转成<1 x i32>向量,然后对应元素相乘后再相加,从而完成计算。

<A40>x<B00>+<A41>x<B10>+<A42>x<B20>+<A43>x<B30>=<C40>

那么是如何得到最后的向量是<1 x i32>类型呢?如前所述,VF决定了整合的向量的元素个数,LLVM通过以下代码来实现,C = 4,R = 5,当I + BlockSize > R时,会不断对BlockSize取半,确保了除1以外的其它向量元素个数都是偶数,BlockSize初值为4,第0列第0行时,I + BlockSize = 4 < R,所以取4个元素作为一组向量,第0列第5行时,I + BlockSize = 8 > R,BlockSize在while循环中最终算得1后满足条件。
在这里插入图片描述
实际在IR中使用了shufflevector、extractelement和insertelement来生成新的向量。如下代码所示,%col.load为矩阵A第0列元素,%col.load2为矩阵A第1列元素,%col.load10为矩阵B第0列元素,从%col.load中取4个元素依次与%col.load10中的第0个元素相乘,从%col.load2中取4个元素依次与%col.load10中的第1个元素相乘,再把乘积依次相加。
在这里插入图片描述

keyvalue
%block< A00,A10,A20,A30>
%splat.splat< B00,B00,B00,B00>
%block23< A01,A11,A21,A31>
% splat.splat25< B10,B10,B10,B10>

在这里插入图片描述

3 举例说明

3.1 Matrix load

编写一段测例matrix_load.c如下所示,通过matrix_type属性指定行列数定义5x5的int类型矩阵,跨步设为5,这样正好把数组中的25元素都加载到矩阵中。
在这里插入图片描述
生成的IR正如前文所述,首先每次从数组element中取出5个元素组成一个向量。
在这里插入图片描述
再依次存到m1对应的矩阵的地址里。
在这里插入图片描述
生成的汇编部分截图如下所示,可以看到,长度25的数组元素被拆成4+4+4+4+4+4+1取出来放到对应地址,存的时候按照4+1作为一组存了5次,正好构成5x5的矩阵。
在这里插入图片描述

3.3 Matrix transpose

矩阵转置同样也是将矩阵操作转换为向量操作,编写测例如下所示。
在这里插入图片描述
以下是生成的部分IR,从矩阵中load出每一列向量,第0列第0个元素插入到%4代表的<5 x double>新向量第0位置,第1列第0个元素插入到%4第1位置,第2列第0个元素插入到%4第2个位置,依次提取,按序插入,这样就完成了矩阵的转置操作。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/106776.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java字节码介绍

Java字节码 概述 学习 Java 的都知道&#xff0c;我们所编写的 .java 代码文件通过编译将会生成 .class 文件&#xff0c;最初的方式就是通过 JDK 的 javac 指令来编译&#xff0c;再通过 java 命令执行 main 方法所在的类&#xff0c;从而执行我们的 Java 程序。而在这中间所…

【矩阵论】6. 矩阵理论——算子范数

6.2 算子范数 6.2.1 定义 CnC^nCn 上任一向量范数 ∥X∥V\Vert X\Vert_V∥X∥V​ 都产生一个矩阵范数 ∥A∥max⁡x≠0{∥AX∥V∥X∥V}\Vert A\Vert\max_{x\neq 0}\limits \{\frac{\Vert AX\Vert_V}{\Vert X\Vert_V}\}∥A∥x0max​{∥X∥V​∥AX∥V​​} ,X∈CnX\in C^nX∈Cn…

Linux 管理联网 测试网络连通性 -- Ping 命令详解 tracepath命令详解

测试网络的连通性 # 我们测试网络的连通性&#xff0c;一般就是使用的 PIng 命令 Ping &#xff1a; 一般格式 &#xff1a; ping [ 选项 ] < 目标主机名 或 IP 地址 > 常用选项 &#xff1a; - c 数字 &#xff1a; 用于 设定本命令发出的 ICMP 消息包的…

限量,Alibaba首发“Java成长笔记”,差距不止一点点

前言 本文是为了帮大家快速回顾了Java中知识点&#xff0c;这套面试手册涵盖了诸多Java技术栈的面试题和答案&#xff0c;相信可以帮助大家在最短的时间内用作面试复习&#xff0c;能达到事半功倍效果。 本来想将文件上传到github上&#xff0c;但由于文件太大有的都无法显示…

CentOS7使用yum安装Golang(超详细)

使用yum安装Golang前言一、go语言介绍二、yum安装golang1.安装go版本为1.19.41.1执行yum install go&#xff08;报错&#xff09;1.2配置go的安装源1.3执行yum install golang1.4查看go的安装版本2.安装go版本为 1.11rc2&#xff08;这个参考&#xff0c;不用操作&#xff09;…

Docker镜像的原理

centos7系统 包括2部分&#xff0c; linux内核&#xff0c;作用是提供操作系统的基本功能&#xff0c;和机器硬件交互&#xff0c;如何读取磁盘数据&#xff0c;管理网络&#xff0c;使用C编写的&#xff0c;由linus的开发团队&#xff0c;内核只提供操作系统的基本功能和特性…

修改嵌入式 ARM Linux 内核映像中的文件系统

zImage 是编译内核后在 arch/arm/boot 目录下生成的一个已经压缩过的内核映像。通常我们不会使用编译生成的原始内核映像 vmlinux&#xff0c;因其体积很大。因此&#xff0c;zImage 是我们最常见的内核二进制&#xff0c;可以直接嵌入到固件&#xff0c;也可以直接使用 qemu 进…

C++的OpenCV中cv::minAreaRect的返回角度的数值范围是多少?

版本不一样的时候&#xff0c;返回也不一样。 我使用opencv/4.5.5。 下图是使用minAreaRect判定的角度&#xff0c;可以看到&#xff0c;数值范围是[0,90]&#xff0c;看起来很离谱。 画出这张图使用的程序如下&#xff1a; C int main() {std::string prefix1 "/mn…

SpringMvc+Thymeleaf实现数据渲染

Thymeleaf是spring boot推荐使用的模板语法&#xff0c;它可以完全替代 JSP 。 从代码层次上讲&#xff1a;Thymeleaf是一个java类库&#xff0c;它是一个xml/xhtml/html5的模板引擎&#xff0c;可以作为mvc的web应用的view层。 Thymeleaf 提供spring标准方言和一个与 SpringMV…

Ui自动化概念+Web自动化测试框架介绍

目录 UI 1.UI自动化测试概念:我们先明确什么是UI 2.为什么对UI采用自动化测试? 3.什么项目适合做UI自动化测试? 4.UI自动化测试介入时机 5.UI自动化测试所属分类 Web自动化测试框架介绍 2.Selenium框架介绍及特点: Web自动化测试环境搭建 2.元素定位(一) idclassna…

【数据结构】栈与集合类Stack

目录 一、栈 二、Java中的集合类之Stack 1、介绍 2、构造方法 3、常用方法 1.push 2.pop 3.peek 4.search 5.empty 三、实现Stack 1、准备字段 2、实现判空 3、实现压栈 4、实现出栈 5、实现获取栈尾元素 6、指定元素到栈顶的距离 一、栈 栈(stack)是一种比较…

Redis高可用之哨兵模式(第二部分)

引言 接上一篇&#xff0c;今天我们来聊一聊Redis的高可用的第二个解决方案&#xff1a;哨兵模式。 一、Redis哨兵模式 哨兵模式&#xff08;sentinel&#xff09;是Redis提供的高可用的解决方案之一。由一个或者多个sentinel示例组成的sentinel系统&#xff0c;可以监听任意…

(Java高级教程)第二章Java多线程常见面试题-第二节:JUC(java.util.concurrent)

文章目录一&#xff1a;Callable接口二&#xff1a;ReentrantLock三&#xff1a;原子类四&#xff1a;信号量SemaphoreJUC&#xff1a;JUC是java.util.concurrent包的简称&#xff0c;目的就是为了更好的支持高并发任务。让开发者进行多线程编程时减少竞争条件和死锁的问题 一…

智己LS7发布,预售价格区间35-50万元

12月20日&#xff0c;智己首款中大型大五座SUV 智己LS7开启预售。动力配置&#xff1a; •最大零百加速4.5S&#xff1b; •峰值公里425kw&#xff0c;峰值扭矩725Nm&#xff1b; •提供90度和100度电池选项&#xff1b; •最大CLTC续航660km&#xff1b;空间配置&#xff1a; …

06. http协议基础,带你了解网络访问

06. http协议基础&#xff0c;带你了解网络访问 渗透测试学习路径 计算机基础网络基础WEB漏洞渗透测试 渗透测试和WEB安全漏洞的区别&#xff1f; 渗透测试包含WEB安全漏洞 WEB网站只是单一的网站服务&#xff0c;在渗透测试过程中可能不是攻击网站&#xff0c;而是寻找其他…

ElasticSearch全文检索原理及过程

倒排索引 ElasticSearch的搜索引擎中&#xff0c;每个文档都有一个对应的文档 ID&#xff0c;文档内容被表示为一系列关键词的集合。例如文档 1 经过分词&#xff0c;提取了 20 个关键词&#xff0c;每个关键词都会记录它在文档中出现的次数和出现位置。那么&#xff0c;倒排索…

差分---(小明的彩灯)蓝桥杯真题,差分思想很明确的模板

小明的彩灯题目描述暴力解法差分的思路和模板差分解法题目描述 小明拥有 N个彩灯&#xff0c;第 i个彩灯的初始亮度为 ai​。 小明将进行 Q次操作&#xff0c;每次操作可选择一段区间&#xff0c;并使区间内彩灯的亮度 x&#xff08;x可能为负数&#xff09;。 求 Q次操作后…

自动控制原理笔记-传递函数

目录 拉普拉斯反变换&#xff1a; 用拉普拉斯变换求解常微分方程的步骤&#xff1a; 部分分式展开法&#xff1a; 留数法&#xff1a; 零极点图: 传递函数 定义&#xff1a; 传递函数的标准形式&#xff1a; 传递函数的性质&#xff1a; 传递函数的局限性&#xff1a…

SOT23-6封装 小封装 超精简外围PD Sink端取电协议芯片

PD协议&#xff08;USB-PD&#xff09;的全名是USB Power Delivery&#xff0c;是由 USB-IF 组织制定的一种快速充电规范&#xff0c;是目前主流的快充协议之一&#xff0c;USB-PD 快充协议是以 Type-C 接口输出的&#xff0c;我们经常看到的华为笔记本配的Type-C 65W充电器就是…

【C语言】函数栈帧的创建和销毁

目录 1.函数栈帧的含义 概念 要用到的汇编语言的知识 示例 2.理解栈帧 2.1 main函数栈帧的创建 2.2 局部变量的创建 2.3 函数传参 2.4 调用函数 2.5 函数返回 一个.c文件在调用函数的时候&#xff08;包括main 函数&#xff09;&#xff0c;其内存中的栈区有什么变…