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

搭建环境

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

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

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 )

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

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

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

关于汇编

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

int sum(int a, int b);//此函数通过汇编实现
int main(int argc, char *argv[])
{
    int num = sum(2, 3);
    return 0;
}

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

global sum
sum:
    add ecx, edx ;直接使用ecx和edx寄存器中的参数
    mov eax, ecx
    ret

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

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

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

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;
}

对应的汇编代码如下:

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

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

#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;
}

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

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

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

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

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

global sum
sum:
    vmovups ymm1, [rcx]
    vmovups ymm2, [rdx]
    vaddps ymm0, ymm1, ymm2
    vmovups [rcx], ymm0
    
    ret

因为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 的编译器。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索编译
, int
, 文件
, 函数
, 参数
代码
c语言指令集、c语言at指令集、51汇编指令集、arm汇编指令集、stm32汇编指令集,以便于您获取更多的相关知识。

时间: 2024-10-27 15:18:12

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

c语言调用汇编的方法_C 语言

c部分很简单,文件名随便,如main.c: 复制代码 代码如下: #include <stdio.h>#include <stdlib.h> void decToBin(long dec,char *b); //声明外部汇编函数int main(){          long dec=254;          char *bin=(char*)malloc(sizeof(char)*64);          decToBin(dec,bin);          printf

PHP调用wsdl文件类型的接口代码分享_php实例

复制代码 代码如下: <?php // 本类由系统自动生成,仅供测试用途 class IndexAction extends Action {     public function index(){         //#分销商订单提交.修改.取消.查询接口          $wsdl1='http://127.0.0.1:8080/ejfxs/services/order?wsdl';          //#分销商可销售产品接口地址         $wsdl='http://127.0

c-C语言调用函数实现矩阵自乘

问题描述 C语言调用函数实现矩阵自乘 各位前辈,小弟新学C语言,怎么也调试不过,不知道哪里出了问题,请问我该怎么调试? 调用void SelfMul(int A1[][3],int A2[][3],int n)实现3阶矩阵的乘法 #include //void SelfMul(int A1[][maxSize],int A2[][maxSize],int n) void SelfMul(int A1[][3],int A2[][3],int n) { int s=0; for(int i=0;i

PhantomJS其他语言调用

PhantomJS其他语言调用 我本身是使用Java语言的,所以会考虑phantomjs如何与java交互.目前考虑方案有如下两种: 1. Java Process 抓取逻辑还是用Javascript,封装成JS脚本,用Java Process去调用和管理每个Phantomjs的进程,但是多开进程,估计对机器性能要求比较高.而且Java只能通过phantomjs的标准输出,来判断脚本执行情况. 2.Phantomjs Webdriver webdriver会开启一个http服务,其他语言可以通过

c语言-C语言调用声音,请大神指导

问题描述 C语言调用声音,请大神指导 比如说我输入:中华人民共和国,然后要求计算机用声音把它念出来,应该怎么做呢? 解决方案 google TTS C++ google Microsoft Speech SDK 解决方案二: Text To Speech,微软开发的文本到语音库. 解决方案三: 自己google下用法.

Go语言调用其它程序并获得程序输出的方法_Golang

本文实例讲述了Go语言调用其它程序并获得程序输出的方法.分享给大家供大家参考.具体实现方法如下: 复制代码 代码如下: package main import (     "exec" // "os/exec" in go1     "fmt" ) func main(){     cmd := exec.Command("ls", "-l")     buf, err := cmd.Output()   

java的虚拟机有底层都调的是c吗?java能调用汇编嘛?阿门!

问题描述 java的虚拟机有底层都调的是c吗?java能调用汇编嘛?阿门! RT.往大师围观.java的指针封装的c那一块的指针嘛?java比较c又一次.nnd 解决方案 java可以用jni调用原生的代码,你可以用C(嵌入汇编)或者汇编去写一个dll给java调用. 但是注意,java本身是平台无关的,但是原生代码就不是.所以你要是调用了原生代码,那么比如你的pc的软件,就不容易移植给手机了. 解决方案二: Java虚拟机jvm--java虚拟机底层结构详解深入java虚拟机(一)--java

c++-Visula Basic 6.0语言调用C++ struct结构体中的字符串问题

问题描述 Visula Basic 6.0语言调用C++ struct结构体中的字符串问题 VB6调用C++ DLL结构体struct中的字符串,内存预分配的写法是什么,内存回收怎么做?VB不能用指针的谢谢 解决方案 VB不支持指针,一般你可以定义成局部变量,这样函数退出会自动回收的. 解决方案二: c++公用体union.结构体struct内存分配问题

在asp中应用vb语言调用Graphics 类

问题描述 在asp中应用vb语言调用Graphics 类 小弟初学asp,在asp中应用vb语言调用Graphics 类,怎么定义具体实现的功能与Graphics g = this.CreateGraphics(); 功能与这句相同 解决方案 asp没有内置绘图软件,也无法直接调用vb.net的,找persits.jpeg这个组件 http://www.greenxf.com/soft/40232.html http://www.cnblogs.com/ly312/archive/2010/10