tesseract引擎RVV代码学习笔记
Tesseract 是一个开源的 OCR(Optical Character Recognition,光学字符识别)引擎,可将图像中的文本转换为机器可读的文本格式。由于组内曾经有同事为这个项目贡献了RVV(RISC-V Vector)的代码,我打算单独拎出来学习一下。
PR链接在此:Add RISC-V V support by hleft · Pull Request #4346 · tesseract-ocr/tesseract。由于有一定的篇幅,我只挑了汇编部分来进行阅读。
static int DotProduct(const int8_t *u, const int8_t *v, int num) {
int total = 0;
asm __volatile__ (
" .option arch, +v \n\t"
" vsetvli t0,zero,e32,m8,ta,ma \n\t"
" vmv.v.i v0,0 \n\t"
"1: \n\t"
" vsetvli t0,%[num],e8,m2,ta,ma \n\t"
" vle8.v v16,0(%[u]) \n\t"
" vle8.v v24,0(%[v]) \n\t"
" sub %[num],%[num],t0 \n\t"
" vwmul.vv v8,v24,v16 \n\t"
" add %[u],%[u],t0 \n\t"
" add %[v],%[v],t0 \n\t"
" vsetvli zero,zero,e16,m4,tu,ma \n\t"
" vwadd.wv v0,v0,v8 \n\t"
" bnez %[num],1b \n\t"
" vsetvli t0,zero,e32,m8,ta,ma \n\t"
" vmv.s.x v8,zero \n\t"
" vredsum.vs v0,v0,v8 \n\t"
" vmv.x.s %[total],v0 \n\t"
: [u] "+r" (u),
[v] "+r" (v),
[num] "+r" (num),
[total] "+r" (total)
:
: "cc", "memory"
);
return total;
}
这个函数主要用来实现一维向量乘积,采用了内嵌汇编的方式进行优化,除了用RVV汇编,还可以用封装好的riscv_vector.h接口,不过这里使用了最原始的汇编,我们分段阅读。
" vsetvli t0,zero,e32,m8,ta,ma \n\t"
" vmv.v.i v0,0 \n\t"
vsetvli是跟向量寄存器组有关的指令,这里设置向量长度为最大(zero表示根据配置自动计算),然后再对向量寄存器初始化为0。
"1: \n\t"
" vsetvli t0,%[num],e8,m2,ta,ma \n\t"
" vle8.v v16,0(%[u]) \n\t"
" vle8.v v24,0(%[v]) \n\t"
" sub %[num],%[num],t0 \n\t"
1这里表示进入了循环,用RVV的好处就是循环过程中步长会自动调整,比如说长度为18,如果每次步长为8,传统的SIMD需要8+8+3,8是可以用向量指令集去实现,但是3这里就需要采用普通for循环手写,但是RVV会自动忽略掉这个过程,不用担心越界,只需关注循环内部本身即可,因为硬件会根据情况自动调整为向量步长为3。另外,这里vsetvli加载了num操作数到t0寄存器,寄存器存的是向量步长,e8代表元素大小,相当于int8类型,因为函数参数传入的也是int8 *的指针。
" vwmul.vv v8,v24,v16 \n\t"
" add %[u],%[u],t0 \n\t"
" add %[v],%[v],t0 \n\t"
vwmul.vv指令首先对v24和v16这两个向量寄存器组里面的元素求和,然后扩展位宽到16位,存到v8向量寄存器里面。之所以要扩展位宽,是因为8位的数乘8位的数可能会变成16位的数。add这里就是分别对传入的两个函数参数进行指针的移动。
" vsetvli zero,zero,e16,m4,tu,ma \n\t"
" vwadd.wv v0,v0,v8 \n\t"
" bnez %[num],1b \n\t"
到了这一步,vsetvli重新将操作数类型变为16位的,因为刚刚上面乘法的时候已经扩展为16位了。接着vwadd.wv将v8的结果累加到v0向量寄存器组,由于最后返回值是32位的,这里同样用了扩展位宽的加法。
" vsetvli t0,zero,e32,m8,ta,ma \n\t"
" vmv.s.x v8,zero \n\t"
" vredsum.vs v0,v0,v8 \n\t"
" vmv.x.s %[total],v0 \n\t"
最后是这里,vsetvli重新调整向量寄存器中的元素为32位,接下来对v8清零,将v0寄存器组的所有元素归约求和到v0寄存器中,最后再将结果移到变量total当中,这个函数到此就实现完成了。
这么看来,RVV在自动调整步长方面还是很有优势的(比起SIMD),不过由于汇编比较晦涩难懂,所以下次打算寻找RVV C-Intrinsics的代码进行解剖。
tesseract引擎RVV代码学习笔记的更多相关文章
- Learning Memory-guided Normality代码学习笔记
Learning Memory-guided Normality代码学习笔记 记忆模块核心 Memory部分的核心在于以下定义Memory类的部分. class Memory(nn.Module): ...
- DeepLearnToolbox-master代码学习笔记
卷积神经网络(CNN)博大精深,网上资料浩如烟海,让初学者无从下手.笔者以为,学习编程还是从代码实例入们最好.目前,学习CNN最好的代码实例就是,DeepLearnToolbox-master,不用装 ...
- html5游戏引擎-Pharse.js学习笔记(一)
1.前言 前几天随着flappy bird这样的小游戏的火爆,使我这种也曾了解过html5技术的js业余爱好者也开始关注游戏开发.研究过两个个比较成熟的html5游戏引擎,感觉用引擎还是要方便一些.所 ...
- 动力学仿真引擎ODE的学习笔记,C#演示(一)
®版权声明:本文为博主原创文章,未经博主允许不得转载. 一.ODE介绍与平台搭建. 接触到动力学仿真引擎, 是因为笔者的一款PLC仿真软件需要3D仿真.我需要达到的效果是,以3D方式构建出工控行业中常 ...
- C# 好代码学习笔记(1):文件操作、读取文件、Debug/Trace 类、Conditional条件编译、CLS
目录 1,文件操作 2,读取文件 3,Debug .Trace类 4,条件编译 5,MethodImpl 特性 5,CLSCompliantAttribute 6,必要时自定义类型别名 目录: 1,文 ...
- 1.JAVA中使用JNI调用C++代码学习笔记
Java 之JNI编程1.什么是JNI? JNI:(Java Natibe Inetrface)缩写. 2.为什么要学习JNI? Java 是跨平台的语言,但是在有些时候仍然是有需要调用本地代码 ( ...
- APM代码学习笔记1
libraries目录 传感器 AP_InertialSensor 惯性导航传感器 就是陀螺仪加速计 AP_Baro 气压计 居然支持BMP085 在我印象中APM一直用高端的MS5611 AP_Co ...
- boost timer代码学习笔记
socket连接中需要判断超时 所以这几天看了看boost中计时器的文档和示例 一共有五个例子 从简单的同步等待到异步调用超时处理 先看第一个例子 // timer1.cpp: 定义控制台应用程序的入 ...
- Java模板引擎之Freemarker 学习笔记 一
什么是Freemarker Freemarker是模板引擎,不是Web框架,只是视图层的组件,官网是 https://freemarker.apache.org/ Freemarker原理 数据模型+ ...
- cc代码学习笔记1
#define #define INT32 int #define INT8 char #define CHAR char #define SSHORT signed short #define IN ...
随机推荐
- CDS标准视图:设备信息 I_EquipmentData
视图名称:I_EquipmentData 视图类型:基础视图 视图代码: 点击查看代码 @EndUserText.label: 'Equipment Data' @VDM.viewType: #COM ...
- C#正则表达式匹配候选词
来自文心一言(多次修改才正确的): public App() { string input = "例子文字{备选,:'词1t324|备选词2gdfg,该方法|备选词3dsfdsf}继续{备选 ...
- 如何快速的开发一个完整的iOS直播app(创建房间)
直播(创建房间) 1.进入主播界面,首先创建房间 2.设计房间模型(key,名称),key作为房间的唯一标识,用来找到房间 3.用socket创建房间,导入socket.io框架 4.一般一个客户端一 ...
- shell脚本中的循环(for循环,while循环)及break、continue的用法示例
Shell脚本中也算是一门简易的编程语言了,当然循环是不能缺少的.常用到的循环有for循环和while循环.下面就分别介绍一下两种循环的结构. [for循环]: Shell脚本中的for循环示例: # ...
- Collection的子接口之一:List 接口
List 接口概述: 鉴于Java中数组用来存储数据的局限性,我们通常使用List替代数组 List集合类中 元素有序.且可重复,集合中的每个元素都有其对应的顺序索引. List容器中的元素都对 ...
- oracle数据导入导出,备份还原命令expdp&impdp(只导出元数据,不导出表数据,最全,最完善的步骤)
感谢金龙鱼先生分享,原文来自https://blog.csdn.net/kou869929526/article/details/125791113 一,编码要求以及数据库版本要求 检查数据库版本(用 ...
- java中的文件流File
数据 + 流(转)操作 IO I表示的是:输入Input O表示的是:Output Stream表示流转 java中的管道不止一个.并且管道有可能不是一样的. 有的管道粗有的管道细 File的常用方法 ...
- .NET Core常用集合的几个坑
C#中的常见集合 注意,箭头线不代表继承关系,只代表功能上的加强,如有错误,欢迎指出. 泛型集合时间复杂度 集合类型 添加 删除 查找 访问(索引/键) 遍历 备注 List<T> O(1 ...
- DexExpress Wpf BackstageItemWithImage
参考链接: https://docs.devexpress.com/WPF/DevExpress.Xpf.Ribbon.BackstageItemWithImage.GlyphStyle 设置 Bac ...
- 【忍者算法】从生活到代码:解密链表大数相加的美妙算法|LeetCode第2题"两数相加"
从生活到代码:解密链表大数相加的美妙算法 从超市收银说起 想象你是一个超市收银员,正在计算两位顾客的购物总和.每位顾客的商品都按照从个位到高位的顺序摆放(比如54元就是先放4元商品,再放50元商品). ...