教程集 www.jiaochengji.com
教程集 >  脚本编程  >  C语言  >  正文 C语言调用汇编及使用指令集对代码进行优化实例教程

C语言调用汇编及使用指令集对代码进行优化实例教程

发布时间:2018-09-08   编辑:jiaochengji.com
教程集为您提供C语言调用汇编及使用指令集对代码进行优化实例教程等资源,欢迎您收藏本站,我们将为您提供最新的C语言调用汇编及使用指令集对代码进行优化实例教程资源
本教程我们分享一个关于C语言调用汇编及使用指令集对代码进行优化的实例,学习C语言的朋友可以看一下

搭建环境

x264使用汇编优化的思想是将汇编代码编译到一个静态库里,供C代码调用,所以首先需要构建一个汇编函数得静态库。因为手动配置使用yasm来编译汇编文件,并生成一个lib相当麻烦,我选择的是使用cmake来构建。

在demo里有一个sum.asm的汇编文件,文件里是所有的汇编函数,通过yasm编译后生成sum.obj,然后通过sum.obj来创建一个sum.lib库供C代码使用。还有一个main。c的C文件,用来生成可执行文件main,CMakeLists.txt文件如下:

<pre class="brush:cpp;toolbar:false">cmake_minimum_required(VERSION 3.0.00) project (asm) find_program(YASM_EXECUTABLE      NAMES yasm yasm-1.2.0-win32 yasm-1.2.0-win64     HINTS $ENV{YASM_ROOT} ${YASM_ROOT}     PATH_SUFFIXES bin ) set(FLAGS -f win64 -DARCH_X86_64=1) add_custom_command(         OUTPUT sum.obj         COMMAND ${YASM_EXECUTABLE}  ARGS ${FLAGS} ../source/sum.asm -o sum.obj         DEPENDS sum.asm) #添加静态库sum add_library( sum  STATIC sum.obj sum.asm ) set_target_properties(sum PROPERTIES LINKER_LANGUAGE C) #添加使用静态库的可执行程序main add_executable( main main.c ) target_link_libraries(main sum )</pre>


其中find_program是在系统环境变量中寻找看是否有yasm汇编器,在此假设是有的。

需要注意的是在COMMAND ${YASM_EXECUTABLE} ARGS ${FLAGS} ../source/sum.asm -o sum.obj中指定汇编文件的路径得是相对与工程文件所在的相对路径,所以这里是../source/sum.asm

至此环境搭建完毕,使用cmake就能生成需要的汇编lib工程和调用汇编函数得可执行文件工程。在vs上如下图所示


关于汇编

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

<pre class="brush:cpp;toolbar:false">int sum(int a, int b);//此函数通过汇编实现 int main(int argc, char *argv[]) {     int num = sum(2, 3);     return 0; }</pre>


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

<pre class="brush:cpp;toolbar:false">global sum sum:     add ecx, edx ;直接使用ecx和edx寄存器中的参数     mov eax, ecx     ret</pre>


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

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

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

<pre class="brush:cpp;toolbar:false">int sum(int a, int b, int c, int d, int e); int main(int argc, char *argv[]) {     int num = sum(2, 3, 4, 5, 6);     return 0; }</pre>


对应的汇编代码如下:

<pre class="brush:cpp;toolbar:false">global sum sum:     add rcx, rdx     add rcx, r8     add rcx, r9     mov rdx, [rsp   40] ;从栈中取出第5个参数放入rdx寄存器     add rcx, rdx     mov rax, rcx     ret</pre>


此处需要注意的是:前四个参数是通过寄存器传递,从栈中取出第五个参数时,并不是从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函数是对两个数组进行求和,代码如下:

<pre class="brush:cpp;toolbar:false">#define N 8 int sum(float a[], float b[]); int sum_c(float a[], float b[]) {     for (int i = 0; i < N; i )     {         a[i]  = b[i];     }     return 0; } int main(int argc, char *argv[]) {     float a[N] = { 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0 };     float b[N] = { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0 };     //将数组b[N]中的数据加到数组a[n]中     sum_c(a, b);//不使用汇编优化     sum(a, b);//使用汇编优化     return 0; }</pre>


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

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

<pre class="brush:cpp;toolbar:false">global sum sum:     movups xmm0, [rcx]     movups xmm1, [rdx]     movups xmm2, [rcx   16]     movups xmm3, [rdx   16]     addps xmm0, xmm1     addps xmm2, xmm3     movups [rcx], xmm0     movups [rcx   16], xmm2          ret</pre>

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

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

<pre class="brush:cpp;toolbar:false">global sum sum:     vmovups ymm1, [rcx]     vmovups ymm2, [rdx]     vaddps ymm0, ymm1, ymm2     vmovups [rcx], ymm0          ret</pre>


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



正常编写的c语言程序编译器会自动进行针对特定指令集用汇编语言优化吗

我的意思是,假如我写一段实现某个算法的C语言代码,我的计算机支持SSE4指令集,那么在编译的时候,编译器会自动挖掘算法中的并行部分,用SSE4的并行指令挖掘可以并行执行的部分生成汇编代码和可执行问件吗?

编译器用gcc或者vc

提问者采纳

这个要看你使用什么编译器了。查看编译器的帮助文档,它会告诉你它支持那些指令集,并且做哪些可能的优化。

不同的编译器,是不一样的。

补充:GCC 不太清楚,你连VC 的版本都不说。汗,VC6是不支持SSE的,需要安装VC6SP5。
VS2005 和 VS2008 都支持 SSE。对 SSE/MMX 指令集优化得最好的,还是 Intel 的 c 编译器。

对并行和高性能计算,Fortran 的优势比较大。特别是 Fortran2003 的新特征,为并行计算做了很多专门的设定。Intel 也有 Fortran 的编译器。

您可能感兴趣的文章:
C语言调用汇编及使用指令集对代码进行优化实例教程
python是汇编语言吗
python和c语言的区别是什么
理解 Golang 中函数调用的原理
golang 没有名字参数_golang内核系列--深入理解plan9汇编&amp;实践
【后端教程】走进Golang之编译器原理
计算机基础:3、计算机层次与编程语言
小米技术出品——走进Golang之编译器原理
python需要编译么
python语言属于汇编语言吗?

[关闭]
~ ~