Java中128位序列按位操作的优化
为了加快Java代码处理问题的速度,我专门研究了一个类,该类通过操纵两个long来执行128位的逐位操作(请参见实现)。实际上,我只需要100位的数据结构,但我认为没有更好的方法来实现这一点Java中128位序列按位操作的优化,java,optimization,bit-manipulation,bitwise-operators,bit,Java,Optimization,Bit Manipulation,Bitwise Operators,Bit,为了加快Java代码处理问题的速度,我专门研究了一个类,该类通过操纵两个long来执行128位的逐位操作(请参见实现)。实际上,我只需要100位的数据结构,但我认为没有更好的方法来实现这一点 public class BitBoard { //Bit-Masks for all N-Bits from the RIGHT public final static long[] GET_N_BITS_FROM_RIGHT = {0x0000000000000000L, 0x000000000000
public class BitBoard {
//Bit-Masks for all N-Bits from the RIGHT
public final static long[] GET_N_BITS_FROM_RIGHT = {0x0000000000000000L, 0x0000000000000001L, 0x0000000000000003L, 0x0000000000000007L, 0x000000000000000fL, 0x000000000000001fL, 0x000000000000003fL, 0x000000000000007fL, 0x00000000000000ffL, 0x00000000000001ffL, 0x00000000000003ffL, 0x00000000000007ffL, 0x0000000000000fffL, 0x0000000000001fffL, 0x0000000000003fffL, 0x0000000000007fffL, 0x000000000000ffffL, 0x000000000001ffffL, 0x000000000003ffffL, 0x000000000007ffffL, 0x00000000000fffffL, 0x00000000001fffffL, 0x00000000003fffffL, 0x00000000007fffffL, 0x0000000000ffffffL, 0x0000000001ffffffL, 0x0000000003ffffffL, 0x0000000007ffffffL, 0x000000000fffffffL, 0x000000001fffffffL, 0x000000003fffffffL, 0x000000007fffffffL, 0x00000000ffffffffL, 0x00000001ffffffffL, 0x00000003ffffffffL, 0x00000007ffffffffL, 0x0000000fffffffffL, 0x0000001fffffffffL, 0x0000003fffffffffL, 0x0000007fffffffffL, 0x000000ffffffffffL, 0x000001ffffffffffL, 0x000003ffffffffffL, 0x000007ffffffffffL, 0x00000fffffffffffL, 0x00001fffffffffffL, 0x00003fffffffffffL, 0x00007fffffffffffL, 0x0000ffffffffffffL, 0x0001ffffffffffffL, 0x0003ffffffffffffL, 0x0007ffffffffffffL, 0x000fffffffffffffL, 0x001fffffffffffffL, 0x003fffffffffffffL, 0x007fffffffffffffL, 0x00ffffffffffffffL, 0x01ffffffffffffffL, 0x03ffffffffffffffL, 0x07ffffffffffffffL, 0x0fffffffffffffffL, 0x1fffffffffffffffL, 0x3fffffffffffffffL, 0x7fffffffffffffffL, 0xffffffffffffffffL,};
public final static long[] GET_N_BITS_FROM_LEFT = {0x0000000000000000L, 0x8000000000000000L, 0xc000000000000000L, 0xe000000000000000L, 0xf000000000000000L, 0xf800000000000000L, 0xfc00000000000000L, 0xfe00000000000000L, 0xff00000000000000L, 0xff80000000000000L, 0xffc0000000000000L, 0xffe0000000000000L, 0xfff0000000000000L, 0xfff8000000000000L, 0xfffc000000000000L, 0xfffe000000000000L, 0xffff000000000000L, 0xffff800000000000L, 0xffffc00000000000L, 0xffffe00000000000L, 0xfffff00000000000L, 0xfffff80000000000L, 0xfffffc0000000000L, 0xfffffe0000000000L, 0xffffff0000000000L, 0xffffff8000000000L, 0xffffffc000000000L, 0xffffffe000000000L, 0xfffffff000000000L, 0xfffffff800000000L, 0xfffffffc00000000L, 0xfffffffe00000000L, 0xffffffff00000000L, 0xffffffff80000000L, 0xffffffffc0000000L, 0xffffffffe0000000L, 0xfffffffff0000000L, 0xfffffffff8000000L, 0xfffffffffc000000L, 0xfffffffffe000000L, 0xffffffffff000000L, 0xffffffffff800000L, 0xffffffffffc00000L, 0xffffffffffe00000L, 0xfffffffffff00000L, 0xfffffffffff80000L, 0xfffffffffffc0000L, 0xfffffffffffe0000L, 0xffffffffffff0000L, 0xffffffffffff8000L, 0xffffffffffffc000L, 0xffffffffffffe000L, 0xfffffffffffff000L, 0xfffffffffffff800L, 0xfffffffffffffc00L, 0xfffffffffffffe00L, 0xffffffffffffff00L, 0xffffffffffffff80L, 0xffffffffffffffc0L, 0xffffffffffffffe0L, 0xfffffffffffffff0L, 0xfffffffffffffff8L, 0xfffffffffffffffcL, 0xfffffffffffffffeL, 0xffffffffffffffffL,};
//Sequence left
public long l0;
//Sequence right
public long l1;
public BitBoard(long l0, long l1) {
this.l0 = l0;
this.l1 = l1;
}
public BitBoard and(BitBoard b) {
return new BitBoard(l0 & b.l0, l1 & b.l1);
}
public void andEquals(BitBoard b) {
l0 &= b.l0;
l1 &= b.l1;
}
public BitBoard or(BitBoard b) {
return new BitBoard(l0 | b.l0, l1 | b.l1);
}
public void orEquals(BitBoard b) {
l0 |= b.l0;
l1 |= b.l1;
}
public BitBoard not() {
return new BitBoard(~l0, ~l1);
}
public void notEquals() {
l0 = ~l0;
l1 = ~l1;
}
public BitBoard rightShift(int amount) {
if (amount <= 63) {
return new BitBoard(l0 >>> amount, l1 >>> amount | ((l0 & GET_N_BITS_FROM_RIGHT[amount]) << (64 - amount)));
} else {
return new BitBoard(0, l0 >>> (amount - 64));
}
}
public void rightShiftEquals(int amount) {
if (amount <= 63) {
l1 = l1 >>> amount | ((l0 & GET_N_BITS_FROM_RIGHT[amount]) << (64 - amount));
l0 = l0 >>> amount;
} else {
l1 = l0 >>> (amount - 64);
l0 = 0;
}
}
public BitBoard leftShift(int amount) {
if (amount <= 63) {
return new BitBoard(l0 << amount | ((l1 & GET_N_BITS_FROM_LEFT[amount]) >>> (64 - amount)), l1 << amount);
} else {
return new BitBoard(l1 << (amount - 64), 0);
}
}
public void leftShiftEquals(int amount) {
if (amount <= 63) {
l0 = l0 << amount | ((l1 & GET_N_BITS_FROM_LEFT[amount]) >>> (64 - amount));
l1 = l1 << amount;
} else {
l0 = l1 << (amount - 64);
l1 = 0;
}
}
public BitBoard xOr(BitBoard b) {
return new BitBoard(b.l0 ^ l0, b.l1 ^ l1);
}
public void xOrEquals(BitBoard b) {
l0 ^= b.l0;
l1 ^= b.l1;
}
public int popCount() {
return Long.bitCount(l0) + Long.bitCount(l1);
}
public boolean equalsZero() {
return l1 == 0 && l0 == 0;
}
public int numberOfTrailingZeros() {
int l1Trail = Long.numberOfTrailingZeros(l1);
if (l1Trail == 64) {
return 64 + Long.numberOfTrailingZeros(l0);
} else {
return l1Trail;
}
}
public BitBoard unsetBit(int bit) {
if (bit <= 63) {
return new BitBoard(l0, l1 & ~(1L << bit));
} else {
return new BitBoard(l0 & ~(1L << (bit - 64)), l1);
}
}
public void unsetBitEquals(int bit) {
if (bit <= 63) {
l1 &= ~(1L << bit);
} else {
l0 &= ~(1L << (bit - 64));
}
}}
慢于
BitBoard bb;
BitBoard bb2;
BitBoard bb3;
BitBoard res=bb;
res.andEquals(bb2);
res.notEquals();
res.xOrEquals(bb3);
因为它正在为中间步骤分配新内存
编辑:
我一直在用JMH测试我的方法
基准1测试方法是否到位:
public class MyBenchmark {
@State(Scope.Thread)
public static class Status{
BitBoard[] arr;
@Setup(Level.Trial)
public void init(){
arr= new BitBoard[1000];
for(int i=0;i<arr.length;i++){
arr[i]= new BitBoard((long)(Math.random()*Integer.MAX_VALUE),i);
}
}
}
@Benchmark @OutputTimeUnit(TimeUnit.NANOSECONDS) @BenchmarkMode(Mode.AverageTime)
public BitBoard[] testMethod(Status s) {
BitBoard[] res= new BitBoard[s.arr.length];
for(int i=0;i<s.arr.length;i++){
res[i]= new BitBoard(0,0);
for(int j=i+1;j<s.arr.length-1;j++){
res[i].andEquals(s.arr[j]);
res[i].andEquals(s.arr[j-1]);
res[i].xOrEquals(s.arr[j+1]);
}
}
return res;
}
}
公共类MyBenchmark{
@状态(Scope.Thread)
公共静态类状态{
比特板[]arr;
@设置(水平试验)
公共void init(){
arr=新位板[1000];
对于(int i=0;i
第二个基准不使用就地方法
public class MyBenchmark {
@State(Scope.Thread)
public static class Status{
BitBoard[] arr;
@Setup(Level.Trial)
public void init(){
arr= new BitBoard[1000];
for(int i=0;i<arr.length;i++){
arr[i]= new BitBoard((long)(Math.random()*Integer.MAX_VALUE),i);
}
}
}
@Benchmark @OutputTimeUnit(TimeUnit.NANOSECONDS) @BenchmarkMode(Mode.AverageTime)
public BitBoard[] testMethod(Status s) {
BitBoard[] res= new BitBoard[s.arr.length];
for(int i=0;i<s.arr.length;i++){
for(int j=i+1;j<s.arr.length-1;j++){
res[i]=s.arr[j].and(s.arr[j-1]).xOr(s.arr[j+1]);
}
}
return res;
}
}
公共类MyBenchmark{
@状态(Scope.Thread)
公共静态类状态{
比特板[]arr;
@设置(水平试验)
公共void init(){
arr=新位板[1000];
对于(int i=0;i
似乎就地方法确实提供了一个加速!您所做的是分析而不是基准测试。对于基准测试,有一个接近完美的方法。我不确定是否有分析器,但大多数都是谎言。很多
如果你真的需要避免分配,你可以在紧循环中重复使用一些对象。你绝对应该使用无池,因为对于这样小的对象,分配和GC的开销要大得多
如何最小化分配
我非常不喜欢你的命名,所以我会使用我自己的。你可以像这样扩展你的操作集
void assign(BitBoard that) {
this.high = that.high;
this.low = that.low;
}
void inplaceAnd(BitBoard that) {
this.high &= that.high;
this.low &= that.low;
}
void inplaceAndNot(BitBoard that) {
this.high &= ~that.high;
this.low &= ~that.low;
}
然后,您可以将分配移出紧循环(代价是使代码更加难看)
为什么不应该最小化分配
所有这些就地操作都使代码比中使用不可变的更容易出错,可读性也更低
BitBoard result = a.and(b).or(a.andNot(c));
另外,这个代码段是否…比…慢,因为它正在为中间步骤分配新内存
你必须自己回答自己的问题,因为我们只能说“可能是的,但通常可以忽略不计”。在您的情况下,这可能很重要,但唯一的方法是对您的情况进行基准测试。忘记探查器,让JMH比较两个版本。JVM可能会在重要的地方优化大部分分配。如果可以修改调用方法的对象,为什么还要创建新对象呢?为什么你的或
和
等方法返回位板
而不是说长
?@JacobG。我不能总是这样做,因为我仍然需要旧对象供将来参考。你需要在真实场景中测试这一点(在某些算法中使用你的对象)当你测量时间时,确保你已经通过运行算法几次来预热VM。Hotspot能够进行有趣的优化,在某些情况下,它可能会在堆栈而不是堆上分配你的对象。有关基准测试,请参阅:否,请阅读我链接的帖子。任何像这样提供详细分类的工具,我都会安装应用程序,并中断影响应用程序性能的热点优化—通常是显著的。
BitBoard tmp = new BitBoard(0, 0);
BitBoard result = new BitBoard(0, 0);
for (...) {
// Let's say, you get a, b and c as inputs.
// You should compute a&b | a&~b
// Let's assume, none of a, b, c may be overwritten.
tmp.assign(a);
tmp.inplaceAnd(b);
result.assign(a);
result.inplaceAndNot(c);
result.inplaceOr(tmp);
}
BitBoard result = a.and(b).or(a.andNot(c));