- <p>vint8mf8_t vcopy_v_i8mf8 (vint8mf8_t src);</p><p>vint8mf4_t vcopy_v_i8mf4 (vint8mf4_t src);</p><p>...</p>
其操作详情可以参考:https://github.com/riscv-non-isa/rvv-intrinsic-doc/
本质上就是将复杂的汇编代码操作换成C语言函数的形式,这种方式可读性更强。rvv1.0后面也会提供这一种方式进行。
第二种就是采用汇编进行操作,裸写汇编代码的难度很大,但是可以实现比较好的优化,这特别是在关键耗时的操作上进行汇编级别的优化,可以很大程度上提升程序运行的性能。
在平头哥开源出来的GCC工具链中,并没有rvv-intrinsic功能,所以只能通过汇编函数的方式进行操作。
平头哥开源出来了一个神经网络的库,里面有C906的RVV底层操作。
https://github.com/T-head-Semi/csi-nn2
实现了一些基本的操作,抽象了各种常用的网络层的接口,并且提供一系列已优化的二进制库。
其主要的使用类似于CMSIS,Tensorflow等等。有了这些抽象接口,在使用RVV进行特定优化的时候,难度也会有所降低。
下面主要从csi-nn2抽取出vertor add, vertor mul,memcpy,也就是加法计算,乘法计算和内存拷贝,三个方面,对其性能进行评估。
https://github.com/bigmagic123/d1-nezha-rtthread/blob/main/bsp/d1-nezha/applications/vector.c
浮点数加法
采用向量操作
- <p>float a[10] = {1.2,1.3,1.4,1.5,1.6,1.7,1.8,2.0,2.1,2.2};</p><p>float b[10] = {1.2,1.3,1.4,1.5,1.6,1.7,1.8,2.0,2.1,2.2};</p><p>float c[10];</p><p>element_mul_f32(a, b, c, 10);</p><p></p>
一次性处理10个元素,采用RVV向量的操作。
普通的加法
- <p>for(ii = 0; ii < 10; ii++)</p><p>{</p><p> d[ii] = a[ii] + b[ii];</p><p>}</p>
单个元素相加。
分别计算30000次。
最后的结果如下:
采用向量加法只需要7ms,而直接相加,则消耗了36ms,性能相差5倍左右。
浮点数相乘
与加法操作类似,也是一次性处理10个元素
- <p>float a[10] = {1.2,1.3,1.4,1.5,1.6,1.7,1.8,2.0,2.1,2.2};</p><p>float b[10] = {1.2,1.3,1.4,1.5,1.6,1.7,1.8,2.0,2.1,2.2};</p><p>float c[10];</p><p>element_mul_f32(a, b, c, 10);</p>
普通的乘法
- <p>for(ii = 0; ii < 10; ii++)</p><p>{</p><p> d[ii] = a[ii] * b[ii];</p><p>}</p>
两者性能对比
向量乘法也比普通的乘法性能强大一些,接近5倍的差别。
内存拷贝
内存拷贝的测试方法是测试rt_memcpy,memcpy,csi_c906_memcpy。分别代表rt-thread内置的内存拷贝函数,采用C语言进行实现,memcpy是newlib库函数的实现,里面会对riscv架构进行优化处理,csi_c906_memcpy则是采用向量操作,进行内存拷贝。
测试是先申请1MB的目标内存,1MB的源内存,往源内存中写随机数。
拷贝源内存中的数据到目标内存,拷贝100次,也就是100M的内存拷贝数据量。
结果如下:
显然,内存拷贝操作newlib中的memcpy性能是最佳的,而向量操作的memcpy反而其次,最差的是rt-thread的rt_memcpy。这里的原因是newlib的memcpy的是经过优化后的,而vector memcpy也可能是优化的不好导致性能与newlib的memcpy相当。rt-thread采用纯C语言实现,其通用性比较好,但是性能不佳。
RVV在RTOS如何使用的更好
这是一个关于更好的在RTOS上使用RVV的问题,由于RTOS是为了追求实时性,一般来说,开启了FPU和RVV后,其寄存器的数量会非常的多,每次入栈和出栈的操作,如果每次都将全部的寄存器压入和弹出,将会让切换任务的时间变长,影响了系统的实时性。对于这种情况,其实可以利用mstatus中的VS和FS的标志位进行判断。
在切换任务时,可以通过这些标识,选择是否压栈和出栈,这样保证了一部分性能实时性的情况下,也可以很好的处理FPU和RVV。