Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/349.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/objective-c/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java是否通过二到位移位的幂优化除法?_Java_Optimization_Compiler Construction_Javac - Fatal编程技术网

Java是否通过二到位移位的幂优化除法?

Java是否通过二到位移位的幂优化除法?,java,optimization,compiler-construction,javac,Java,Optimization,Compiler Construction,Javac,Java编译器或JIT编译器是否将除法或乘法优化为恒定的二次幂直至位移位 例如,以下两个语句是否优化为相同 int median = start + (end - start) >>> 1; int median = start + (end - start) / 2; (基本上是针对Java)不,Java编译器不会这样做,因为它无法确定(end-start)的符号是什么。为什么这很重要?负整数上的位移位产生与普通除法不同的结果。在这里您可以看到一个演示: 还要注意,我使用了

Java编译器或JIT编译器是否将除法或乘法优化为恒定的二次幂直至位移位

例如,以下两个语句是否优化为相同

int median = start + (end - start) >>> 1;
int median = start + (end - start) / 2;

(基本上是针对Java)

不,Java编译器不会这样做,因为它无法确定
(end-start)
的符号是什么。为什么这很重要?负整数上的位移位产生与普通除法不同的结果。在这里您可以看到一个演示:

还要注意,我使用了
>
而不是
>
>>
是无符号位移位,而
>
是有符号位移位

System.out.println((-10) >>> 1); // prints 2147483643


@神秘:我写了一个基准测试,它表明编译器/JVM没有进行优化:

虽然公认的答案是正确的,即除法不能简单地用右移代替,但基准测试是错误的。任何运行不到一秒钟的Java基准测试都可能是在测量解释器的性能,而不是您通常关心的事情

我忍不住写了一篇自己的文章,主要是说明它更复杂。我不想完全解释这个问题,但我可以这么说

  • 一个普通师是一个非常缓慢的行动
  • 尽可能避免这种情况
  • 除以一个常数总是得到某种程度的优化
  • 被二的幂除法被右移和负数调整所代替
  • 手动优化的表达式可能更好

如果JVM不这样做,您可以自己轻松地完成

如前所述,负数上的右移与除法的行为不同,因为结果向错误的方向取整。如果你知道股息是非负的,你可以用移位代替除法。如果可能是负数,可以使用以下技术

如果您可以用以下表格表达您的原始代码:

int result = x / (1 << shift);
或者,或者:

int result = (x + ((x >> 31) & ((1 << shift) - 1))) >> shift;
我发现这些技术即使在位移不恒定的情况下也会更快,但显然,如果位移恒定,它们的效益最大。注意:对于
long
x
而不是
int
,将31和32分别更改为63和64


这表明(毫不奇怪)当移位不变时,热点服务器VM可以自动执行此优化,但(也毫不奇怪)热点客户端VM太笨,无法执行此优化。

您是否查看了这两条语句生成的字节码?请注意,有几个编译器。例如javac和eclipse中的那个。@Julien我也在考虑JIT。@WChargin:没错。查看字节码并找到2的除法,并不意味着JIT不能做一些不同的事情。
javac
compliler几乎不做任何优化。只有JIT可以做到这一点,但我认为情况并非如此,因为操作不同。一个除法肯定比3个基本指令慢。根据除以的数值,它们往往在10-70个周期之间。而大多数基本指令只有一个周期。(不计算吞吐量)这里是核心细节:您不需要删除您的答案。编译器不能直接将其优化为一个移位,这是正确的。我只是说它可能会做些别的事情。真是个惊喜!我的C++编译器会这么做。我本以为JIT能够进行这样的“基本”优化。我说“basic”,因为虽然它对程序员来说不一定是基本的(而且你无论如何也不应该手动执行),但对于编译器来说,它被认为是基本的。你的测试只是证明了优化器在这么短的时间内根本没有启动。由于您的测试代码不使用计算结果,Hotspot将完全删除计算,而不是将其替换为移位或类似操作(一旦开始工作)。这反过来又允许完全删除循环,因此您肯定会注意到优化器何时启动。如果你测量的时间超过了几纳秒,它就不会……现在你的基准测试显示出巨大的差异:“运行1:295044177,运行2:3749731100”。然而,它坏得很厉害。请用JMH或caliper benchmark(我的答案)替换它,或者用w.r.t.死代码消除和其他问题来修复它。你的答案是“2的幂除被右移和负数调整所取代”虽然被接受的答案说它不知道是哪一个?@vach我的基准测试清楚地表明优化确实完成了(但是,取决于虚拟机和CPU)。由于消除了死代码,接受答案中的基准完全被破坏,所以我们可以忘记它(如果您不确定该信任谁,请阅读关于JMH blackhole的文章)。
int result = (x + (x >> 31 >>> (32 - shift))) >> shift;
int result = (x + ((x >> 31) & ((1 << shift) - 1))) >> shift;
int result = (x + (x >>> 31)) >> 1;