64位应用程序和内联汇编

35

我正在使用Visual C++ 2010开发32位Windows应用程序。有些东西我真的想使用内联汇编。但我刚刚意识到,Visual C++不支持64位应用程序中的内联汇编。因此,在将来进行64位移植是一个大问题。

我不知道64位应用程序与32位应用程序有什么不同。是否有可能所有32位应用程序在未来都必须升级为64位?我听说64位CPU具有更多寄存器。由于性能不是我的应用程序关注的重点,因此使用这些额外的寄存器对我来说不是问题。除了64位应用程序可能使用64位CPU特有的寄存器或指令之外,32位应用程序需要升级为64位的其他原因吗?64位应用程序在处理事务时是否与32位应用程序有所不同?

我的应用程序需要与其他操作系统组件(例如驱动程序)进行交互,我知道在64位Windows上这些组件必须是64位的。我的32位应用程序能够与它们兼容吗?


2
只需将汇编转换为C++,问题就解决了。 - David Heffernan
15
既然性能不是我的应用程序的关注点,那么你为什么��要使用内联汇编呢? - Hans Passant
7
@Hans Passant:另一个常见的原因是动态创建代码的程序,例如虚拟机运行时/JIT编译器。它们可以从能够重写代码中获益,如果您不知道之前有哪些代码,这可能会更加困难。但是编写此类VM的人可能不需要问这个问题 - 语句“编译器比人类编写更好的汇编代码”并不适用于编写编译器的人类 ;) - MSalters
另请参见Making assembly function inline in x64 Visual Studio。如果可以让Visual Studio编译器/链接器内联自由的ASM,那么在实践中损失不大。也可以查看How to do a naked function and inline assembler in x64 Visual C++ - jww
@MSalters “编译器比人类写得更好的汇编代码”这种说法并不适用于编写编译器的人类 实际上有时候也可能会适用。举个例子,编写下棋人工智能的人类可能无法在下棋中战胜自己编写的AI。 - WhatsUp
4个回答

15

Visual C++ 不支持x64(或ARM)处理器的内联汇编,因为通常使用内联汇编是个坏主意。

  1. 通常编译器产生的汇编比人类产生的汇编好。
  2. 即使你能够产生比编译器更好的汇编,使用内联汇编通常会破坏任何类型的代码优化器。确实,你的手工优化的一点代码可能更快,但是它周围的代码不能被优化,这通常会导致整个程序变慢。
  3. 编译器内建函数在几乎所有主要编译器中都可用,它们让您以与C和C++语言一致的方式访问高级CPU特性(例如SSE),并且不破坏优化器。

我想知道是否有机会让32位应用程序在未来全部升级到64位。

这取决于您的目标受众。如果您的目标是服务器,则允许用户不安装WOW64子系统是合理的,因为它是服务器--您知道它可能不会运行太多32位代码。我相信Windows Server 2008 R2已经允许在安装为“服务器核心”实例时选择该选项。

由于性能对我的应用程序不是问题,所以使用额外的64位寄存器对我来说不是一个问题。除了64位应用程序使用一些唯一于64位CPU的寄存器/指令之外,32位应用程序是否有其他原因必须升级到64位?

64位与寄存器无关,而是与可寻址虚拟内存的大小有关。

与32位应用程序处理方式不同,64位应用程序处理方式有何不同,除了64位应用程序使用一些唯一于64位CPU的寄存器/指令之外?

很有可能。32位应用程序受到限制,因为它们一次不能将超过约2GB的内容映射到内存中。64位应用程序没有这个问题。即使它们没有使用超过4GB的物理内存,能够寻址超过4GB的虚拟内存有助于将磁盘上的文件映射到内存中。

我的应用程序需要与其他操作系统组件交互,例如驱动程序,我知道在64位的Windows上必须使用64位驱动程序。我的32位应用程序是否兼容它们?

这完全取决于您如何与那些驱动程序通信。如果是通过类似“命名文件接口”的东西进行通信,那么您的应用程序可以保持为32位。如果您尝试执行共享内存之类的操作(天呐!可从用户模式访问带有驱动程序的共享内存?)那么您将不得不构建您的应用程序为64位。


3
更正一下,32位应用程序可以映射大约4GB的内存——这是32位声明的。这取决于操作系统,有些额外的限制可能会出现——在32位Windows上,默认情况下可获得2GB,但可以使用引导开关获得3GB。我认为在大多数64位系统上,应用程序可以获得完整的4GB内存。此外,32位操作系统可以访问超过4GB的物理内存:http://msdn.microsoft.com/en-us/library/aa366778(v=vs.85).aspx#physical_memory_limits_windows_server_2003 - WS 2003 x86 == 64 GB - RnR
17
我不同意你说64位与寄存器无关。X64具有比X86多两倍的寄存器。 - Boofhead
70
非常抱歉打扰一下,但是……微软(Microsoft)决定不在x64中包含内联汇编,因为这通常是个坏主意的人,应该立即被枪毙。我是程序员,让我自己面对你们(微软)认为我会写出的糟糕代码的后果就好了。 - rev
9
当我切换到x64时,他们肯定会失去一个客户。这是我的应用程序,对于我所做的工作,我需要内联汇编(我主要从事逆向工程)。因此,又多了一个GCC客户,少了一个MSVC客户。这仅仅是我的个人意见。 - rev
13
除了速度之外,内联汇编还有其他原因。 - Brennan Vincent
显示剩余10条评论

12

除了@ Billy的精彩发言,如果您真的感觉有必要使用64位汇编语言,则可以使用外部汇编器(如MASM)来完成此操作,请参见此处。 (也可以使用预构建脚本加速这个过程)。


7

Intel C Compiler 15也在64位中具有内联功能。您可以将IC集成到Visual Studio中作为工具集:然后您就可以拥有带有内联汇编的VC ++ 64位。不过需要注意的是,它很昂贵。


2
在此同时,MinGW也具有64位内联汇编语言,而且速度相当快且免费。它曾经在某些数学方面速度很慢,所以我会先比较MSVC和MinGW的性能,看看它是否适合您的应用程序作为一个合理的起点。
此外,手写的汇编语言是否更慢:
1.实际上,人类经常编写比编译器运行更高效的汇编代码——或者至少在我学习编程的70年代和80年代时是普遍的智慧,并且一直延续到2000年左右。 2.你总是可以在“C”或C++中编写代码,将其编译成汇编语言,并进行调整以查看是否可以改进它。这样,您就可以从优化中学习,然后看看您是否可以改进它们。
无论M\$说什么,汇编都非常适合需要高度优化的代码。除非你尝试过,否则你不会真正知道汇编是否会加速代码。其他一切都只是空话。
与上述情况类似,我更喜欢将c++代码编译成汇编语言,然后进行手工优化。这样可以节省大量编写代码的麻烦;通过一些实验,您可能会得到一个测试速度更快的东西。对于现代程序,我从未需要这样做过。通常,其他事情可以使它运行得更快-例如多线程、使用查找表、将时间消耗昂贵的操作移出循环、使用静态分析器、使用实时分析器(例如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 vs. x64的特定经验有关。


1
编译器比80年代要好得多。内联后的常量传播通常允许简化,而内联汇编则会破坏这种优化。此外,现代超标量乱序CPU更适合作为编译器目标(特别是x86-64,其16个寄存器比32位模式中的8个寄存器有了很大的改进),而减慢CPU速度的因素变得更加难以捉摸。但编译器仍然远远不完美。 - Peter Cordes
1
编写比编译器慢的代码非常容易(C++代码用于测试Collatz猜想,比手写汇编快 - 为什么?),但是确实从编译器输出开始并对更改进行基准测试通常可以避免这种危险,至少对于您测试的微架构而言是如此。如果您熟悉Agner Fog的微架构PDF(https://agner.org/optimize)和一系列现代CPU的指令表,那么如果您真的想要,可以尝试击败编译器。 - Peter Cordes
手写的针对某个微架构优化的代码可能不适用于未来的微架构,理论上,10年后的编译器在某个未来的CPU上使用“-march=native”选项可以做得更好。因此,请确保您维护一个合理的C版本,以进行测试和可移植性,并且可以针对未来CPU上由编译器生成的汇编代码进行测试。 - Peter Cordes
2
32位操作系统上的CPU时钟比64位操作系统上的快。我以前从未听说过任何品牌的Intel或者AMD CPU会出现这种情况,也没有看到任何证据来支持这种说法。最大睿频频率不受长模式(全64位模式或在64位内核下兼容32位用户空间)的限制。Agner Fog的优化指南和微架构指南(https://agner.org/optimize/)以及英特尔自己的优化手册都没有提到任何类似的影响。 - Peter Cordes
如果您拥有Skylake或更高版本的处理器,现代操作系统会将时钟速度(P状态)的控制权移交给硬件。否则,不同的操作系统可能需要更长时间才能达到最高的P状态(这使得硬件可以在功率和热预算允许的情况下达到最大的Turbo)。但这只是一个配置细节,而不是根本性的差异。确保让CPU在基准测试之前“热身”到最大时钟速度。如果您要提出像这样的非凡声明,我不会轻易相信,除非您提供更多关于如何进行基准测试以及基于哪些数据得出结论的详细信息;也许可以在新的SO问题中提问。 - Peter Cordes
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接