C代码调用汇编&使用指令集优化

先写一个最简单的例子(在此针对的是64bit汇编),假设main函数里需要对两个数字求和,代码如下:

复制代码
1 int sum(int a, int b);//此函数通过汇编实现
2 
3 int main(int argc, char *argv[])
4 {
5     int num = sum(2, 3);
6     return 0;
7 }
复制代码

  那麽对应的汇编实现sum函数的代码如下:

复制代码
1 global sum
2 
3 sum:
4 
5     add ecx, edx ;直接使用ecx和edx寄存器中的参数
6     mov eax, ecx
7 
8     ret
复制代码

   这是一个最简单的C调用汇编函数得demon,在写汇编函数的时候碰到了以下问题:

  1. 之前学习的都是32位汇编的时候,函数参数的传递都是通过栈来完成的,在64位汇编中,前四个参数是通过寄存器rcx、rdx、r8、r9来传递的,只有参数个数大于4个后才通过栈来传递,所在在以上汇编代码中直接使用了寄存器ecx和edx中的值

  当函数参数个数大于4时,假设C代码如下:

复制代码
1 int sum(int a, int b, int c, int d, int e);
2 
3 int main(int argc, char *argv[])
4 {
5     int num = sum(2, 3, 4, 5, 6);
6     return 0;
7 }
复制代码

  对应的汇编代码如下:

复制代码
 1 global sum
 2 
 3 sum:
 4 
 5     add rcx, rdx
 6     add rcx, r8
 7     add rcx, r9
 8 
 9     mov rdx, [rsp + 40] ;从栈中取出第5个参数放入rdx寄存器
10     add rcx, rdx
11 
12     mov rax, rcx
13 
14     ret
复制代码

  此处需要注意的是:前四个参数是通过寄存器传递,从栈中取出第五个参数时,并不是从rsp+8的地方取,而是从rsp+40(40 = 4*8 + 8)的地方取,说明虽然前四个参数是通过寄存器传递,但是在栈中还是占用了相应的空间,我对此的理解是为了__stdcall和__cdecll的兼容吧。

  • 使用指令集优化(SSE AVX等)

  首先来看一下SIMD寄存器

 

 

  SSE使用到的SIMD寄存器是128bit,一共有16个,从XMM0到XMM15

  AVX拓展出来的SIMD寄存器是256bit,一共也是16个,从YMM0到YMM16,当然AVX也能使用SSE的XMM寄存器

  AVX2.0的时候将寄存器拓展到了512bit,一共有32个,从ZMM0到ZMM31

  假设我们的main函数是对两个数组进行求和,代码如下:

复制代码
 1 #define N 8
 2 
 3 int sum(float a[], float b[]);
 4 
 5 int sum_c(float a[], float b[])
 6 {
 7     for (int i = 0; i < N; i++)
 8     {
 9         a[i] += b[i];
10     }
11     return 0;
12 }
13 
14 int main(int argc, char *argv[])
15 {
16     float a[N] = { 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0 };
17     float b[N] = { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0 };
18     //将数组b[N]中的数据加到数组a[n]中
19     sum_c(a, b);//不使用汇编优化
20     sum(a, b);//使用汇编优化
21     return 0;
22 }
复制代码

  可以看到,不使用汇编优化的话,在sum_c函数中,我们需要依次计算出a[i] + b[i]的和并保存在a[i]中。

  如果使用SSE指令集优化的话,代码如下:

复制代码
 1 global sum
 2 
 3 sum:
 4 
 5     movups xmm0, [rcx]
 6     movups xmm1, [rdx]
 7     movups xmm2, [rcx + 16]
 8     movups xmm3, [rdx + 16]
 9 
10     addps xmm0, xmm1
11     addps xmm2, xmm3
12 
13     movups [rcx], xmm0
14     movups [rcx + 16], xmm2
15     
16     ret
复制代码

  可以看到,只需要进行两次加法运算就能计算出a[8]和b[8]中8个数字相加的和,这里需要进行两次计算是因为xmm寄存器是128bit,所以每次只能计算4个float数据,8个数据得分两次计算。

  使用AVX指令集优化代码如下:

复制代码
 1 global sum
 2 
 3 sum:
 4 
 5     vmovups ymm1, [rcx]
 6     vmovups ymm2, [rdx]
 7 
 8     vaddps ymm0, ymm1, ymm2
 9     vmovups [rcx], ymm0
10     
11     ret
复制代码

  因为AVX使用到了256bit的ymm寄存器,所以一次可以处理8个32bit的float数据,一次计算就能完成两组8个float数据分别的求和操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ww506772362

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值