X86 为什么不应该';我不能捕获未定义的指令异常而不是使用CPUID吗?

X86 为什么不应该';我不能捕获未定义的指令异常而不是使用CPUID吗?,x86,cpuid,hammingweight,X86,Cpuid,Hammingweight,假设我想使用可能不可用的指令。这个指令不是透明的回退指令,当它不可用时,它是未定义的指令。比如说它是popcnt 我可以试着调用它而不是使用cpuid吗 如果失败,我将捕获异常,并将此信息保存在bool变量中,并进一步使用不同的分支 当然会有表现的惩罚,但只有一次。这种方法还有其他缺点吗?一个主要困难是为第一次调用提供正确的执行 一旦您通过找出出现故障的指令并对其进行仿真并修改保存的任务状态来解决此问题,问题就变成了包含popcnt的循环的性能,该循环在您乐观地调度到该循环的popcnt版本后运

假设我想使用可能不可用的指令。这个指令不是透明的回退指令,当它不可用时,它是未定义的指令。比如说它是
popcnt

我可以试着调用它而不是使用
cpuid

如果失败,我将捕获异常,并将此信息保存在
bool
变量中,并进一步使用不同的分支


当然会有表现的惩罚,但只有一次。这种方法还有其他缺点吗?

一个主要困难是为第一次调用提供正确的执行

一旦您通过找出出现故障的指令并对其进行仿真并修改保存的任务状态来解决此问题,问题就变成了包含
popcnt
的循环的性能,该循环在您乐观地调度到该循环的
popcnt
版本后运行100万次迭代

如果您的整个代码都是用asm编写的(或者编译器可以为您编写此代码),那么对于信号处理程序来说,收集所有必要的状态并在此类循环的另一个版本中恢复执行可能是合理的,但很难做到

(GNU/Linux信号处理程序获得了一个非标准的关于它们正在运行的线程的已保存寄存器状态的命令,因此理论上您可以在那里这样做。)

这大概只与提前编译相关;如果您正在进行JIT,您应该提前检查CPUID,而不是构建异常处理路径


能够高效地分派意味着您的代码可能已经使用多版本函数的函数指针编写

所以这里唯一保存的是一个简单的init函数,程序运行一次,它运行CPUID几次,并设置所有函数指针。以后根据需要延迟执行意味着更多的缓存未命中,除非有很多函数指针未使用。e、 g.
大型程序--help

这些异常/信号处理程序的代码可能不会比简单的init函数小。这个想法很有趣,但总的来说,我看不到任何有意义的好处


如果您的程序使用多个CPU功能,您还需要知道哪个指令出现故障

如果您正在模拟或做其他事情,您需要检查它是否是您期望的指令之一,它可能会引发执行/SIGILL信号。e、 g.通过检查故障地址处的机器代码


但是,如果您让函数跟踪它们刚刚执行的乐观调度(以便它们能够检测它是否工作),那么您需要在每次调度之前设置一个变量,这样实际上会增加额外的开销。

我将在循环失败后重新启动循环,而不尝试确定到底失败了什么。因此,它不会像模拟那样困难和低效。当然,循环的编写方式应该确保在潜在故障之前所做的任何事情都不会对进一步执行有害。但是如果我想进行模拟,这也是可行的:在x64 MSVC上,除了表达式之外,所有这些信息都可以通过
GetExceptionInformation()
,以及
AddVectoredExceptionHandler
回调获得,并且有一个选项返回
EXCEPTION\u CONTINUE\u EXECUTION
@AlexGuteniev:ok,因此,您需要一个有效的try/catch,它在无异常的情况下具有很小但非零的开销。至少可以,因为可能发生跳转,限制了编译器的优化能力。仅限于可以中止并以另一种方式重新尝试的循环确实避免了很多问题。@AlexGuteniev:对于从不出错的情况来说,更快的启动优势似乎很小,我认为即使try/catch没有增加任何每次调用的开销,也不值得这么做。在Linux上,您必须进行系统调用以安装SIGILL的信号处理程序,这将比在用户空间中执行CPUID带来更多的启动开销。如果Windows只是自动拥有SEH,那可能就不同了。让这种方法不可移植也是一个问题。我想的是只有两个分支:对于CPU,程序实际上运行,而CPU是在最低要求中指定的。所以安装
CPUID
ing
SSE2/3/4
POPCNT
,不管怎样,只要在一个地方捕获所有无效指令异常
x64
SEH(与
x86
相反)声称,如果不发生异常,开销几乎为零,因为它使用函数表而不是执行SEH特定的指令。@AlexGuteniev:如果您有一些函数的专用版本,一些函数可能只需要SSSE3、一些SSSE4.1、一些SSE4.2和/或popcnt。你可能有一个AVX+FMA版本或者AVX2版本。旧的CPU是最需要帮助以获得可接受的交互使用性能的CPU,因此您不想因为AVX不能用于不太常用的东西而放弃SSSE3版本的重要内容。(例如,不带AVX的CPU仍然很普遍,奔腾/赛扬版本的当前Intel uarches。还有像Goldmont这样的低功耗CPU。)