Windows 10 的可移植性是否限制了操作系统功能?

Windows 10 的可移植性是否限制了操作系统功能?

据我所知,Windows 10 可以支持 2015 年的 CPU,而且我知道所有 x86 架构都使用类似的指令集,但如果它支持许多旧 CPU 所没有的功能,那么肯定会缺少一些功能,例如 AVX 等指令集扩展。如果它支持的 CPU 少得多,它会是一个更快的操作系统吗?它为什么仍然支持那么老的 CPU,它是如何支持它们的?

答案1

如果它支持更少的 CPU,它的操作系统是否会更快?

作为一名建造从头开始的整个操作系统,不,它不会“快很多”。在重要的情况下最多会快几个百分点,在绝大多数代码中根本没有区别。我实际上已经在相对较新的硬件(AMD Zen 2 系统)上对此进行了基准测试,对于通用目标(基本上是 2000 年的原始 x86-64 规范)和特定硬件之间的构建之间的差异对于大多数事情(除了多媒体转码、加密、数据压缩和跨架构全系统仿真等高性能密集型事物)之外,不到 1%。

程序可以通过三种方式有效利用“新”指令:

  • 通过内联汇编显式使用。这是许多加密和多媒体编码/解码东西的做法,代码的一般部分调用手写优化的性能敏感部分的实现,这些实现通常使用特殊指令。这通常是最划算的,但也是最难实现的,而且它极好的特定于特定算法。加密、压缩和其他非常大规模的批量数据转换操作可能受益匪浅,但它们是唯一可以受益的东西。
  • 通过编译器中的自动检测隐式使用。典型示例是 x86 上的 POPCNT 指令(您可以在此处找到有关此指令的作用及其重要性的精彩说明:https://vaibhavsagar.com/blog/2019/09/08/popcount/)。GCC、Clang 和几乎所有其他现代编译器都足够智能,可以检测到此指令执行的操作的大多数手动实现,并且只需用对该指令的调用替换手动实现。这可能会对性能产生重大影响,但它通常不会出现在性能至关重要的代码中,除非它是可以从编写良好的内联汇编实现中受益的某些代码的一部分。
  • 通过自动矢量化间接使用。在某些情况下,给定的 CPU 可以并行对多个数据实例执行给定的操作。例如,AVX 和 SSE 提供了许多方法来做到这一点。编译器中的自动矢量化可以检测到某些此类指令可以可以使用,然后简化代码中的循环以使用它们。这通常也可以提供显着的性能优势,但它需要满足许多约束才能正常工作(如果有的话),而且在很多代码中,满足这些约束的情况实际上并不常见。模拟、游戏和类似的东西通常会从中受益匪浅,但许多其他东西却不会。

除了少数非常特殊的地方之外,这些通常都不会对基本“操作系统”代码产生重大影响。这些地方通常对性能至关重要,但性能差异通常很小,大多数用户都不会注意到。

它们真正产生重大影响的地方是特定用户的工作量。但这很少与操作系统有关,通常是应用软件,即使在那里,也往往只是应用程序的一小部分,而不是全部。

为什么它仍然支持那么老的 CPU

金钱。大型企业(即微软实际上从中赚取大量金钱的人)的典型硬件生命周期通常至少为 5 年,前提是他们能够妥善管理硬件的生命周期。10 年、15 年甚至 20 年的生命周期并不罕见,而且我有亲自多次退役比我当时年龄还大的系统。

如果您所有的主要客户仍在使用 5 到 10 年前的硬件,那么您就必须继续支持该硬件,直到他们停止使用或开始亏损为止。

此外,在某种程度上,继续支持旧的 x86 系统实际上更容易。这样做需要更简单的代码,而更简单的代码几乎总是更可靠且更易于维护。

以及如何支持他们?

非常容易。您几乎总是必须选择加入任何“新”功能,构建 64 位 x86 可执行文件的典型编译器将生成可在 2003 年的原始 AMD K8 Opteron 上运行的代码,或者如果不是,则在更新的版本上运行(我见过的最新“默认”目标是 2008 年的硬件)。从那时起,支持旧硬件的唯一问题就是处理硬件错误(请参阅奔腾 F00F 漏洞这是 32 位 x86 时代的一个相当著名的例子)。

如果需要,您可以选择更新的东西,智能软件通常都会这样做。有几种方法可以做到这一点。大多数用户软件通常采用的简单方法是在启动时检查 CPU(通常在 x86 系统上调用 CPUID),然后根据该信息进行分支以选择要使用的代码。更智能的方法(至少 Linux 内核使用)是在启动时同样检查 CPU,然后对代码进行热修补以仅使用所需的代码路径。

答案2

如果它支持更少的 CPU,它的操作系统是否会更快?

从外部很难看出,但你可以看看一些 Linux 发行版正在做的“x86-64-v3”工作——它将是大致相同。(“v2”和“v3”表示不同级别的 CPU 功能,例如 I思考v3 意味着除其他几项外还支持 AVX。)

它怎么还支持那么旧的 CPU 以及它是如何支持它们的?

编译器通常具有调整其生成的机器码的选项。例如,GCC 具有-march=-mtune=和类似的选项可以在编译 C 文件时使用;因此,如果您想编译一个程序以便它在没有 SSE2 的情况下运行,您可以告诉 GCC 这样做。(MSVC 和 Clang 有类似的选项。)这当然会给您最慢的“最低分母”输出,但它很容易实现。

此外,当程序在实际 CPU 上运行时,它可以使用cpuid代码中的 CPU 指令来检查哪些功能特定 CPU能够实现 - 根据结果,它可以在同一函数的不同编译版本之间切换;例如,输入 .c 文件可能有一个“矩阵乘法”函数,编译器会自动将其复制到“基本”、“SSE2”、“AVX”等版本中,以供运行时选择。这可以让您“两全其美”,但需要更多的前期工作,因此通常只在重要的地方进行。

我还看到了一种中间方法,其中整个.c 文件被编译两次,一次编译成“foo_AVX2.dll”,再次编译成“foo_noAVX.dll”,程序使用 CPUID 来决定在启动时加载哪一个。

答案3

Win10 确实放弃了对没有 128 位 Lock-CompareSwap 指令的旧 CPU 的支持,该指令用于实现多线程同步原语。所以是的,Windows 有理由放弃对旧 CPU 的支持。

但是,您提到的 AVX 功能并不是主要问题 - 较新的操作系统只需要能够在切换执行上下文时保存和恢复这些寄存器,因此如果您的操作系统只理解 AVX(甚至是 SSE),AVX-512 将无法在较新的应用程序中使用。

最后,我认为 Windows 主要关注的不是可移植性,Windows 是一个广泛使用的系统,与苹果不断弃用旧 API 并引入新 API 的 macOS 不同,Windows API 必须保持数十年的稳定性,而不会破坏漏洞兼容性!(是的,错误兼容性是真实存在的。)

答案4

它怎么还支持那么旧的 CPU 以及它是如何支持它们的?

它仍然支持旧的 CPU,因为很多旧的 CPU 还存在并且人们正在使用它们。

它是如何支持他们的?

这过于简单化了,但是...

If AVX supported then
{
run code A}
else
  {run code B}
}

现在在大多数情况下,B 代码路径较慢,因为它需要更多行代码来创建替换函数。

您可以使用类似上述“if”语句的代码块替换大多数 CPU 功能。

问题是,代码 B 路径越多,操作系统就越慢,最终会变得太慢而无法用于任何实际工作。

相关内容