为了账号安全,请及时绑定邮箱和手机立即绑定

模运算符和按位与的性能比较

模运算符和按位与的性能比较

慕工程0101907 2021-05-07 18:23:40
我正在努力确定32位整数是偶数还是奇数。我设置了2种方法:模(%)法int r = (i % 2);按位(&)方法int r = (i & 0x1);两种方法都可以成功工作。因此,我将每条线运行15000次以测试性能。结果:模(%)方法(源代码)平均141.5801887ns | SD 270.0700275ns按位(&)方法(源代码)平均141.2504ns | SD 193.6351007ns问题:为什么逐位(&)比除法(%)更稳定?JVM是否根据此处使用AND(&)优化取模(%)?
查看完整描述

3 回答

?
宝慕林4294392

TA贡献2021条经验 获得超8个赞

让我们尝试使用JMH复制。


@Benchmark

@Measurement(timeUnit = TimeUnit.NANOSECONDS)

@BenchmarkMode(Mode.AverageTime)

public int first() throws IOException {

    return i % 2;

}


@Benchmark

@Measurement(timeUnit = TimeUnit.NANOSECONDS)

@BenchmarkMode(Mode.AverageTime)

public int second() throws IOException {

    return i & 0x1;

}

好的,它是可复制的。的first速度略慢于second。现在让我们找出原因。使用以下命令运行它-prof perfnorm:


Benchmark                                 Mode  Cnt   Score    Error  Units

MyBenchmark.first                         avgt   50   2.674 ±  0.028  ns/op

MyBenchmark.first:CPI                     avgt   10   0.301 ±  0.002   #/op

MyBenchmark.first:L1-dcache-load-misses   avgt   10   0.001 ±  0.001   #/op

MyBenchmark.first:L1-dcache-loads         avgt   10  11.011 ±  0.146   #/op

MyBenchmark.first:L1-dcache-stores        avgt   10   3.011 ±  0.034   #/op

MyBenchmark.first:L1-icache-load-misses   avgt   10  ≈ 10⁻³            #/op

MyBenchmark.first:LLC-load-misses         avgt   10  ≈ 10⁻⁴            #/op

MyBenchmark.first:LLC-loads               avgt   10  ≈ 10⁻⁴            #/op

MyBenchmark.first:LLC-store-misses        avgt   10  ≈ 10⁻⁵            #/op

MyBenchmark.first:LLC-stores              avgt   10  ≈ 10⁻⁴            #/op

MyBenchmark.first:branch-misses           avgt   10  ≈ 10⁻⁴            #/op

MyBenchmark.first:branches                avgt   10   4.006 ±  0.054   #/op

MyBenchmark.first:cycles                  avgt   10   9.322 ±  0.113   #/op

MyBenchmark.first:dTLB-load-misses        avgt   10  ≈ 10⁻⁴            #/op

MyBenchmark.first:dTLB-loads              avgt   10  10.939 ±  0.175   #/op

MyBenchmark.first:dTLB-store-misses       avgt   10  ≈ 10⁻⁵            #/op

MyBenchmark.first:dTLB-stores             avgt   10   2.991 ±  0.045   #/op

MyBenchmark.first:iTLB-load-misses        avgt   10  ≈ 10⁻⁵            #/op

MyBenchmark.first:iTLB-loads              avgt   10  ≈ 10⁻⁴            #/op

MyBenchmark.first:instructions            avgt   10  30.991 ±  0.427   #/op

MyBenchmark.second                        avgt   50   2.263 ±  0.015  ns/op

MyBenchmark.second:CPI                    avgt   10   0.320 ±  0.001   #/op

MyBenchmark.second:L1-dcache-load-misses  avgt   10   0.001 ±  0.001   #/op

MyBenchmark.second:L1-dcache-loads        avgt   10  11.045 ±  0.152   #/op

MyBenchmark.second:L1-dcache-stores       avgt   10   3.014 ±  0.032   #/op

MyBenchmark.second:L1-icache-load-misses  avgt   10  ≈ 10⁻³            #/op

MyBenchmark.second:LLC-load-misses        avgt   10  ≈ 10⁻⁴            #/op

MyBenchmark.second:LLC-loads              avgt   10  ≈ 10⁻⁴            #/op

MyBenchmark.second:LLC-store-misses       avgt   10  ≈ 10⁻⁵            #/op

MyBenchmark.second:LLC-stores             avgt   10  ≈ 10⁻⁴            #/op

MyBenchmark.second:branch-misses          avgt   10  ≈ 10⁻⁴            #/op

MyBenchmark.second:branches               avgt   10   4.014 ±  0.045   #/op

MyBenchmark.second:cycles                 avgt   10   8.024 ±  0.098   #/op

MyBenchmark.second:dTLB-load-misses       avgt   10  ≈ 10⁻⁵            #/op

MyBenchmark.second:dTLB-loads             avgt   10  10.989 ±  0.161   #/op

MyBenchmark.second:dTLB-store-misses      avgt   10  ≈ 10⁻⁶            #/op

MyBenchmark.second:dTLB-stores            avgt   10   3.004 ±  0.042   #/op

MyBenchmark.second:iTLB-load-misses       avgt   10  ≈ 10⁻⁵            #/op

MyBenchmark.second:iTLB-loads             avgt   10  ≈ 10⁻⁵            #/op

MyBenchmark.second:instructions           avgt   10  25.076 ±  0.296   #/op

注意周期和说明上的差异。现在,这很明显。该first很关心的迹象,但second不(只按位与)。为了确保这是原因,请看一下程序集片段:


第一的:


0x00007f91111f8355: mov     0xc(%r10),%r11d   ;*getfield i

0x00007f91111f8359: mov     %r11d,%edx

0x00007f91111f835c: and     $0x1,%edx

0x00007f91111f835f: mov     %edx,%r10d

0x00007f6bd120a6e2: neg     %r10d

0x00007f6bd120a6e5: test    %r11d,%r11d

0x00007f6bd120a6e8: cmovl   %r10d,%edx       

第二:


0x00007ff36cbda580: mov     $0x1,%edx

0x00007ff36cbda585: mov     0x40(%rsp),%r10

0x00007ff36cbda58a: and     0xc(%r10),%edx  


查看完整回答
反对 回复 2021-05-19
?
翻翻过去那场雪

TA贡献2065条经验 获得超14个赞

这两个操作对应于不同的JVM处理器指令:

irem     // int remainder (%)
iand     // bitwise and (&)

我读过的某个地方irem通常由JVM实现,而iand在硬件上可用。Oracle解释了以下两条指令:

iand

通过取值1和值2的按位与(连接)来计算int结果。

irem

整数结果为值1-(值1 /值2)*值2。

在我看来,假设这样做iand会减少CPU周期,这似乎是合理的。


查看完整回答
反对 回复 2021-05-19
  • 3 回答
  • 0 关注
  • 238 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信