【问题标题】:Convert C++ code to x87 inline assembly code将 C++ 代码转换为 x87 内联汇编代码
【发布时间】:2021-08-16 16:30:36
【问题描述】:

我正在尝试将 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, b = 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 fld b 
    __asm fmul st(0), st(0) 
    __asm faddp st(1), st(0) 
    __asm fstp norm2
  
  }

虽然我的汇编代码可以工作,但速度很慢。如何加快速度?

【问题讨论】:

  • 找到一个支持你的平台的编译器并让它生成优化的代码,然后比较两者。
  • 众所周知,这种内联汇编方式效率低下,因为需要大量重新加载。尝试完全在汇编中编写汇编函数,而不是使用内联汇编。
  • @RichardCritten 我尝试使用godbolt,但我不太明白。我无法复制和粘贴并运行 Godbolt 的输出
  • 更好的问题,使用优化编译器编译时原始代码会变慢吗?你需要组装吗?特别是x87?这是您应用程序的瓶颈吗?
  • 是的,我需要组装,是的 x87。它是项目规范的一部分。原始代码现在比我的汇编代码快。我希望它至少具有相同的速度

标签: c++ assembly inline-assembly x87


【解决方案1】:

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

不要在 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) 用加法代替乘法。请注意,这可能会更改舍入,并且与“不要执行两次工作”的建议不兼容。但也许它可以用于初始迭代。

【讨论】:

  • 谢谢,这很有帮助。你有关于如何在汇编中编写循环的提示吗?我正在为此苦苦挣扎
  • @user3702643 您正在努力解决的具体问题是什么?循环只是它开头的标签和结尾的条件跳转,如果条件仍然成立,则返回开头。
  • 循环的条件部分。我该怎么做 norm2
  • @user3702643 对于n &lt; N,使用cmp,后跟jl。对于norm2 &lt; 4.0,这取决于您是否可以使用 Pentium Pro 引入的指令。你可以这样做吗?还是代码也必须在旧处理器上运行?
  • @fuz:公平地说,你可以在一个 MSVC 内联 asm 块中编写整个循环。然后存储/重新加载往返是/在循环之外。这至少与传递指针参数的函数调用一样好。
猜你喜欢
  • 2013-04-15
  • 1970-01-01
  • 2014-09-01
  • 1970-01-01
  • 2020-11-04
  • 2011-05-04
  • 2013-01-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多