D1 riscv芯片上運行rt-thread進行RVV性能評估
掃描二維碼
隨時隨地手機看文章
D1 riscv芯片上運行rt-thread進行RVV性能評估
-
概述
-
rt-thread在D1芯片上的移植
-
如何開啟D1&&D1s的rvv擴展
-
RVV性能對比評估
-
RVV在RTOS如何使用的更好
概述
D1 && D1s(f133)采用的是平頭哥C906的core,上面已經(jīng)支持了RVV 0.7.1版本,雖然目前RVV1.0已經(jīng)frozen,這就意味著上游編譯器或者一些相關(guān)的生態(tài)軟件將支持RVV1.0,但是作為性能評估RVV0.7.1與RVV1.0影響并不大。下面的文章主要描述如何在D1 && D1s芯片上運行rt-thread,并且描述如何開啟RVV,同時對RVV性能進行一個簡單的評估,最后討論RVV如何與RTOS使用的問題。
rt-thread在D1芯片上的移植
目前D1s有64MB的內(nèi)置DDR2,這非常適合運行RTOS,所以將rtos移植到D1s上是非常不錯的選擇。
其移植的細節(jié)不詳細介紹,可以查看源代碼
https://github.com/bigmagic123/d1-nezha-rtthread
移植的難點在于:
1.啟動,從芯片上電啟動入口處跳轉(zhuǎn)到rt-thread入口處的匯編代碼
2.硬件串口的初始化,這里會涉及到時鐘配置
3.tick時鐘中斷,目前采用的是riscv的通用timer,需要配置clic
4.任務(wù)切換和上下文的保存與恢復,這部分需要對入棧出棧的順序十分的清晰
5.串口中斷,這里需要弄清楚plic中斷處理機制
解決上述問題,移植rt-thread將非常的簡單,當前的rt-thread運行在M-mode,具有比較高的權(quán)限,可以隨意操作寄存器進行配置。
編譯與運行在windows和Linux上均可操作,主要參考如下的文檔:
https://github.com/bigmagic123/d1-nezha-rtthread/blob/main/README.md
目前已經(jīng)實現(xiàn)的功能有:
1.timer 2.uart 3.gpio 4.vector 5.FPU
如何開啟D1&&D1s的rvv擴展
想要使用RVV功能,需要開啟VS標志位,該位位于
VS位于MSTATUS寄存器的23到24位。但是需要注意的是,當使用RVV時,需要開啟浮點寄存器(FS),不然會報錯。
這部分在rt-thread的體現(xiàn)是在上下文切換的時候,需要將使用RVV的線程的MSTATUS設(shè)置成開啟VS的模式。
可以VS設(shè)置b01。
只有當使能該位,才能正常使用RVV指令,不然運行會直接報錯。
接著,編譯選項中還需要添加成如下選項
-march=rv64gcvxtheadc -mabi=lp64d -mtune=c906
這樣才能告訴編譯器支持RVV指令。
RVV性能對比評估
riscv 的RVV其編程模型主要有兩種方式,第一種采用rvv-intrinsic。這就是在編譯器中進行intrinsic函數(shù)的構(gòu)建,可以將相關(guān)的rvv操作變成編輯器的內(nèi)置函數(shù)。采用類似下面的函數(shù)方式進行編程。
vint8mf8_t vcopy_v_i8mf8 (vint8mf8_t src); vint8mf4_t vcopy_v_i8mf4 (vint8mf4_t src); ...
其操作詳情可以參考:
https://github.com/riscv-non-isa/rvv-intrinsic-doc/
本質(zhì)上就是將復雜的匯編代碼操作換成C語言函數(shù)的形式,這種方式可讀性更強。rvv1.0后面也會提供這一種方式進行。
第二種就是采用匯編進行操作,裸寫匯編代碼的難度很大,但是可以實現(xiàn)比較好的優(yōu)化,這特別是在關(guān)鍵耗時的操作上進行匯編級別的優(yōu)化,可以很大程度上提升程序運行的性能。
在平頭哥開源出來的GCC工具鏈中,并沒有rvv-intrinsic功能,所以只能通過匯編函數(shù)的方式進行操作。
平頭哥開源出來了一個神經(jīng)網(wǎng)絡(luò)的庫,里面有C906的RVV底層操作。
https://github.com/T-head-Semi/csi-nn2
實現(xiàn)了一些基本的操作,抽象了各種常用的網(wǎng)絡(luò)層的接口,并且提供一系列已優(yōu)化的二進制庫。
其主要的使用類似于CMSIS,Tensorflow等等。有了這些抽象接口,在使用RVV進行特定優(yōu)化的時候,難度也會有所降低。
下面主要從csi-nn2抽取出vertor add,vertor mul,memcpy,也就是加法計算,乘法計算和內(nèi)存拷貝,三個方面,對其性能進行評估。
https://github.com/bigmagic123/d1-nezha-rtthread/blob/main/bsp/d1-nezha/applications/vector.c
浮點數(shù)加法
采用向量操作
float a[10] = {1.2,1.3,1.4,1.5,1.6,1.7,1.8,2.0,2.1,2.2}; float b[10] = {1.2,1.3,1.4,1.5,1.6,1.7,1.8,2.0,2.1,2.2}; float c[10]; element_mul_f32(a, b, c, 10);
一次性處理10個元素,采用RVV向量的操作。
普通的加法
for(ii = 0; ii < 10; ii++)
{
d[ii] = a[ii] + b[ii];
}
單個元素相加。
分別計算30000次。
最后的結(jié)果如下:
采用向量加法只需要7ms,而直接相加,則消耗了36ms,性能相差5倍左右。
浮點數(shù)相乘
與加法操作類似,也是一次性處理10個元素
float a[10] = {1.2,1.3,1.4,1.5,1.6,1.7,1.8,2.0,2.1,2.2}; float b[10] = {1.2,1.3,1.4,1.5,1.6,1.7,1.8,2.0,2.1,2.2}; float c[10]; element_mul_f32(a, b, c, 10);
普通的乘法
for(ii = 0; ii < 10; ii++)
{
d[ii] = a[ii] * b[ii];
}
兩者性能對比
向量乘法也比普通的乘法性能強大一些,接近5倍的差別。
內(nèi)存拷貝
內(nèi)存拷貝的測試方法是測試rt_memcpy,memcpy,csi_c906_memcpy。分別代表rt-thread內(nèi)置的內(nèi)存拷貝函數(shù),采用C語言進行實現(xiàn),memcpy是newlib庫函數(shù)的實現(xiàn),里面會對riscv架構(gòu)進行優(yōu)化處理,csi_c906_memcpy則是采用向量操作,進行內(nèi)存拷貝。
測試是先申請1MB的目標內(nèi)存,1MB的源內(nèi)存,往源內(nèi)存中寫隨機數(shù)。
拷貝源內(nèi)存中的數(shù)據(jù)到目標內(nèi)存,拷貝100次,也就是100M的內(nèi)存拷貝數(shù)據(jù)量。
結(jié)果如下:
顯然,內(nèi)存拷貝操作newlib中的memcpy性能是最佳的,而向量操作的memcpy反而其次,最差的是rt-thread的rt_memcpy。這里的原因是newlib的memcpy的是經(jīng)過優(yōu)化后的,而vector memcpy也可能是優(yōu)化的不好導致性能與newlib的memcpy相當。rt-thread采用純C語言實現(xiàn),其通用性比較好,但是性能不佳。
RVV在RTOS如何使用的更好
這是一個關(guān)于更好的在RTOS上使用RVV的問題,由于RTOS是為了追求實時性,一般來說,開啟了FPU和RVV后,其寄存器的數(shù)量會非常的多,每次入棧和出棧的操作,如果每次都將全部的寄存器壓入和彈出,將會讓切換任務(wù)的時間變長,影響了系統(tǒng)的實時性。對于這種情況,其實可以利用mstatus中的VS和FS的標志位進行判斷。
在切換任務(wù)時,可以通過這些標識,選擇是否壓棧和出棧,這樣保證了一部分性能實時性的情況下,也可以很好的處理FPU和RVV。