欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

浮点指令

程序员文章站 2022-07-02 23:23:21
...

第一代:x87浮点指令集

特征

  • 使用80位浮点协处理器处理浮点运算
  • 浮点协处理器内部为栈结构
    浮点指令
    运算过程
    浮点指令

指令

  • 这里只介绍部分指令,详细的参考Intel / AMD 开发手册
;入栈
fld st(i)               ;st(i)的值压入栈顶
fld mem32/mem64/mem80   ;将浮点数压栈 交给st(0) 同时原st(0)交给st(1)

;加法
fadd st(0),st(i)        ;st(0)st(i)作加法 结果放在st(0)
fadd st(i),st(0)        ;st(i)st(0)作加法 结果放在st(0)

fadd mem32/mem64        ;st(0)3264位地址内存上的值相加,结果放在st(0)

faddp                   ;st(1)st(0)相加,结果放在st(1),并弹出栈顶值
faddp st(i),st(0)       ;st(i)st(0)相加,结果放在st(i),并弹出栈顶值

fiadd mem32/mem64       ;st(0)16位或32位的整型值相加,结果放在st(0)

;出栈
fst st(i)               ;st(0)值拷贝到st(i)
fst mem32/mem64         ;st(0)值拷贝到指定内存地址处,不改变栈顶

fstp st(i)              ;st(0)值拷贝到st(i),同时弹出栈顶
fstp mem32/mem64        ;st(0)值拷贝到指定内存地址处,同时弹出栈顶
  • 代码示例
int main()
{
    double f1 = 1.0f;
    double f2 = 2.0f;
    float ret;

    __asm
    {
        fld f1;
        fld f2;
        faddp st(1), st(0);          //加完值放在栈顶
        fstp ret;                    //将栈顶值弹出到指定内存中
    }

    return 0;
}

浮点指令

第二代:多媒体指令集(MMX)

特征

1.使用64位浮点协处理器处理浮点运算
2.寄存器可以拆分后做同时运算,极大提升了运算效率

  • 可以拆分为8个单字节同时作运算
  • 可以拆分为2个float同时作运算

3.不再使用栈结构,对比x87性能得到极大提升(差不多在10倍左右)
4.寄存器在拆分做独立运算时有两种模式

  • 环绕模式: 255+1 = 0 (溢出)
  • 饱和模式: 255+1 = 255 (不溢出)

指令

  • 汇编语法,这里只介绍部分语法,详细的参考Intel / AMD 开发手册
int main()
{
    char ary1[] = { 255, 2, 3, 4, 5, 6, 7, 8 };
    char ary2[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
    char ary3[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
    __asm
    {
        movq  MM0, ary1         //相当于mov指令,64位(8字节)传送
        movq  MM1, ary2
        paddb MM0, MM1          //环绕模式加法,允许溢出
        paddusb MM0, MM1        //饱和模式加法,不允许溢出
        movq  ary3, MM0
    }
    
    return 0
  • 高级语法
#include <iostream>
#include <time.h>
#include <mmintrin.h>      //MMX指令集对应的头文件

void x87_add(char *ary, int size)
{
  for (int i = 0; i < size; i++)
    ary[i] += 1;
}

void mmx_add(char *ary, int size)
{
  __m64 sum;//64位联合体
  sum.m64_i64 = 0x0101010101010101L;

  __m64 *p = (__m64 *)ary;
  for (int i = 0; i < size; i++)
    _m_paddb(p[i], sum);                 //打包加法,相当于每次给 8个字节的char同时加 1
}


int main()
{
  //x87
  int size = 100000000;
  char* ary = (char*)malloc(sizeof(char) * size);
  for (int i = 0; i < size; i++)
    ary[i] = 1;

  time_t start = clock();
  x87_add(ary, size);
  printf("x87_add time:%d\n", clock() - start);

  start = clock();
  mmx_add(ary, size / 8);
  printf("mmx_add time:%d\n", clock() - start);

  free(ary);
  
  return 0;
  
}

第三代:多媒体指令集(SSE)

特征

1.采用128位位浮点协处理器处理浮点运算
2.寄存器可以拆分后做同时运算,极大提升了运算效率,广泛应用于3D游戏开发

  • 可以拆分为16个单字节同时作运算
  • 可以拆分为4个float同时作运算
  • SSE2.0以后支持拆分为2个double同时作运算

指令

  • 汇编语法(这里只介绍部分语法,详细的参考Intel / AMD 开发手册)
int main()
{
    char ary1[] = { 255, 2, 3, 4, 5, 6, 7, 8, 255, 2, 3, 4, 5, 6, 7, 8 };
    char ary2[] = { 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8 };
    char ary3[] = { 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8 };
    __asm
    {
        movdqu  xmm0, ary1                 //相当于mov指令,这里mov的是128位(16字节),movdqu(移动非对齐的双四字),movdqa(移动对齐的双四字)
        movdqu  xmm1, ary2
        paddq xmm0, xmm1                    //环绕模式加法,允许溢出
        //paddusb xmm0, xmm1                //饱和模式加法,不允许溢出
        movdqu  ary3, xmm0
    }

    return 0;
}
  • 高级语法
#include <iostream>
#include <time.h>
#include <xmmintrin.h> //SSE  float+float  int+int
#include <emmintrin.h> //SSE2
#include <intrin.h>    //all 


float x87_add(float *ary, int size)
{
  float sum = 0.0f;
  for (int i = 0; i < size; i++)
    sum += ary[i];

  return sum;
}


float sse_add(float *ary, int size)
{
  __m128 sum = { 0.0f, 0.0f, 0.0f, 0.0f };//128位联合体
  __m128 *p = (__m128*)ary;
  for (int i = 0; i < size; i++)
    sum = _mm_add_ps(sum, p[i]); //打包加法,相当于每次给4个浮点作运算

  return sum.m128_f32[0] + sum.m128_f32[1] + sum.m128_f32[2] + sum.m128_f32[3];
}


int main()
{
    //SSE: MOVSS ADDSS ADDSD  ADDPS  ADDPD

    int size = 200000000;
    float sum;
    float* ary = (float*)malloc(sizeof(float) * size);
    for (int i = 0; i < size; i++)
      ary[i] = 1.0f;

    time_t start = clock();
    sum = x87_add(ary, size);
    printf("x87_add sum:%f time:%d\n", sum, clock() - start);

    start = clock();
    sum = sse_add(ary, size / 4);
    printf("sse_add sum:%f time:%d\n", sum, clock() - start);
    

    free(ary);

    return 0;
}

第四代:AVS浮点指令集

特征

1.采用256位位浮点协处理器处理浮点运算
2.寄存器可以拆分后做同时运算,极大提升了运算效率,广泛应用于3D游戏开发

  • 可以拆分为32个单字节同时作运算
  • 可以拆分为8个float同时作运算
  • SSE2.0以后支持拆分为4个double同时作运算

3.对比SSE指令集,做8个float同时运算时效率提升2倍(但实际提升不大,因为3D游戏坐标最多只需3个float同时作运算即可)
指令

  • 汇编语法(这里只介绍部分语法,详细的参考Intel / AMD 开发手册) ,相对于SSE的语法,前缀加V即可
int main()
{
    char ary1[] = { 255, 2, 3, 4, 5, 6, 7, 8, 255, 2, 3, 4, 5, 6, 7, 8 };
    char ary2[] = { 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8 };
    char ary3[] = { 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8 };
    __asm
    {
        vmovdqu  xmm0, ary1                   //相当于mov指令,这里mov的是128字节
        //vmovdqu  ymm0, ary1                 //相当于mov指令,这里寄存器是ymm0,对应mov的是256字节
        vmovdqu  xmm1, ary2
        vpaddq xmm0, xmm1                    //环绕模式加法,允许溢出
        //vpaddusb xmm0, xmm1                //饱和模式加法,不允许溢出
        vmovdqu  ary3, xmm0
    }

    return 0;
}
  • 高级语法
#include <iostream>
#include <time.h>
#include <intrin.h>     //all 
#include <immintrin.h>  //avx

//版本迭代:SSE SSE1  SSE2  SSE3 SSE4 SSE4.1 SSE4.2 SSE5.0(AVX)  AVX2

float x87_add(float *ary, int size)
{
  float sum = 0.0f;
  for (int i = 0; i < size; i++)
    sum += ary[i];

  return sum;
}


float avx_add(float *ary, int size)
{
  __m256 sum = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
  __m256 *p = (__m256*)ary;
  for (int i = 0; i < size; i++)
    sum = _mm256_add_ps(sum, p[i]);             //一次可同时对8个float作加法 性能极大提升

  return sum.m256_f32[0];
}


int main()
{
  	//AVX: VMOVSS VADDSS .....

    int size = 200000000;
    float sum;
    float* ary = (float*)malloc(sizeof(float) * size);
    for (int i = 0; i < size; i++)
      ary[i] = 1.0f;

    time_t start = clock();
    sum = x87_add(ary, size);
    printf("x87_add sum:%f time:%d\n", sum, clock() - start);

    start = clock();
    sum = avx_add(ary, size / 8);
    printf("avx_add sum:%f time:%d\n", sum, clock() - start);

    free(ary);

    return 0;
}

vs中设置默认浮点指令集位置

浮点指令

相关标签: c/c++逆向