您当前的位置: 首页 >  Java

蔚1

暂无认证

  • 0浏览

    0关注

    4753博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Java 中对于位运算的优化以及运用与思考

蔚1 发布时间:2020-03-12 23:31:35 ,浏览量:0

随着 JDK 的发展以及 JIT 的不断优化,我们很多时候都可以写读起来易读但是看上去性能不高的代码了,编译器会帮我们优化代码。之前大学里面学单片机的时候,由于内存以及处理器性能都极其有限(可能很多时候考虑内存的限制优先于处理器),所以很多时候,利用位运算来节约空间或者提高性能,那么这些优秀的思想,放到目前的 Java 中,是否还有必要这么做呢?我们逐一思考与验证下(其实这也是一个关于 Premature optimization 的界定的思考)。

引言

随着 JDK 的发展以及 JIT 的不断优化,我们很多时候都可以写读起来易读但是看上去性能不高的代码了,编译器会帮我们优化代码。之前大学里面学单片机的时候,由于内存以及处理器性能都极其有限(可能很多时候考虑内存的限制优先于处理器),所以很多时候,利用位运算来节约空间或者提高性能,那么这些优秀的思想,放到目前的 Java 中,是否还有必要这么做呢?我们逐一思考与验证下(其实这也是一个关于 Premature optimization 的界定的思考)

1. 乘法与左移位

左移一位,相当于乘以 2,左移 n 位,相当于乘以 2 的 n 次方。

1 >> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>> 16;n += 1;  //大于 N 的最小的 2 的 N 次方n = n >>> 1; //小于 N 的最大的 2 的 N 次方

如果有兴趣,可以看一下 Java 的 ForkJoinPool 类的构造器,其中的 WorkQueue 大小,就是通过这样的转换得来的。

5. 交换两个数字

这个在单片机编程中经常会使用这个位运算性质:一个数字异或自己为零,一个数字异或 0 为自己本身。那么我们就可以利用这个性质交换两个数字。

假设有数字 x,y。我们有x^y^y = x^(y^y)= x^0 = x还有x^y^y^x^y = 0^y = y那么我们可以利用:

x = x ^ y;y = x ^ y; //代入后就是 x^y^yx = x ^ y; //代入后就是 x^y^y^x^y

这个方法虽然很巧妙,但是是一种时间换空间的方式; 我们常用的利用另一个变量实现交换是一种空间换时间的方式,来对比下性能:

@Benchmark@Warmup(iterations = 0)@Measurement(iterations = 300)public int swap_1() {    int x = Integer.MAX_VALUE, y = Integer.MAX_VALUE / 2;    int z = x;    x = y;    y = z;    return x + y;}@Benchmark@Warmup(iterations = 0)@Measurement(iterations = 300)public int swap_2() {    int x = Integer.MAX_VALUE, y = Integer.MAX_VALUE / 2;    x ^= y;    y ^= x;    x ^= y;    return x + y;}

结果:

Benchmark            Mode  Cnt          Score           Error  UnitsBitUtilTest.swap_1  thrpt  300  267787894.370 ± 559479133.393  ops/sBitUtilTest.swap_2  thrpt  300  265768807.925 ± 387039155.884  ops/s

测试来看,性能差异并不明显,利用位运算减少了空间占用,减少了 GC,但是交换减少了 cpu 运算,但是 GC 同样是消耗 cpu 计算,所以,很难界定。目前还是利用中间变量交换的更常用,也更易读一些。

6. bit 状态位

我们为了节省空间,尝尝利用一个数字类型(例如 long 类型)作为状态数,每一位代表一个状态是 true 还是 false。假设我们使用 long 类型,则一个状态数可以最多表示 64 个属性。代码上一般这么写:

public static class Test {    //如果你的 field 是会被并发修改访问,那么最好还是加上缓存行填充防止 false sharing    @jdk.internal.vm.annotation.Contended    private long field;    private static final long SWITCH_1_MASK = 1;    private static final long SWITCH_2_MASK = 1             
关注
打赏
1560489824
查看更多评论
0.1112s