为什么我不应该捕获未定义指令异常而不是使用 CPUID?

Why shouldn't I catch Undefined Instruction exception instead of using CPUID?

假设我想使用一条可能不可用的指令。并且这条指令不是那些透明的回退,当它不可用时它是未定义的指令。例如说是popcnt

我可以不使用 cpuid 而是尝试调用它吗?

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

当然会有性能损失,但只有一次。这种方法还有其他缺点吗?

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

一旦你通过找出哪个指令出错并模拟它并修改保存的任务状态来解决这个问题,问题就变成了包含 popcnt 的循环的性能,在你乐观地调度到 [ 之后运行 100 万次迭代=10=] 该循环的版本。

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

(GNU/Linux 信号处理程序对它们 运行 所在线程的已保存寄存器状态进行了非标准处理,因此理论上您可以在那里执行此操作。)

大概这只与提前编译有关;如果你是 JITing,你应该提前检查 CPUID 而不是构建异常处理路径。


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

所以这里唯一的保存是你的程序运行一次的一个简单的 init 函数,它运行 CPUID 几次并设置所有函数指针。稍后根据需要懒惰地执行它意味着更多的缓存未命中,除非很多函数指针未被使用。例如large-program --help.

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


如果您的程序使用了多个 CPU 功能,您还需要知道哪个指令出错。

如果你正在模拟或其他什么,你需要检查它是否是你预期的指令之一,可能会引发 #UD 执行/ SIGILL 信号。例如通过检查故障地址处的机器代码。

但是,如果您改为让函数跟踪它们刚刚执行的乐观分派(这样他们就可以检测它是否不起作用),您需要在每次分派之前设置一个变量,这实际上是额外的开销.