如何在运行时区分 C++ 中的英特尔 CPU 代系?

How to differentiate between Intel CPU generations in C++ at runtime?

提问人:Norgannon 提问时间:11/7/2023 最后编辑:phuclvNorgannon 更新时间:11/8/2023 访问量:312

问:

过去,SIMD 在 Intel CPU 上产生了初始化成本。因此,我正在寻找一种方法来区分在 C++ 运行时运行我的程序的一代 Intel CPU。

有没有一种简单的方法可以区分所有比 Ice Lake 更旧的 Intel CPU?

注意:检测运行的 CPU 是否是 Intel CPU 相当简单,但遗憾的是,对于这个用例来说还不够。

C++ x86 英特尔 SIMD 内部函数

评论

4赞 Some programmer dude 11/7/2023
文件(在支持环境中)和指令都可以提供更多信息。多看看这些。/proc/cpuinfocpuid
1赞 harold 11/7/2023
我想您希望有一种简单的方法来从系列/型号中提取“代号”,但这并不是那么简单,也无法面向未来
2赞 MSalters 11/7/2023
这当然是一个XY问题——它首先假设“生成”是有意义的,但真正的动机似乎是初始化成本,并且假设“生成”是负相关的。也许这甚至是一个 XYZ 问题,因为这些初始化成本可能也不是真正的问题。
3赞 Andrey Semashev 11/7/2023
一个很好的实际近似值是测试自 Ice Lake 以来才支持的 CPU 功能。例如,AVX512_VBMI2 或 GFNI。后者也适用于不支持 AVX-512 的 CPU,例如 Alder Lake 及其后代。
2赞 Peter Cordes 11/8/2023
有关 SIMD 的 CPU 频率影响的更详细答案,请参阅降低 CPU 频率的 SIMD 指令

答:

2赞 Henrique Bucher 11/8/2023 #1

您可以让编译器在加载时使用编译器目标属性(或GCC调用的函数多版本控制)为您确定cpu属性,而不是在运行时手动检查cpu属性。

不幸的是,即使在“三大”编译器之间,它也不是标准功能,但 clang 和 gcc 至少都提供了类似的实现。

这里有一个教程,讨论了几种边缘情况的解决方法,但总的来说,这应该是相当透明的。

您可以在 Github 上查看此代码,但基本上有两种方法可以做到这一点。

首先,您可以手动实现每个体系结构的版本target

namespace detail {
inline Vector multiply(const Matrix& m, const Vector& v) {
    Vector r;
    r[0] = v[0] * m[0] + v[1] * m[2];
    r[1] = v[0] * m[1] + v[1] * m[3];
    return r;
}
}  // namespace detail

__attribute__((target("default"))) Vector multiply(const Matrix& m, const Vector& v) {
    printf("default\n");
    return detail::multiply(m, v);
}

__attribute__((target("arch=core2"))) Vector multiply(const Matrix& m, const Vector& v) {
    printf("core2\n");
    return detail::multiply(m, v);
}

__attribute__((target("arch=sandybridge"))) Vector multiply(const Matrix& m, const Vector& v) {
    printf("sandybridge\n");
    return detail::multiply(m, v);
}

其次,如果所有实现在代码中都相似,则在选择所有可能的体系结构进行版本控制时,使用会更方便。target_clones

__attribute__((target_clones("default", "arch=core2", "arch=sandybridge", "arch=haswell", "arch=cascadelake",
                             "arch=znver1", "arch=znver2"))) Vector
multiply(const Matrix& m, const Vector& v) {
    Vector r;
    r[0] = v[0] * m[0] + v[1] * m[2];
    r[1] = v[0] * m[1] + v[1] * m[3];
    return r;
}) 

运行后,链接的代码将自动为目标计算机选择正确的实现。

阅读更多:文档

评论

1赞 Norgannon 11/9/2023
这是一个有趣的选择,知道存在!我肯定必须多读一些关于它的信息。谢谢!您知道 MSVC 是否也具有此功能的实现吗?我找不到它,所以假设它没有,但我可能没有使用正确的关键字。
1赞 Soonts 11/9/2023
@Norgannon 据我所知,事实并非如此。(1) 不支持多版本控制 (2) VC++ 编译器在编译时也不会区分这些代。它只有启用/禁用 AVX1/AVX2/AVX512 的开关,但没有专门针对 Skylake/Zen3/等进行优化的开关。
0赞 Soonts 11/9/2023
如果你真的需要在Visual Studio中,我相信不久前Microsoft发布了一个clang分支作为IDE的可选功能。您可以尝试将编译器从 MSVC 切换到那个编译器,但我没有这方面的实践经验。
0赞 Norgannon 11/9/2023
@Soonts 有趣!从 MSVC 完全切换到 Clang 很可能会比这里更麻烦。我可能只是依靠正在实施的 GFNI 来识别 MSVC 上的 Ice Lake+ Intel CPU。我也在研究 GCC/Clang 兼容性,所以......使用这种更可靠的解决方案很诱人,但混合使用这两种解决方案可能是一个糟糕的决定。我会考虑的。也许从长远来看,这将是解决方案,除非问题变得太老了,而那些较旧的CPU在这一点上无处可寻。