3

我知道很多人第一眼看到这个问题可能会立即大喊“Java”,但不,我知道 Java 的品质。请允许我先详细说明我的问题。

通常,当我们希望我们的程序在系统上以本机速度运行时,无论是 Windows、Mac OS X 还是 Linux,我们都需要从源代码编译。如果你想在你的系统中运行另一个系统的程序,你需要使用虚拟机或者模拟器。虽然这些工具允许您在非本地操作系统上使用您需要的程序,但它们有时会出现性能和故障问题。

我们还有一个更新的编译器,称为“JIT Compiler”,编译器会在执行前将字节码程序解析为本地机器语言。使用 JIT Compiler 可能会在很大程度上提高性能,但性能仍然与在本机系统上运行它不同。

Linux 上的另一个程序 WINE 也是在 Linux 系统上运行 Windows 程序的好工具。我曾尝试在其上运行 Team Fortress 2,并尝试尝试一些设置。我在 Windows 上以 1280 x 1024 的中高设置获得约 40 fps。在 Linux 上,我需要将所有内容都调低至 1280 x 1024 以获得约 40 fps。不过有两点值得注意:

  1. 无论我将其设置为低还是高,多边形模型设置似乎都不会影响帧率。
  2. 当有后期处理效果或一些特殊效果需要对当前帧的绘制像素进行操作时,帧率会下降到 10-20 fps。

从这一点来看,我可以看到法线多边形渲染还不错,但是当涉及到需要显卡才能工作的较新渲染方法时,它就会慢下来。

无论如何,这个问题是相当理论的。有什么我们可以做的吗?我看到WINE可以运行STEAM和军团要塞2。虽然有缺陷,但它们可以在较低的设置下运行。或者,也许我还应该问,“是否可以将一个系统上的整个程序翻译到另一个系统而无需从源代码重新编译并获得本机速度? ”我看到我们也有 AOT 编译器,是否可以将它用于某些事情像这样?或者有太多的限制(例如 DirectX 调用或软件架构的差异),使得不可能有一个完美且非原生的以原生速度运行的系统程序?

4

2 回答 2

6

在不重新编译的情况下以本机速度在多个系统上运行相同的编译代码体的第一步是选择一个处理器指令集并丢弃所有其他系统。如果你选择英特尔,那么你必须抛弃 ARM、MIPS、PowerPC 等等,因为一种架构的本机机器代码指令对于其他处理器来说是完全无法理解的。

行。因此,现在的任务是以本机速度在多个系统(都使用相同的处理器架构)上运行相同的已编译本机代码主体,而无需重新编译。因此,基本上,您希望在同一硬件上的不同操作系统下运行相同的代码。

如果硬件相同并且唯一的区别是操作系统,那么简单的答案是肯定的,如果您可以编写代码而不需要对操作系统进行任何调用,那么您就可以做到。没有内存分配。没有控制台输出。没有文件 I/O。没有网络 I/O。没有什么好玩的。

此外,您的代码必须以不需要地址重定位修复的方式编写,因为每个操作系统都有不同的方式来表示可重定位代码。一种方法是在磁盘上完全按照它在内存中出现的方式排列代码,包括为可写数据(全局变量、堆栈和堆)保留空间。然后,运行代码所需要做的就是将文件字节复制到内存中预定义的基地址,然后跳转到起始地址。

MSDOS .com 可执行文件格式至少从 1981 年就开始这样做了,而 CP/M 在此之前很久。

然而,当时的 MSDOS 并没有今天的病毒扫描程序可以与之抗衡。当主机操作系统以外的任何人将文件数据加载到内存中并尝试执行该内存时,病毒扫描程序会非常兴奋。因为,你知道,这正是病毒所做的。

由于每个操作系统都有自己的可执行文件格式,因此您还需要弄清楚如何将“完美”的本机代码块放入所有这些不同操作系统的内存中。您至少需要为每个要在其中运行本机代码块的操作系统编译一个程序加载器。当您为每个要定位的操作系统编写程序加载器时,您还可以定义自己的文件 I/ O 映射到 OS 本机等效项的函数,以便您的本机代码块可以在任何系统上执行文件 I/O。同样适用于控制台 I/O 或图形输出。

哦,等等——这正是 WINE 所做的。

这也是为什么您在 WINE 中看到的帧速率远低于主机操作系统中的相同操作的原因 - WINE 正在将 Win32 GDI 图形调用转换为本机主机操作系统(Linux -> XWindows)提供的东西,并且没有在精确的函数匹配或存在操作语义不匹配的情况下(经常出现这种情况),WINE 必须自己实现所有功能,有时成本很高。

但鉴于 IDE 驱动器、USB 设备和 BIOS 功能等标准化硬件无处不在,也许您无需费心将自己的便携式 API 映射到操作系统内置的任何东西上。只需编写一点代码即可对 IDE 设备进行文件 I/O,使用 VESA BIOS 功能进行图形输出。如果您稍微抽象一下代码,您可以支持多种硬件,并根据您在运行时找到的硬件选择合适的函数指针来使用。

然后,您可以在任何系统(使用一种特定的处理器架构)上以本机速度真正运行您的本机代码块,而无需重新编译。

哦等等——你刚刚写了你自己的操作系统。;>

于 2011-01-09T08:39:31.603 回答
4

是的,在技术上可以将针对一个处理器架构和操作系统编写的二进制可执行程序转换为将在另一个处理器和操作系统上运行的二进制可执行程序。这也是一项不洁的工作。

“本机代码执行速度”术语存在问题。您可以将程序编译为禁用优化的本机代码,生成的代码将以“本机代码执行速度”运行的本机可执行代码,但它可能比启用优化编译的相同源代码运行得更慢。两者都在运行“本机代码执行速度”,但它们运行不同数量和质量的机器代码以实现相同的核心算法。

机器指令比高级源编程语言更原始。将源代码编译成机器码时,会丢失很多信息。例如,数据类型通常被编译器简化为少数机器原语——指针、整数、浮点数。字符串是指向内存的指针。一个 char 是一个整数。对象实例是一个指针。

当您将一个机器指令集翻译成另一个机器指令集时,您会遇到障碍,因为您没有源代码编译器所拥有的关于数据的那么多信息。从源代码编译,编译器可以看到数据中的关系和优化,这些关系和优化仅通过查看机器代码很难发现。

故事时间:Digital Equipment Corporation 创建了一个名为 FX!32 的系统,该系统采用本机编译的 Win32 Intel x86 可执行文件,对其进行反编译,并将逻辑转换为运行 Windows NT AXP 的本机 Alpha AXP 处理器指令。在这种情况下,操作系统至少是从同一块布料上剪下来的,但一个是 32 位的,另一个是 64 位的,而且在机器代码级别,它们的调用约定完全不同。尽管如此,它还是奏效了,而且效果非常好。由于硬件的差异,在 AXP 上运行的 Intel x86 应用程序最终可能比在 Intel 硬件上运行的相同应用程序运行得更快。(FX!32 在英特尔应用程序运行几次后使用分析来重新优化 AXP 代码,因此性能通常开始时非常糟糕,但每次运行应用程序时都会有所改善)

然而,即使一切都在执行原生 AXP 指令,FX!32 翻译应用程序的运行速度从来没有像获取源代码并专门为 AXP 指令集重新编译它那样快。FX!32 翻译的本机 AXP 指令流由于需要完全表示原始 Intel x86 指令的语义而被放大,即使(看不见的)更高级别的算法不需要这些语义的所有方面。

在进行机器指令到机器指令的翻译时,您可以看到/听到交响乐中的每个音符,但您可能难以分辨出哪些音符定义了旋律。

于 2011-01-09T09:40:25.807 回答