假设我想使用可能不可用的指令。而且这条指令不是那些透明的回退,当它不可用时它是未定义的指令。popcnt
举例来说。
我可以不使用cpuid
只是尝试调用它吗?
如果失败,我将捕获异常,并将此信息保存在一个bool
变量中,然后将使用不同的分支。
当然会有性能损失,但只有一次。这种方法还有其他缺点吗?
假设我想使用可能不可用的指令。而且这条指令不是那些透明的回退,当它不可用时它是未定义的指令。popcnt
举例来说。
我可以不使用cpuid
只是尝试调用它吗?
如果失败,我将捕获异常,并将此信息保存在一个bool
变量中,然后将使用不同的分支。
当然会有性能损失,但只有一次。这种方法还有其他缺点吗?
一个主要的困难是正确执行第一次调用。
一旦你通过找出哪条指令出错并模拟它并修改保存的任务状态来解决这个问题,问题就变成了popcnt
在你乐观地分派到该循环的版本之后包含运行 100 万次迭代popcnt
的循环的性能。
如果您的整个代码都是用 asm 编写的(或者编译器可以为您编写此代码),那么信号处理程序收集所有必要的状态并在此类循环的另一个版本中恢复执行可能是合理的,但很难。
(GNU/Linux 信号处理程序对它们正在运行的线程的已保存寄存器状态进行了非标准处理,因此理论上您可以在那里执行此操作。)
大概这仅与提前编译有关;如果您正在 JITing,您应该提前检查 CPUID,而不是构建异常处理路径。
能够有效地调度意味着您的代码可能已经编写了多版本函数的函数指针。
所以这里唯一节省的是你的程序运行一次的一个简单的 init 函数,它运行 CPUID 几次并设置所有函数指针。稍后根据需要懒惰地执行它意味着更多的缓存未命中,除非许多函数指针未被使用。例如large-program --help
。
这些异常/信号处理程序的代码可能不会比简单的 init 函数小。有趣的想法,但总的来说,我没有看到任何有意义的好处。
如果您的程序使用多个 CPU 功能,您还需要知道哪条指令出错。
如果您正在模拟或其他什么,您需要检查它是否是您预期的指令之一,可能会引发#UD
execptions / SIGILL 信号。例如,通过检查故障地址处的机器代码。
但是,如果您让函数跟踪他们刚刚执行了哪个乐观调度(这样他们就可以检测它是否不起作用),那么您需要在每次调度之前设置一个变量,这实际上是额外的开销。