RISC-V Optimization Guide(笔记)

官网发表的文章地址:RISC-V Optimization Guide
B站有人做过解读视频,这篇文章也是看视频时做的笔记:视频地址

一、标量整数优化

1.1 常量的具体化

使用lui/addiw将立即数加载至寄存器,当立即数低12位的最高位为1时,需要特殊处理,提前补值0x800

具体的推导过程见:https://zhuanlan.zhihu.com/p/374235855

1.2 有效使用x0寄存器

1)将目标寄存器置0

正确做法如下:

mv x10, x0
or
li x10, 0

不要使用下面方法,因为会有一个读寄存器x10额外的开销。

xor x10, x10, x10
and x10, x10, x0
andi x10, x10, 0
sub x10, x10, x10

2)善于将x0作为指令的源操作数

x0可以折叠到任何指令中作为源操作数(有些情况还可以用作目的操作数),所以避免使用额外指令将0先加载到寄存器中。 下表列出了可以通过谨慎使用 x0 来消除临时寄存器的情况。
在这里插入图片描述

1.3 有效利用指令的立即数域

尽量将范围符合立即数域编码的常量编码到立即数域,而不是通过额外的指令加载到寄存器再使用。

比如访问一个数组,a0存放的是数组基地址,8offset。应该用:

ld t0, 8(a0)

而不是:

addi a1, a0, 8
ld t0, (a1)

这部分的扩展资料:https://www.bilibili.com/video/BV1pN411H7Y3

1.4 善于利用常量池

对64bit的立即数常量,使用常量池。如果该常量在程序中用到多次,收益则会更大。

将一个64bit的数加载至寄存器:

li a0, 0x123456789abcde1

其背后的逻辑可能是,要耗费32byte:

lui a0,0x92
addiw a0,a0,-1493
slli a0,a0,0xc
addi a0,a0,965
slli a0,a0,0xd
addi a0,a0,-1347
slli a0,a0,0xc
addi a0,a0,-543

如果我们使用常量池,常量可以在16个字节中加载,8个字节用于常量,8个字节用于加载它所需的指令。

1:auipc a0, %pcrel_hi(large_constant)ld a0, %pcrel_lo(1b)(a0)
....section .rodata
.p2align 3
large_constant:.dword 0x123456789abcde1

1.5 使用典型的mov指令

使用汇编器 MV 助记符(可转换为 ADDI rd, rs1, 0)将值从一个寄存器复制到另一个寄存器。 例如使用:

mv x10, x11

优先于以下任何指令:

or x10, x11, x0
ori x10, x11, 0
xor x10, x11, x0
xori x10, x11, 0

1.6 使用条件mov代替分支指令

处理器进行分支预测的代价是很高的,一旦分支预测错误,就需要清空流水线,重新开始读取。通过把分支指令替换成条件move是编译器的常见优化,实际上是将控制流的依赖转换成数据流的依赖。而这种转换通常会用到zicond 扩展,也就是czero.eqzczero.nez指令。

czero.eqz rd, rs1, rs2	// if rs2==0 then rd=rs1 else rd=0
czero.nez rd, rs1, rs2	// if rs2!=0 then rd=rs1 else rd=0

原始分支指令版本:

  beqz a0, 1fli a0, constant1j 2f
1:li a0, constant2
2:

初步优化后的指令版本:

li t2, constant1
li t3, (constant2 - constant1)
czero.nez t3, t3, a0
add a0, t3, t2

如果能确定constant112bit范围,还持续优化:

li t3, (constant2 - constant1)
czero.nez t3, t3, a0
addi a0, t3, constant1

如果没有zicond 扩展指令,还可以使用seqz和逻辑组合来达成条件move

li t2, constant1
li t3, constant2
seqz t0, a0			// if a0==0 then t0=1 else t0=0
addi t0, t0, -1		// if t0==1 then t0置为全0 else t0置为全1
xor t1, t2, t3
and t1, t1, t0
xor a0, t1, t3		// if t1是全0 then result=t3 else result=t2

1.7 代码段的Padding

分支跳转的目标或者函数地址的起始能够Padding在一个Cache行的开头地址上,对于整个跳转来说都是比较好的操作。所以在编译一个函数时,会在其结束的时候进行Padding从而占满Cache行,或者在函数的开头Padding让其位于Cache行的开头地址。

  • 函数之间对齐padding使用0 (illegal instruction)
  • 函数内部对齐padding使用Nop或C.Nop
  • 因为在函数内部的执行频率高,乱序执行情况下,流水线在遇见非法指令后可能就不执行后续指令了
  • 而控制流如果传递到函数之间,极有可能是程序出错了,非法指令反而会帮助debug

1.8 将字符数组对齐到更大的对齐单位

如果CPU不支持快速非对齐访问,那么在连续访问字符数组(元素大小8bit)时,lw/sw指令从4字节边界访问,ld/sd8字节边界访问,效率会高很多。

1.9 使用移位指令取出前导位和末尾位(leading/trailing bits)

如将x5的低12位取出:

slli  x6, x5, 20
srli x7, x6, 20

而不是:

lui x6, 1
addi x7, x6, -1
and x8, x7, x5

二、标量浮点优化

2.1 与整数优化相似的部分

  1. 尽量使用尽可能短的代码序列实现同样的功能。
  2. 加载立即数0的折叠。
  3. 内存访问对齐。

2.2 高效控制舍入模式

为什么要舍入? 因为单精度浮点数只取23位尾数(除去隐藏位),而一些运算不可避免的得到的尾数会超过23位,因此需要考虑舍入。

RISC-V的浮点计算舍入模式有两种:静态模式和动态模式。

  • 静态舍入模式:浮点指令的编码中有3位作为舍入模式域,RISC-V架构支持如下五种合法的舍入模式。除此之外,如果舍入模式编码为101或者110 ,则为非法模式;如果舍入模式编码111 ,则意味着使用动态舍入模式。
    在这里插入图片描述
    并不是所有的指令都有舍入模式,根据指令编码格式,以下的指令存在舍入模式的
    浮点运算指令:fadd fsub fmul fdiv fsqrt
    浮点乘加指令:fmadd fmsub fnmadd fnmsub
    浮点转换指令:fcvt.w.s fcvt.s.w fcvt.uw.s fcvt.s.uw
    
  • 动态舍入模式:如果使用动态舍入模式,则使用fcsr寄存器中的舍入模式域。fcsr 寄存器包含舍入模式域。不同的舍入模式编码同样如上图所示,仅支持五种合法的舍入模式。如果 fcsr 寄存器中的舍入模式域指定为非法的舍入模式,则后续浮点指令会产生非法指令异常。

浮点优化,尽可能使用静态模式:

fadd.s f10, f10, f11, rtz

而不是通过csr读写指令来设置FPCSR.FRM:

csrrwi t0, frm, 1      ; 1 = rtz
fadd.s f10, f10, f11
fsrm t0

三、向量优化

3.1 保留v0寄存器作为mask register来使用

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《操作系统实践-基于Linux应用与内核编程》第10章-Linux综合应用

前言: 内容参考《操作系统实践-基于Linux应用与内核编程》一书的示例代码和教材内容,所做的读书笔记。本文记录再这里按照书中示例做一遍代码编程实践加深对操作系统的理解。 引用: 《操作系统实践-基于Linux应用与内核编程》 作者:房胜、李旭健、黄…

【MySQL】5. 数据类型

数据类型 1. 数据类型分类 2. 数值类型 2.1 tinyint类型 数值越界测试: mysql> use tt; Database changed mysql> create table t1(-> num tinyint-> ); Query OK, 0 rows affected (0.01 sec)mysql> insert into t1 values(-128); Query OK, 1 r…

创新指南|制药行业如何拥抱生成式AI在新药发现与开发中突破获益

生成式AI在药物发现中的应用可加速药物研发过程,并可能降低成本。通过利用GenAI,制药公司能在早期药物发现和开发中实现更快的成果,这包括从目标识别、验证,到优化的多个环节。 AI有潜力在药物筛选和优先排序、目标识别及验证、药…

【SpringBoot3】整合Druid数据源和Mybatis 项目打包和运行

文章目录 一、整合Druid数据源二、整合Mybatis2.1 MyBatis整合步骤2.1 Mybatis整合实践2.1 声明式事务整合配置2.1 AOP整合配置 三、项目打包和运行命令启动和参数说明 总结web 与 springboot 打包区别JDK8的编译环境 执行17高版本jar 一、整合Druid数据源 创建模块 &#xff1…

STM32(TIM定时器中断)

理论知识 定时器定时中断 接线图 定时器工作配置步骤 定时中断和内外时钟源选择 定时器中需要使用的函数 程序实现效果: void TIM_DeInit(TIM_TypeDef* TIMx); **// 恢复定时器的缺省配置**void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef*TIM…

MySQL语法分类 DQL(3)排序查询

为了更好的学习这里给出基本表数据用于查询操作 create table student (id int, name varchar(20), age int, sex varchar(5),address varchar(100),math int,english int );insert into student (id,name,age,sex,address,math,english) values (1,马云,55,男,杭州,66,78),…

Tiktok在线网页版和Tiktok安卓解锁版教程(Tiktok免登录国内直接看)

TikTok和抖音作为众所周知的一对兄弟,所开创的市场前景不可估量。它们不仅颠覆了很多传统认知,也直接让更多人接受了这些新奇事物。然而,TikTok的版本受限于国外,需要特定网络环境,一旦识别为国内,将无法使…

Mock.js了解(Mock就是模拟一个后端,Postman模拟前端)

JSON5 Node.js Vue CLI与Mock.js Jquery与Mock.js Mock与分页

桌面待办,电脑桌面怎么设置待办事项

在忙碌的工作生活中,我们经常会有许多事情需要处理,为了提高工作效率和管理时间,很多人都有一套自己的桌面待办事项管理方法。那么,如何利用电脑桌面待办事项来提高工作效率,电脑桌面怎么设置待办事项呢? …

C语言例:设 int a=11; 则表达式 a+=a-=a*a 的值

注&#xff1a;软件为VC6.0 代码如下&#xff1a; #include<stdio.h> int main(void) {int a11, b;b (aa-a*a); //a*a121 -->a-121结果为a-110 -->a-110结果为a-220printf("表达式aa-a*a 的值为&#xff1a; %d\n",b);return 0; } //优先级&#x…

免费接口调用 招标信息自动抽取|招标信息|招标数据解析接口

一、开源项目介绍 一款多模态AI能力引擎&#xff0c;专注于提供自然语言处理&#xff08;NLP&#xff09;、情感分析、实体识别、图像识别与分类、OCR识别和语音识别等接口服务。该平台功能强大&#xff0c;支持本地化部署&#xff0c;并鼓励用户体验和开发者共同完善&#xf…

复习 --- windows 上安装 git,使用相关命令

文章目录 很少使用windows的git工具&#xff0c;这次借助这个任务&#xff0c;记录下使用过程&#xff0c;其他的等有空在整理。 其中&#xff0c;还使用了浏览器的AI小助手&#xff0c;复习了git相关的命令&#xff1a;图片放最后