您可能听说或者使用过SIMD指令,例如x86的MMX,SSE,AVX-2和AVX-512,ARM的NEON、SVE这些。而RISC-V为什么采用Vector(V向量)扩展呢?David Patterson 和Andrew Waterman曾发表过一篇《SIMD instruction considered harmful》(中文标题:SIMD指令被认为是有害的)的文章,感兴趣的可以去阅读以下。但,无论是SIMD还是Vector的加速指令集,都是现代处理器极其重要的特性,对提升处理器性能起着非常重要的作用。
-
使用GCC编译器进行benchmark,得到一些不准确的处理器性能指标。原因可能是GCC版本并未对RISC-V指令集进行起码的优化,甚至于不支持自动向量化。这有时相当于,一个高手绑住了他的左手进行决斗。 RISC-V还没有类似Intel针对自家处理器深度优化的各种基础库,RISC-V所使用的绝大多数开源库都还没有社区进行优化,这制约了RISC-V处理器性能的发挥。
-
绝大多数软件工程师,欠缺对于SIMD和Vector加速指令集的理解,忽视了加速指令集的重要性。
测试环境
【硬件参数】
CPU: SOPHGO SG2042
RISC-V Core:C920,Vector 0.7.1
L1 Cache: I:64KB and D:64KB
L2 Cache: 1MB/Cluster
L3 Cache: 64MB System Cache
DRAM: DDR4 16Gx4
【软件环境】
linux版本: 22.10
gcc版本: 10.2.0
-
T-HEAD C920 的矢量扩展支持以下特性:
1.C920 兼容 RISC-V Vector Spec 0.7.1。支持所有标准矢量指令,支持 Zvamo 指令和 Zvlsseg 指令,不支持 Zvediv 扩展。
2.32 个独立的矢量寄存器 v0-v31。矢量寄存器的位宽为 128 位(即 VLEN=128)。
3.对于矢量浮点指令,支持的元素类型为 FP16、FP32、FP64(即 SEW=16/32/64)。
4.对于矢量整型指令,支持的元素类型为 INT8、INT16、INT32 和 INT64(即 SEW=8/16/32/64)。
5.支持矢量寄存器的分组,以提高矢量运算的效率。支持 4 种分组方式:每组包含 1/2/4/8 个矢量寄存器,分成32/16/8/4个组。
使用Vector加速memcpy
简介
内存拷贝操作是一个非常常见且基本的任务。本实验将使用vector对memcpy进行加速和评估性能提升。
函数原型
void *memcpy(void *destin, void *source, unsigned n);
destin-- 指向用于存储复制内容的目标数组,类型强制转换为 void* 指针。source-- 指向要复制的数据源,类型强制转换为 void* 指针。n-- 要被复制的字节数。
实验过程
1.为了便于比较,我用C语言编写了memcpy的函数,考虑到64位处理器,每次拷贝了一个8个bytes。
C代码:
void *memcpy_8byte(void *destin, void *source, size_t n){long *dst = destin;const long *src = source;for (; n > 7; n -= 8){*dst++ = *src++;}if (n > 0){char *char_dst = (char *)dst;const char *char_src = (const char *)src;for (size_t i = 0; i < n; i++){char_dst[i] = char_src[i];}}return destin;}
gcc -S memcpy_8byte.c -O3 -march=rv64gcv0p7_zfh_xtheadc -mabi=lp64d -mtune=c920
memcpy_8byte:li a5,7bleu a2,a5,.L5addi a7,a2,-8andi a7,a7,-8addi a7,a7,8add a6,a0,a7mv a4,a1mv a5,a0:ld a3,0(a4)addi a5,a5,8addi a4,a4,8sd a3,-8(a5)bne a5,a6,.L3andi a2,a2,7add a1,a1,a7:beq a2,zero,.L4lbu a4,0(a1)li a5,1sb a4,0(a6)beq a2,a5,.L4lbu a4,1(a1)li a5,2sb a4,1(a6)beq a2,a5,.L4lbu a4,2(a1)li a5,3sb a4,2(a6)beq a2,a5,.L4lbu a4,3(a1)li a5,4sb a4,3(a6)beq a2,a5,.L4lbu a4,4(a1)li a5,5sb a4,4(a6)beq a2,a5,.L4lbu a4,5(a1)li a5,7sb a4,5(a6)bne a2,a5,.L4lbu a5,6(a1)sb a5,6(a6):ret:mv a6,a0j .L2memcpy_8byte, .-memcpy_8byte"GCC: (GNU) 10.2.0".note.GNU-stack,"",@progbits
写了一个简单的测试代码如下:
int main(){char src[50]={"Hello,World"};char dst[50];memcpy_8byte(dst, src, strlen(src)+1);printf("dest=%s\n", dst);return 0;}
测试结果如下:
2.使用intrinsic实现memcpy函数的加速,注意该CPU的Vector位宽位128bits。
C代码:
void *memcpy_rvv(void *destin, void *source, unsigned n){unsigned char *dst = destin;const unsigned char *src = source;for (size_t vl; n > 0; n -= vl, src += vl, dst += vl){vl = vsetvl_e8m4(n);vuint8m4_t vec_src = vle8_v_u8m4(src, vl);vse8_v_u8m4(dst, vec_src, vl);}return destin;}
gcc -S rvv_memcpy.c -O3 -march=rv64gcv0p7_zfh_xtheadc -mabi=lp64d -mtune=c920
memcpy_rvv:beq a2,zero,.L2mv a4,a0.align 3.L3:extu a5,a2,31,0vsetvli a5,a5,e8,m4vle.v v4,0(a1)subw a2,a2,a5add a1,a1,a5vse.v v4,0(a4)add a4,a4,a5bne a2,zero,.L3.L2:ret.size memcpy_rvv, .-memcpy_rvv.ident "GCC: (GNU) 10.2.0".section .note.GNU-stack,"",@progbits
写了一个简单的测试代码如下:
int main(){char src[50]={"Hello,World"};char dst[50];memcpy_rvv(dst, src, strlen(src)+1);printf("dest=%s\n", dst);return 0;}
测试结果如下:
性能测试
测试代码实现如下,测试数据大小1~64K Bytes,循环十次,取最大值。
void benchmark_memcpy_task(void *(*memcpy_function)(void *, void *, size_t), size_t buf_size, size_t loop_times){unsigned char *source_buffer = malloc(buf_size);unsigned char *destination_buffer = malloc(buf_size);initialize_buffer(source_buffer, buf_size);double max_speed = 0.0;for (int i = 0; i < loop_times; ++i){struct timespec start_clock, end_clock;clock_gettime(CLOCK_MONOTONIC, &start_clock);memcpy_function(destination_buffer, source_buffer, buf_size);clock_gettime(CLOCK_MONOTONIC, &end_clock);long long elapsed_time = (end_clock.tv_sec - start_clock.tv_sec) * 1000000000 + (end_clock.tv_nsec - start_clock.tv_nsec);double speed = (double)buf_size / (1024 * 1024) / (elapsed_time / 1000000000.0);if (speed > max_speed){max_speed = speed;}}double buf_size_kb = (double)buf_size / 1024;printf("%8.0f %8.0f \n", buf_size_kb, max_speed);// 使用memcmp比较两个内存块是否相等int result = memcmp(source_buffer, destination_buffer, buf_size);if (result != 0){printf("Memory comparison result: Not equal.\n");}free(source_buffer);free(destination_buffer);}
性能对比曲线如下:
参考文档:
https://github.com/riscv/riscv-v-spec/tree/master/example
https://github.com/riscv-non-isa/rvv-intrinsic-doc/tree/main/examples
--正文结束--
关于RISC-V公共测试平台

RISC-V高性能处理器公共测试平台。
-
快速使用指南 下载链接: https://www.kdocs.cn/l/cmnYcyFIlVRx
加入我们的RISC-V社区
欢迎关注我们,参与进来共建RISC-V软件生态。加入我们的讨论群后,可以向管理员申请免费的64核RISC-V服务器SUDO权限试用账号。
发邮件到riscvinfo@perfxlab.com
加入微信讨论群:加iYuta-R2为好友后可拉入群。
加入QQ讨论群:906962594(RVBoards·Only RISC-V)
扫描二维码加群👇

RISC-V公共测试平台系列文章
-
RISC-V公测平台发布 · 第一个WEB Server“Hello RISC-V world!” -
RISC-V公测平台发布 ·如何在SG2042上玩转k3s -
“RISC-V成长日记” blog发布,第一个运行在RISC-V服务器上的blog? -
RISC-V公测平台发布:如何在SG2042上玩转OpenMPI -
RISC-V公测平台发布:Compiling The Fedora Linux Kernel Natively on RISC-V -
RISC-V公测平台发布 · Unix Bench完整测试 -
RISC-V公测平台发布 · 使用YCSB测试SG2042上的MySQL性能 -
RISC-V公测平台发布 · 7-zip 测试 -
RISC-V公测平台发布 · CoreMark测试报告 -
RISC-V公测平台发布 · 数据库在RISC-V服务器上的适配评估 -
RISC-V公测平台发布 · FFTW的移植和性能对比 -
RISC-V公测平台发布 · 在SG2042上配置Jupiter+Octave科学计算环境 -
RISC-V公测平台发布:在SG2042上玩转Caddy
-
RISC-V公测平台发布:在SG2042上玩转docker -
RISC-V Vector加速实验-memcpy (本篇)

