您的位置:首页 > 编程语言 > Java开发

Java使用"指针"快速比较字节

2014-09-03 17:03 465 查看
转载过来的,感谢创作人的耐心讲解:

如何才能快速比较两个字节数组呢?我将问题描述成下面的接口:

publicintcompareTo(byte[]b1,ints1,intl1,byte[]b2,ints2,intl2);
最直观的做法是同时遍历两个数组,两两比较。

publicintcompareTo(byte[]buffer1,intoffset1,intlength1,
byte[]buffer2,intoffset2,intlength2){
// Short circuit equal case
if(buffer1==buffer2&&offset1==offset2
&&length1==length2){
return0;
}
// Bring WritableComparator code local
intend1=offset1+length1;
intend2=offset2+length2;
for(inti=offset1,j=offset2;i<end1&&j<end2;i++,j++){
inta=(buffer1[i]&0xff);
intb=(buffer2[j]&0xff);
if(a!=b){
returna-b;
}
}
returnlength1-length2;
}
如果事情这么简单就结束了,就没有意思了。

如果要提升性能,可以做循环展开等等优化,但这些优化应该依赖JVM来做,新的JVM可以做的很好。那还有什么办法可以提高性能呢?
可以将字节数组合并!!上面的例子中,每个byte被迫转型成了int,再比较。其实我们可以将8个byte转换成一个long,在比较long,这样效果会不会好些?用什么方法转换才是最优的?

1
longsun.misc.Unsafe.getLong(Objecto,intoffset)
Java提供了一个本地方法,可以最快最好转换byte与long。该函数是直接访问一个对象的内存,内存地址是对象指针加偏移量,返回该地址指向的值。有人说Java很安全,不可以操作指针,所以有的时候性能也不高。其实不对,有了这个Unsafe类,Java一样也不安全。所以Unsafe类中的方法都不是public的,不过没关系,我们有反射。言归正传,下面是使用这种技术手段的实现代码。
publicintcompareTo(byte[]buffer1,intoffset1,intlength1,
byte[]buffer2,intoffset2,intlength2){
// Short circuit equal case
if(buffer1==buffer2&&offset1==offset2
&&length1==length2){
return0;
}
intminLength=Math.min(length1,length2);
intminWords=minLength/Longs.BYTES;
intoffset1Adj=offset1+BYTE_ARRAY_BASE_OFFSET;
intoffset2Adj=offset2+BYTE_ARRAY_BASE_OFFSET;

/*
* Compare 8 bytes at a time. Benchmarking shows comparing 8
* bytes at a time is no slower than comparing 4 bytes at a time
* even on 32-bit. On the other hand, it is substantially faster
* on 64-bit.
*/
for(inti=0;i<minWords*Longs.BYTES;i+=Longs.BYTES){
longlw=theUnsafe.getLong(buffer1,offset1Adj+(long)i);
longrw=theUnsafe.getLong(buffer2,offset2Adj+(long)i);
longdiff=lw^rw;

if(diff!=0){
if(!littleEndian){
return(lw+Long.MIN_VALUE)<(rw+Long.MIN_VALUE)?-1
:1;
}

// Use binary search,一下省略若干代码
.....
return(int)(((lw>>>n)&0xFFL)-((rw>>>n)&0xFFL));
}
}

// The epilogue to cover the last (minLength % 8) elements.
for(inti=minWords*Longs.BYTES;i<minLength;i++){
intresult=UnsignedBytes.compare(buffer1[offset1+i],
buffer2[offset2+i]);
if(result!=0){
returnresult;
}
}
returnlength1-length2;
}
实现比原来复杂了一些。但这次一次可以比较8个字节了。这种getLong函数和系统的字节序是紧紧相关的,如果是小端序操作起来有点麻烦,代码先省略掉。这样操作实际效果如何?我们需要对比测试下。对比两个1M的字节数组,如果使用第一个版本,每次比较平均需要2.5499ms,如果使用第二个版本,需要0.8359ms,提升了3倍。对应这种CPU密集型的操作,这样的提升可是很可观的。

如果要提升性能,使用Unsafe直接访问内存也是不错的选择。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐