Is a (common) CPU faster at computing low values than great ones?
问题很简单:将两个低值与一个常见的基本操作(如加法、除法、模、位移位等)结合起来,会比具有更大值的相同操作进行更快的组合吗?
据我所知,这将需要CPU跟踪最重要的位(我认为这不太可能),但业务中可能还有其他一些东西。
我特别要求,因为我经常看到一些低级素数(例如EDOCX1,0),在一些Java的基本类的EDCOX1×1的方法中使用(例如EDCOX1,2,EDCX1,3),这是令人惊讶的,因为更大的值最有可能导致更多的扩散(这对于HASH函数来说通常是好事)。
算术
我不认为有很多流水线处理器(即几乎所有的处理器,除了最小的以外)简单的算术指令的成本会随着寄存器或内存操作数的值而变化。这将使管道的设计更加复杂,在实践中可能适得其反。
我可以想象,一个非常复杂的指令(至少是一个除法)与管道长度相比可能需要很多周期,它可能会显示出这样的行为,因为它可能无论如何都会引入等待状态。Agner Fog写道,这是真的"在AMD处理器上,但在英特尔处理器上"。
如果一条指令不能计算出一个操作,例如大于本机整数宽度的数的乘法,那么对于两个操作数的上半部分都为零的情况,实现很可能包含一个"快速路径"。一个常见的例子是MSVC使用的x86 32位体系结构上的64位乘法。一些较小的处理器没有除法指令,有时甚至没有乘法指令。对于较小的操作数,用于计算这些操作的程序集可能会提前终止。这种效果在较小的体系结构上会更加强烈。
即时值编码
对于即时值(常量),这可能不同。例如,RISC处理器允许在加载/添加立即数指令中立即编码多达16位,并且需要两个操作通过加载上立即数+添加立即数加载32位字,或者必须从程序内存加载常量。
在cisc处理器中,较大的立即数可能占用更多的内存,这可能会减少每个周期可获取的指令数,或增加缓存未命中数。
在这种情况下,较小的常数可能比较大的常数便宜。
我不确定编码差异对Java是否同样重要,因为大多数代码至少最初是以Java字节码的形式分发的,即使JIT启用的JVM也将代码转换为机器代码,一些库类可能具有预编译实现。我不太了解Java字节码,以确定它的常数大小的后果。从我所读到的内容来看,大多数常量通常是通过常量池中的索引加载的,而不是直接在字节码流中编码的,所以我不希望这里有很大的差异(如果有的话)。
强度降低优化
对于非常昂贵的操作(相对于处理器而言),编译器和程序员通常会使用一些技巧,用对常量有效的更简单的计算来替换硬计算,就像在乘法示例中提到的那样,乘法被移位和减法/加法替换。
在给出的示例中(乘以31与乘以65537),我不希望有差异。对于其他数字,会有差异,但它不会与数字的大小完全相关。常数除法通常也被一个神秘的乘法和移位序列所取代。
例如,参见gcc如何将除法转换为13。
在x86处理器上,一些小常量的乘法可以被加载有效地址指令代替,但只能用于某些常量。
总之,我希望这种效果在很大程度上取决于处理器体系结构和要执行的操作。由于Java应该在几乎所有地方运行,所以我认为库作者希望他们的代码在大范围的处理器上有效,包括小的嵌入式处理器,其中操作数的大小将起到更大的作用。