64 位应用程序和内联汇编
Posted
技术标签:
【中文标题】64 位应用程序和内联汇编【英文标题】:64bit Applications and Inline Assembly 【发布时间】:2011-05-29 07:23:52 【问题描述】:我正在使用 Visual C++ 2010 开发 32 位 Windows 应用程序。有些东西我真的很想使用内联汇编。但我刚刚意识到,Visual C++ 不支持 64 位应用程序中的内联汇编。所以未来移植到64位是个大问题。
我不知道 64 位应用程序与 32 位应用程序有何不同。未来 32 位应用程序是否有可能全部升级到 64 位?我听说 64 位 CPU 有更多的寄存器。由于性能不是我的应用程序关心的问题,因此使用这些额外的寄存器对我来说不是一个问题。是否还有其他原因需要将 32 位应用程序升级到 64 位?除了 64 位应用程序可能使用 64 位 CPU 独有的寄存器或指令之外,64 位应用程序与 32 位应用程序处理事情是否会有所不同?
我的应用程序需要与其他操作系统组件交互,例如驱动程序,我知道在 64 位 Windows 中必须是 64 位。我的 32 位应用程序会与它们兼容吗?
【问题讨论】:
只需将程序集转换为 C++ 即可完成Since performance is not a concern for my applications
,那你到底为什么要使用内联汇编呢?
@Hans Passant:另一个常见的原因是动态创建代码的程序,例如虚拟机运行时/JIT 编译器。他们可以从能够重写代码中受益,如果你不知道以前有哪些代码,这会有点困难。但是编写此类 VM 的人可能不需要问这个问题——“编译器比人类编写更好的程序集”这句话并不真正适用于编写编译器的人;)
另见Making assembly function inline in x64 Visual Studio。如果您可以让 Visual Studio 编译器/链接器内联独立 ASM,那么在实践中这并不是什么大损失。也许How to do a naked function and inline assembler in x64 Visual C++.
【参考方案1】:
Visual C++ does not support inline assembly for x64 (or ARM) processors,因为通常使用内联汇编是个坏主意。
-
通常编译器产生比人类更好的汇编。
即使您可以生成比编译器更好的汇编,使用内联汇编通常也会使任何类型的代码优化器失效。当然,您手动优化的代码可能会更快,但无法优化它周围的代码通常会导致整个程序变慢。
Compiler intrinsics 可从几乎所有主要编译器中获得,让您以与 C 和 C++ 语言一致的方式访问高级 CPU 功能(例如 SSE),并且不会破坏优化器。
我想知道将来 32 位应用程序是否有可能必须全部升级到 64 位。
这取决于您的目标受众。如果你的目标是服务器,那么是的,允许用户不安装 WOW64 子系统是合理的,因为它是一个服务器——你知道它可能不会运行太多的 32 位代码。如果您将其安装为“服务器核心”实例,我相信 Windows Server 2008 R2 已经允许此选项。
由于我的应用程序不关心性能,因此使用额外的 64 位寄存器对我来说不是问题。 32位应用程序将来必须升级到64位,还有其他原因吗?
64 位与寄存器无关。它与可寻址虚拟内存的大小有关。
除了 64 位应用程序使用一些 64 位 CPU 独有的寄存器/指令之外,64 位应用程序进程是否与 32 位应用程序进程不同?
很有可能。 32 位应用程序受到限制,因为它们不能一次将超过 ~2GB 的内容映射到内存中。 64 位应用程序没有这个问题。即使他们不使用超过 4GB 的物理内存,能够寻址超过 4GB 的虚拟内存也有助于将磁盘上的文件映射到内存等。
我的应用程序需要与其他操作系统组件交互,例如驱动程序,我知道在 64 位 Windows 中必须是 64 位。我的 32 位应用程序会与它们兼容吗?
这完全取决于您与这些司机的沟通方式。如果它通过类似“命名文件接口”的方式,那么您的应用程序可以保持为 32 位。如果您尝试执行共享内存之类的操作(哎呀!可以通过驱动程序从用户模式访问共享内存?!?),那么您将不得不将您的应用程序构建为 64 位。
【讨论】:
一个更正 - 32 位应用程序可以映射 ~4GB - 正如 32 位声明的那样 :) 取决于操作系统,会出现一些额外的限制 - 在 32 位 Windows 上,默认情况下您可以获得 2GB,但可以使用 3GB开机开关。我认为在大多数 64 位系统上,应用程序可以获得完整的 4GB。此外,32 位操作系统可以处理超过 4GB 的物理内存:msdn.microsoft.com/en-us/library/… - WS 2003 x86 == 64 GB 我不同意你所说的 64 位与寄存器无关。 X64 的寄存器是 X86 的两倍。 我真的很抱歉碰到这个问题,但是......(在微软)决定不在 x64 中包含内联汇编的人,因为它“通常是一个坏主意”,应该立即被枪杀。我是程序员,让我面对你(微软)假设我要编写的糟糕代码的后果。 当我切换到 x64 时,他们肯定会失去一个客户。这是我的应用程序,对于我所做的,我需要 内联汇编(我主要做逆向工程)。所以多了一位 GCC 客户,少了一位 MSVC 客户。只是我的 5 美分。 内联汇编除了速度之外还有其他原因【参考方案2】:除了@Billy 的精彩文章之外,如果您真的觉得需要使用 64 位汇编,那么您可以使用像 MASM 这样的外部汇编器来完成这项工作,see this。 (也可以使用预构建脚本加快速度)。
【讨论】:
【参考方案3】:英特尔 C 编译器 15 也具有 64 位的内联功能。 您可以将 IC 作为工具集集成到 Visual Studio 中:然后您将拥有 VC++ 64 位和内联汇编。 虽然有一个问题 - 它很贵 干杯
【讨论】:
【参考方案4】:虽然我们正在使用它,但 MinGW 还具有 64 位内联汇编语言;而且速度很快,而且免费。它曾经在一些数学上很慢;所以我会开始比较 MSVC 与 MinGW 的性能,看看它是否适合您的应用程序。
另外,关于手工编码的组装速度较慢:
-
实际上,人类经常进行比编译器更高效的代码汇编 - 或者至少当我在 70 年代和 80 年代学习编程时,这一直是普遍的看法,并且一直持续到 2000 年左右。
您始终可以使用“C”或 C++ 对其进行编码,将 that 编译为程序集,然后对其进行调整以查看是否可以改进它。这样,您可以从优化中学习; 和看看你是否可以改进它们。
无论 M$ 说什么,程序集都可以在需要高度优化的代码中占有一席之地。在您尝试之前,您不会真正知道汇编是否会加速代码。其他一切都只是自以为是。
如上所述,我喜欢将 c++ 代码编译成汇编,然后手动优化的方法。它省去了你写很多东西的麻烦;并且通过一些实验,您可能会得到更快测试出来的东西。 FWIW,我从来不需要现代程序。通常,其他事情可以加快速度,甚至更多 - 例如。例如多线程,使用查找表,将耗时的操作移出循环,使用静态分析器,使用实时分析器,如 valgrind(如果你在 Linux 上)等。但是,对于性能关键应用程序,我认为没有理由不尝试;如果它有效,就使用它。 M$ 只是懒惰地放弃了内联汇编。
至于 64 位还是 32 位更快,这类似于 16 位与 32 位的情况。更宽的带宽可以更快地传输大量数据。如果两者都在 64 位操作系统上运行,则它们以完全相同的时钟速度运行;所以32位程序不应该更快。然而,我观察到 32 位 Win7 上的 CPU 时钟运行速度略快于 64 位 Win7。因此,对于相同数量的线程和更多 CPU 密集型操作,32 位 Win7 上的 32 位应用程序会更快。但是,差异并不大。和 64 位指令真的可以有所作为。但是,给定用户将只安装一个操作系统;因此,对于该操作系统,64 位应用程序要么更快;要么或者如果在 64 位操作系统上运行 32 位应用程序,则最好是相同的速度。然而,这将是一个更大的下载。您不妨选择 64 位可能更快的速度;除非您正在处理专用系统运行代码,否则您知道不会移动大量数据。
另外,请注意,我使用各自版本的 MinGW 在各自大小的操作系统上对 64 位和 32 位应用程序进行了基准测试。它做了很多 64 位浮点数运算,我确信 64 位版本会有优势。它没有!我的猜测是,内置数学协处理器中的浮点寄存器在两个操作系统上以相同数量的时钟周期运行,在 64 位 Win7 上可能会稍微慢一些。我的基准测试在两个版本中都非常接近,以致于没有明显更快。在 64 位上,长时间的数字运算操作可能较慢,但 64 位程序代码运行得更快 - 导致几乎相同的结果。
基本上,32 位唯一有意义的时候,恕我直言,是当您认为您可能有一个内部应用程序可以在 32 位操作系统上运行得更快时;您想要一个非常小的可执行文件,或者当您在 32 位操作系统机器(许多开发人员仍然提供两种版本)或 32 位嵌入式系统上交付给用户时。
已编辑以反映我的一些评论与我在 Win7 x86 与 x64 方面的具体经验有关。
【讨论】:
编译器比 80 年代要好得多。内联后的不断传播通常允许内联汇编会失败的简化。此外,现代超标量乱序 CPU 是更好的编译器目标(尤其是具有 16 个寄存器的 x86-64 与 32 位模式下的 8 个寄存器是一个很好的改进),并且减慢 CPU 速度的事情变得更加模糊。但是编译器仍然离完美远。 编写比编译器慢的代码 (C++ code for testing the Collatz conjecture faster than hand-written assembly - why?) 非常容易,但是从编译器输出开始,对您的更改进行基准测试通常可以避免这种危险,至少对于您测试的微架构而言。如果您熟悉 Agner Fog 的 microarch PDF (agner.org/optimize) 和一系列现代 CPU 的指令表,那么如果您真的想的话,一定要尝试击败编译器。 为一个 uarch 调整的手写代码对于未来的 uarch 可能并不完美,理论上 10 年后的编译器在未来的某些 CPU 上使用-march=native
可能会做得更好。因此,请确保您维护一个体面的 C 版本,以进行测试和可移植性以及在未来 CPU 上针对编译器生成的 asm 进行测试。
我不能说我不同意这些;但我要指出的是,与提高 CPU 密集型代码的性能相比,击败优化可能是一个小得多的问题,具体取决于具体情况。毕竟,“用于测试 Collatz 猜想的 C++ 代码比手写汇编更快——为什么?”确实表明了人类可以想到的一些方法来优化编译器没有的汇编;例如在“击败编译器”中。我想我们是说内联汇编可以通过一些努力来完成;但要确保你真的在更大的范围内击败它,并保持 C++ 版本。
32 位操作系统上的 CPU 时钟比 64 位操作系统运行得更快。 我以前从未听说过英特尔或 AMD CPU 的这种说法,也没有见过任何它的证据。最大涡轮时钟不受在长模式(完全 64 位模式或 64 位内核下的兼容 32 位用户空间)而不是传统(纯 32 位)模式下运行的限制。 Agner Fog 的优化指南或微架构指南 (agner.org/optimize) 和英特尔自己的优化手册都没有提到任何类似的效果。以上是关于64 位应用程序和内联汇编的主要内容,如果未能解决你的问题,请参考以下文章