经过进一步的编码,我忽然发现无法使用coremark做效率改善验证了,原因是CoreMark的矩阵乘操作,存在大量的格式转换,而且输出不是定点小数或者与输入一致格式的数据,导致无法直接调用矢量运算指令,以 matrix_mul_const为例:
乘法运算就涉及到16bit转换为32bit的操作,而矢量运算的造数据那一步,就需要load固定bit数的数据,即16bit数据如果要转换成32bit数据矢量,那就得先把数据转换成32bit的,然后再去操作,这种操作很明显的增加了运算量,反而无法判断出提升幅度。
另外,由于输入输出数据不是按照定点小数的方式设计的,导致输出数据的格式要求和输入数据的格式要求不一致,无法使用矢量乘除操作去加速并输出。
即使MATDAT和MATRES定义成浮点行,在coremark里,也是需要经过格式转换才能使用,导致无法进行下一步操作。
而这么看的话,测试矢量运算的提升效果,不能直接从coremark上去入手了,而是需要改用cmsis-dsp来做验证了。
代码修改
开启CMSIS-DSP
这个修改需要在RASC中操作,具体操作:
不在调试用的电脑前,晚点给出具体图片,操作是在components选项中勾选DSP后生成工程
增加测试代码
经过观察,CMSIS-DSP的测试代码,所引用的底层计算接口都存在Helium加速和完全无加速的版本,因此完全可以把CMSIS-DSP测试代码再封装一遍,替换CoreMark入口,达到测试目的。具体修改如下:
移植测试代码
从CMSIS-DSP库中提取测试代码文件(.c和.h文件),在CMSIS-DSP代码中的路径为:Examples/ARM。
由于CMSIS-DSP所有例程入口都是main,因此需要把main改为其他函数名,以保证不与现有工程冲突。另外,例程中存在大量的同名变量,因此需要把这些变量尽可能声明成static类型,一方冲突,另外,几个data.c的文件需要更名成.h文件,由对应的代码#include进去。
最终改完后的目录结构如下:
至于细节,由于修改点比较多,这里就不一一展示了,有兴趣的话可以访问CPKCOR_RA8D1B_TEST: cpkcor_ra8d1测试helium工程c查看。
增加测试入口文件
增加测试文件dsp_test.h和dsp_test.c,用于运行实际测试和结果输出代码,具体内容如下:
dsp_test.c
#include "dsp_test.h" #include "arm_math.h" #include "system_tick.h" // 单项测试入口声明 extern int32_t arm_matrix_main(void); extern int32_t arm_bayes_main(void); extern int32_t class_marks_main(); extern int32_t arm_fft_bin_main(void); extern int32_t arm_fir_main(void); extern int32_t graphic_equalizer_main(void); extern int32_t arm_svm_main(void); extern int32_t arm_linear_interp_main(void); extern int32_t arm_variance_main(void); extern int32_t arm_sin_cos_main(void); extern int32_t arm_signal_converge_main(void); extern int32_t arm_dotproduct_main(void); extern int32_t arm_convolution_main(void); // 测试执行结构体定义 struct dsp_test_t { int32_t (*func)(void); char *name; unsigned long retry; }; // 具体测试项 struct dsp_test_t dsp_test[] = { {arm_bayes_main, "arm_bayes_main", 100000}, {arm_convolution_main, "arm_convolution_main", 10000}, {arm_dotproduct_main, "arm_dotproduct_main", 200000}, {arm_fft_bin_main, "arm_fft_bin_main", 2500}, {arm_fir_main, "arm_fir_main", 5000}, {arm_linear_interp_main, "arm_linear_interp_main", 200000}, {arm_matrix_main, "arm_matrix_main", 100000}, {arm_signal_converge_main, "arm_signal_converge_main", 200}, {arm_sin_cos_main, "arm_sin_cos_main", 50000}, {arm_svm_main, "arm_svm_main", 300000}, {arm_variance_main, "arm_variance_main", 300000}, {class_marks_main, "class_marks_main", 200000}, {graphic_equalizer_main, "graphic_equalizer_main", 2500}, }; // 测试执行函数 void dsp_test_func(void) { unsigned long i; unsigned int j; TIMING_FORMAT tick_start = 0, tick_end = 0; #if defined(ARM_MATH_MVEF) || defined(ARM_MATH_MVEI) printf("Helium version\n\r"); #else printf("Normal version\n\r"); #endif for (j = 0; j < sizeof(dsp_test) / sizeof(struct dsp_test_t); j++) { tick_start = systick_get(); for (i = 0; i < dsp_test[j].retry; i++) { dsp_test[j].func(); } tick_end = systick_get(); printf("%s use: %ums\n\r", dsp_test[j].name, tick_end - tick_start); } printf("test done\n\r"); while (1) { ; } }
dsp_text.h
#ifndef __DSP_TEST_H_ #define __DSP_TEST_H_ void dsp_test_func(void); #endif
去掉coremark并添加测试代码入口
//#include "coremark.h" #include "dsp_test.h" ...... void hal_entry(void) { ...... // coremark_main(); dsp_test_func(); } ......
测试结果
开关helium功能的选项在此位置,选择Not Used代表完全不开启Helium,选择Integer + Floating Point代表开启Helium定点部分和浮点部分。
开启helium
Helium version arm_bayes_main use: 1748ms arm_convolution_main use: 1176ms arm_dotproduct_main use: 1347ms arm_fft_bin_main use: 1332ms arm_fir_main use: 1271ms arm_linear_interp_main use: 1898ms arm_matrix_main use: 1701ms arm_signal_converge_main use: 1333ms arm_sin_cos_main use: 1186ms arm_svm_main use: 1468ms arm_variance_main use: 1536ms class_marks_main use: 1702ms graphic_equalizer_main use: 1254ms test done
关闭helium
Normal version arm_bayes_main use: 2032ms arm_convolution_main use: 1215ms arm_dotproduct_main use: 824ms arm_fft_bin_main use: 2015ms arm_fir_main use: 3194ms arm_linear_interp_main use: 1885ms arm_matrix_main use: 2703ms arm_signal_converge_main use: 1936ms arm_sin_cos_main use: 716ms arm_svm_main use: 1825ms arm_variance_main use: 2456ms class_marks_main use: 2116ms graphic_equalizer_main use: 1866ms test done
统计后的提升量
测试视频
测试过程视频:https://www.bilibili.com/video/BV1PBBkYaEYL/
结论
从测试数据上看,大部分运算都出现了正向的优化,优化程度不一,最明显的优化点应该是fir计算,因为fir算法从逻辑上来说,是最为契合矢量运算的要求的。
负优化的部分,arm_sin_cos_main和arm_dotproduct_main两个部分是让我感觉最不可思议的地方,两个函数在实现上,主要调用的都是arm_mult_f32 和 arm_add_f32。在使用helium优化前,一个循环做一次计算,采用矢量运算后,一次循环做了四次乘或加的运算,照理来说,应该是正向优化才对,但实际效果却是负优化,有些没想明白原因。
另外,由于测试函数是件计算和初始化部分都放在一起实现的,因此显示的优化程度并不代表实际的优化程度,实际的优化程度,正向和负向都会更大,因为数据初始化部分并未加入helium运算,开不开helium,理论上效果都是一样的,除非编译器在开启helium时,自动做了代码转矢量运算的操作。