【问题标题】:How to do inline assembly in C++ (Visual Studio 2010)如何在 C++ 中进行内联汇编(Visual Studio 2010)
【发布时间】:2011-02-19 20:22:09
【问题描述】:

我正在编写一个性能关键、数字运算的 C++ 项目,其中 70% 的时间用于 200 行核心模块。

我想使用内联汇编优化核心,但我对此完全陌生。不过,我确实知道一些 x86 汇编语言,包括 GCC 和 NASM 使用的那种。

我所知道的:

我必须将汇编程序指令放在我希望它们所在的_asm{} 中。

问题:

  • 我不知道从哪里开始。当我的内联汇编开始起作用时,哪个寄存器在哪个寄存器中?

【问题讨论】:

标签: c++ visual-studio-2010 visual-c++ inline-assembly micro-optimization


【解决方案1】:

当涉及内联汇编时,微软编译器在优化方面很差。它必须备份寄存器,因为如果您使用 eax,那么它不会将 eax 移动到另一个空闲寄存器,它将继续使用 eax。 GCC 汇编器在这方面要先进得多。

为了解决这个问题,微软开始提供intrinsics。这些是进行优化的更好方法,因为它允许编译器与您一起工作。正如克里斯提到的那样,内联汇编在 x64 下也不能在 MS 编译器下工作,所以在那个平台上,你真的最好只使用内在函数。

它们易于使用并具有良好的性能。我承认我经常可以通过使用外部汇编器来缩短几个周期,但它们对于提高生产力非常有用

【讨论】:

  • 我不会说编译器很差,它真的无法知道您是要更改以下 C 代码的寄存器还是要编写自己的一点代码而不影响世界其他地区。当人们责怪编译器时,我通常会发现他们根本不了解所涉及的所有内容。
  • 我可以说它在这种情况下很差,因为它很差。我知道那里发生了什么,而且我也知道微软建议不要使用内联汇编正是出于这个原因。
  • @Blindy “它真的无法知道是否......” - 好吧,GCC 确实知道这一点。因为它提供了向程序的其余部分声明内联程序集的“接口”的方法。
  • 当然,但是 GCC 有一个不同的 asm 关键字。我说的是 VC 本身,出于向后兼容性的原因,它很可能会保留它们所具有的简单的 asm,您无法定义要使用的寄存器。
  • @Blindy:除非您在其中编写整个循环(输入操作数必须在内存中),否则 MSVC 样式的内联 asm 语法不仅不利于性能,it is / was poor and brittle 的实际实现。例如显然,在具有 register-args 调用约定的函数中使用甚至都不安全。没有理由应该是个问题;如果编译器不能证明你没有破坏寄存器,编译器就会溢出。
【解决方案2】:

我真的很喜欢组装,所以我不会在这里反对。您似乎已经分析了您的代码并找到了“热点”,这是正确的开始方式。我还假设有问题的 200 行没有使用很多高级构造,例如 vector

我确实必须给出一点警告:如果数字运算涉及浮点数学,那么您将进入一个痛苦的世界,特别是一整套 specialized instructions,以及一个大学学期的 @987654322 @。

说了这么多:如果我是你,我会在 VS 调试器中使用反汇编视图逐步检查有问题的代码。如果您在阅读代码时感觉很舒服,那是一个好兆头。之后,进行发布编译(调试关闭优化)并为该模块生成 ASM 列表。 那么如果你认为你看到了改进的空间......你有一个开始的地方。其他人的答案已链接到 MSDN 文档,该文档确实很简陋,但仍然是一个合理的开始。

【讨论】:

    【解决方案3】:

    我更喜欢在汇编中编写整个函数,而不是使用inline 汇编。这使您可以在构建过程中将高级语言函数替换为汇编语言函数。此外,您不必担心编译器优化会妨碍您。

    在编写单行汇编之前,打印出您的函数的汇编语言列表。这为您提供了构建或修改的基础。另一个有用的工具是汇编与源代码的交织。这将告诉您编译器如何对特定语句进行编码。

    如果您需要为大型函数插入内联汇编,请为需要内联的代码创建一个新函数。在构建期间再次替换为 C++ 或程序集。

    这些是我的建议,您的里程可能会有所不同 (YMMV)。

    【讨论】:

      【解决方案4】:

      首先去寻找低垂的果实......

      正如其他人所说,Microsoft 编译器的优化很差。您可以通过投资一个像样的编译器(例如英特尔的 ICC)并“按原样”重新编译代码来为自己节省很多精力。您可以从英特尔获得 30 天免费评估许可证并试用。

      此外,如果您可以选择构建 64 位可执行文件,那么在 64 位模式下运行可以产生 30% 的性能提升,因为可用寄存器数量增加了 2 倍。

      【讨论】:

        【解决方案5】:

        寄存器中没有任何内容。随着 _asm 块的执行。你需要把东西移到寄存器中。如果有一个变量:'a',那么你需要

        __asm {
          mov eax, [a]
        }
        

        值得一提的是VS2010自带了微软的汇编器。右键单击一个项目,转到构建规则并打开汇编程序构建规则,然后 IDE 将处理 .asm 文件。

        这是一个更好的解决方案,因为 VS2010 支持 32 位和 64 位项目,而 __asm 关键字在 64 位版本中不起作用。对于 64 位代码,您必须使用外部汇编程序:/

        【讨论】:

          【解决方案6】:

          您可以通过变量名访问变量并将它们复制到寄存器中。 这是来自 MSDN 的示例:

          int power2( int num, int power )
          {
             __asm
             {
                mov eax, num    ; Get first argument
                mov ecx, power  ; Get second argument
                shl eax, cl     ; EAX = EAX * ( 2 to the power of CL )
             }
             // Return with result in EAX
          }
          

          Using C or C++ in ASM blocks 可能对您来说也很有趣。

          【讨论】:

          • 记录在案,在 EAX 中留下一些东西后从非void 函数的末尾脱落 显然在 MSVC 中得到支持,即使函数内联。这与具有 GNU 风格 asm("template" : operands); 的 GCC 完全不同,甚至 Clang 的 -fasm-blocks 语法看起来像 MSVC 风格的内联 asm 也不支持。在所有其他情况下,脱离非void 函数的末尾是未定义的行为。
          猜你喜欢
          • 1970-01-01
          • 2017-05-03
          • 1970-01-01
          • 1970-01-01
          • 2014-04-01
          • 2012-10-22
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多