Mat*_*szL 8 c++ x86 gcc cpu-architecture
我们用 编译我们的代码g++ -march=ivybridge -mtune=skylake
。如果有人在较旧/不兼容的架构上运行,我希望应用程序能够优雅地通知并退出。我该怎么做呢?AMD处理器怎么样?是否存在某种同等的架构/指令?
这是令人惊讶的困难,并且是特定于编译器的。我将介绍可用于 gcc 的技术,然后是 clang,然后是替代方案。请记住,截至 2023 年末,所提供的信息是正确的;未来可能会改变。
注意:如果您完全控制构建系统,则可以跳过此步骤,因为您可以只传递 eg -march=skylake -DMARCH=skylake
,但无论如何它都值得一读,因为稍后将重用某些技术。
gcc (in ix86_target_macros_internal
) 设置与处理器对应的预处理器宏;因此,如果您有受支持的处理器列表(例如从 中提取processor_names
),您可以测试每个宏(使用#ifdef
或在宏内部使用/sf/answers/3479873141/)。示意图:
char const* target_processor() {
// ...
#if __skylake__
return "skylake";
#endif
#if __skylake_avx512__
return "skylake-avx512";
#endif
// ...
}
Run Code Online (Sandbox Code Playgroud)
请注意,您可能希望确保在没有设置或设置多个宏的情况下发生编译错误,因为在这种情况下您需要更新代码。
gcc 提供__builtin_cpu_is
精确的处理器检测。请注意,参数必须是字符串文字,因为在内部它会转换为检查__cpu_model
global上的整数字段,该字段是通过cpu_indicator_init
调用 from设置的__builtin_cpu_init
。因此,您可以再次列出已知的处理器:
char const* runtime_processor() {
// ...
if (__builtin_cpu_is("skylake"))
return "skylake";
if (__builtin_cpu_is("skylake-avx512"))
return "skylake-avx512";
// ...
return "unknown";
}
Run Code Online (Sandbox Code Playgroud)
请注意,您将需要处理未知处理器的情况,因为您的代码可能在比 gcc 所知的更新的处理器上运行!
即使您知道目标处理器和运行时处理器,这并不总是意味着您知道该处理器将能够运行您的代码;功能在不同时间添加到服务器和客户端线路,可以删除它们(例如 3dNow),它们取决于供应商(Intel 与 AMD),并且某些功能取决于操作系统支持(例如通过 OSXSAVE 的 AVX,通过 XGETBV 在运行时检测) :gcc 的 __builtin_cpu_supports 是否检查操作系统支持?)。因此,最好-march
使用 .NET 来检测运行时是否存在启用的ISA 功能__builtin_cpu_supports
。
您可以使用processor_features
枚举来迭代已知功能,使用设置的宏来ix86_target_macros_internal
检测正在使用的功能,并ISA_NAMES_TABLE
使用表格在这些功能和功能名称字符串之间进行映射。示意图:
void check_features() {
// ...
#if __F16C__
if (!__builtin_cpu_supports("f16c"))
error("f16c not present");
#endif
#if __RDSEED__
if (!__builtin_cpu_supports("rdseed"))
error("rdseed not present");
#endif
// ...
}
Run Code Online (Sandbox Code Playgroud)
通过构建此代码,您可以确保运行时环境实际上支持您告诉 gcc 可用的所有功能(通过标志-march=
)。
不幸的是,对于 clang 来说事情有点棘手。首先,它不设置__processor__
宏,因此如果您想通知用户目标处理器,您将需要通过构建系统传递它。
使用X86TargetParser.def检测运行时处理器版本应该可以工作;__builtin_cpu_is
与 gcc 上的工作方式大致相同。
其次,clang在支持 gcc 所了解的 ISA 功能方面远远落后- 目前,它没有公开__builtin_cpu_supports
对过去任何内容的支持avx512vp2intersect
(例如,3dNow、ADX 和更高版本的指令)。您可以直接检查__cpu_model
和__cpu_features2
全局变量,尽管如果您使用 ,这将不起作用-rtlib=compiler-rt
,因为编译器-rt 只设置一组有限且过时的标志。
希望这个问题很快就能得到解决,但与此同时,您可能需要自己编写检查并参考 cpuid 标志;请get_available_features
参阅 libgcc 如何执行此操作,以及getAvailableFeatures
相应的编译器 rt 代码。
一种勉强可行的技术可能是编译具有不同-march=
标志的单独 TU,并使用它来静态检测 clang 期望每个处理器可用的 ISA 功能;但是,这不适用于有条件存在的功能或您的 clang 版本不知道的较新处理器。
更好的替代方案可能是将您的目标架构设置为 x86-64 psABI定义的四个微架构级别(x86-64-v1 到 x86-64-v4)之一,即在您的情况下(v3 是 Haswell,v4 是 Skylake) -AVX512 / Cannon Lake),并使用ifunc 多版本支持 ( ) 为性能关键型代码提供中间和专用处理器支持。-march=x86-64-v2
-mtune
__attribute__ ((target)) / [[gnu::target]]
这种方法的优点是您可以编写__builtin_cpu_supports("x86-64-v2")
(自 gcc 12 起)并知道您至少有一个支持该微体系结构级别的处理器。缺点是 clang 尚未发布对此的支持,但它将在 clang 18 中发布。
如果您在具有基于不同架构构建的性能和效率核心的处理器(例如 Alder Lake、Raptor Lake)上运行,则可能会担心这将如何工作,因为它们的芯片支持不同的指令集。如果硬件公开不同的功能集,如果您的进程可能在 P 核上启动,然后在 E 核上重新调度(或调度一些线程),则可能会出现问题,这就是 CPU 不这样做的原因。当前的软件还没有为此做好准备(并且没有计划对此进行更改)。
Alder Lake 在 P 核心上禁用了 AVX-512,并在 E 核心上添加了 AVX2+BMI2 支持,将所有核心带到 x86-64-v3 以及除 AVX-512 之外的各种其他扩展。 效率核心是否支持与性能核心相同的指令?(是的)。由于性能特征不同,内核之间的适当调整选择仍然可能有所不同。(请参阅Agner Fog 的博客。)
gcc -mtune=alderlake
(我认为)适用于 P 核心。 -mtune=gracemont
适用于 E 核。(或者对于只有 E 核的 CPU。)作为-march
设置,它们启用相同的扩展集。
早期的 Alder Lake 系统允许在 BIOS 中禁用 E 核心或根本不存在 E 核心的情况下启用 AVX-512,但不幸的是英特尔改变了主意,新的微代码不允许 AVX-512(某些 BIOS供应商已经解决了),并且较新的 CPU 步进物理上熔断了 AVX-512,因此即使是旧的微码版本也无法启用它。
Agner Fog 关于如何检测 Intel Alder Lake CPU 中的 P/E-Core?表示 P 和 E 核心通过报告相同的系列/型号cpuid
(除了启用 AVX-512 的旧 Alder Lake 上)。但其他一些cpuid
叶子有不同的数据,根据英特尔混合 CPU 文档,有一个叶子用于检测核心类型。
(除非这种差异因与不良 DRM 的兼容性而被禁用,该 DRM会检测 P 和 E 核心,因为不同的系统试图在同一键上玩游戏。某些 BIOS 中有一个遗留游戏兼容性功能;它可能通过干扰检测来工作机制。)
归档时间: |
|
查看次数: |
313 次 |
最近记录: |