将 C++ 代码转换为 x87 内联汇编代码

问题描述

我正在尝试将 C++ 代码转换为 x87 样式的内联汇编代码。

C++ 代码:

  double a = 0.0,b = 0.0,norm2 = 0.0;
  int n;
  for (n = 0; norm2 < 4.0 && n < N; ++n) {
    double c = a*a - b*b + x;
    b = 2.0*a*b + y;
    a = c;
    norm2 = a*a + b*b;
  }

内联汇编代码:

  double a = 0.0,norm2 = 0.0;
  int n;
  for (n = 0; norm2 < 4.0 && n < N; ++n) { 
    // double c = a * a - b * b + x;
    __asm fld a 
    __asm fmul st(0),st(0) 
    __asm fld b 
    __asm fmul st(0),st(0) 
    __asm fsubp st(1),st(0) 
    __asm fld x 
    __asm faddp st(1),st(0) 
    __asm fstp c 

    // b = 2.0 * a * b + y;
    __asm fld two 
    __asm fld b 
    __asm fld a 
    __asm fmulp st(2),st(0) 
    __asm fmulp st(1),st(0) 
    __asm fld y
    __asm faddp st(1),st(0) 
    __asm fstp b

    // a = c
    __asm fld c
    __asm fstp a
    
    //norm2 = a * a + b * b;
    __asm fld a 
    __asm fmul st(0),st(0) 
    __asm faddp st(1),st(0) 
    __asm fstp norm2
  
  }

虽然我的汇编代码可以工作,但速度很慢。我怎样才能加快速度?

解决方法

这个有很多需要改进的地方。以下是一些要点:

不要在 MSVC 风格的内联汇编中编程

MSVC 风格的内联汇编可能很容易编程,但它也强制所有变量都存在于内存中。每次读取或分配给一个变量时,都会执行缓慢的内存访问。这会严重影响性能。

相反,在单独的程序集文件中以程序集形式编写整个函数。如果这是不可能的,至少在开始您的汇编代码时将所有变量加载到寄存器中,然后完全在这些寄存器上进行计算,并通过将寄存器写回变量来结束汇编部分。这样一来,无用的数据移动量就会降到最低。

执行此操作时,请在汇编中实现 for 循环本身,这样您就不必在每次迭代中写出然后读回所有变量,而在整个循环中只需一次。

在寄存器中保留尽可能多的值

如前所述,所有这些 fldfstp 指令都需要时间。将数字保存在寄存器中,这样您就不必经常重新加载它们。此外,如果这是不可能的,至少将加载和存储合并到以下说明中。例如,代替

__asm fld x 
__asm faddp st(1),st(0) 

你可以这样做

__asm fadd x

但最好将所有内容都保存在寄存器中。例如,您只需将 c 变量保留在堆栈中即可轻松摆脱它。

不要做两次工作

您的代码计算 a*ab*b 两次:一次在前一次迭代中计算 norm2,一次在下一次迭代中计算 c。计算一次这些乘积并保留它们以节省两次乘法。

使用更便宜的指令而不是更昂贵的指令。

回想一下 2x = x + x 并用加法替换昂贵的常量和乘法负载。

还记得 a² - b² = (a + b)(a - b) 用加法代替乘法。请注意,这可能会更改舍入并且与“不要执行两次工作”建议不兼容。但也许它可以用于初始迭代。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...