zoukankan      html  css  js  c++  java
  • C代码调用汇编&使用指令集优化

      最近研究x264汇编代码,感觉使用到的优化思想和手法非常不错,在此写一个demon来记录我学习过程

    • 从搭建环境开始

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

      在demon里有一个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函数里需要对两个数字求和,代码如下:

    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数据分别的求和操作。

  • 相关阅读:
    中国历史朝代公元对照简表
    [Solved] DashBoard – Excel Service: The data sources may be unreachable, may not be responding, or may have denied you access.
    Delete/Remove Project from TFS 2010
    Sharepoint site showing system account instead of my username on the top right corner.
    你的成功在于你每天养成的习惯
    Internet Information Services is running in 32bit emulation mode. Correct the issue listed above and rerun setup.
    Prepare to back up and restore a farm (Office SharePoint Server 2007)
    Word中字号与磅值的对应关系
    How to: Change the Frequency for Refreshing the Data Warehouse for Team System
    UI Automation in WPF/Silverlight
  • 原文地址:https://www.cnblogs.com/JeroZeng/p/4782438.html
Copyright © 2011-2022 走看看