大连仟亿科技
客服中心
  • 电话
  • 电话咨询:0411-39943997
  • 手机
  • 手机咨询:15840979770
    手机咨询:13889672791
网络营销 >更多
您现在的位置:仟亿科技 > 新闻中心 > 常见问题

Java使用指针快速比较字节

作者:billionnet 发布于:2012/6/9 17:14:05 点击量:

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

  1. public int compareTo(byte[] b1, int s1, int l1, byte[] b2, int s2,int l2);

最直观的做法是同时遍历两个数组,两两比较。

  1. public int compareTo(byte[] buffer1, int offset1, int length1,
  2.   byte[] buffer2, int offset2, int length2) {
  3.  // Short circuit equal case
  4.  if (buffer1 == buffer2 && offset1 == offset2
  5.   && length1 == length2) {
  6.   return 0;
  7.  }
  8.  // Bring WritableComparator code local
  9.  int end1 = offset1 + length1;
  10.  int end2 = offset2 + length2;
  11.  for (int i = offset1, j = offset2; i < end1 && j < end2; i++, j++) {
  12.   int a = (buffer1[i] & 0xff);
  13.   int b = (buffer2[j] & 0xff);
  14.   if (a != b) {
  15. return a - b;
  16.   }
  17.  }
  18.  return length1 - length2;
  19. }

如果事情这么简单就结束了,就没有意思了。

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

  1. long sun.misc.Unsafe.getLong(Object o,int offset)

Java提供了一个本地方法,可以最快很好转换byte与long。该函数是直接访问一个对象的内存,内存地址是对象指针加偏移量,返回该地址指向的值。有人说Java很安全,不可以操作指针,所以有的时候性能也不高。其实不对,有了这个Unsafe类,Java一样也不安全。所以Unsafe类中的方法都不是public的,不过没关系,我们有反射。言归正传,下面是使用这种技术手段的实现代码。

  1. public int compareTo(byte[] buffer1, int offset1, int length1,
  2.   byte[] buffer2, int offset2, int length2) {
  3.  // Short circuit equal case
  4.  if (buffer1 == buffer2 && offset1 == offset2
  5.    && length1 == length2) {
  6.   return 0;
  7.  }
  8.  int minLength = Math.min(length1, length2);
  9.  int minWords = minLength / Longs.BYTES;
  10.  int offset1Adj = offset1 + BYTE_ARRAY_BASE_OFFSET;
  11.  int offset2Adj = offset2 + BYTE_ARRAY_BASE_OFFSET;
  12.  
  13.  /*
  14.   * Compare 8 bytes at a time. Benchmarking shows comparing 8
  15.   * bytes at a time is no slower than comparing 4 bytes at a time
  16.   * even on 32-bit. On the other hand, it is substantially faster
  17.   * on 64-bit.
  18.   */
  19.  for (int i = 0; i < minWords * Longs.BYTES; i += Longs.BYTES) {
  20.   long lw = theUnsafe.getLong(buffer1, offset1Adj + (long) i);
  21.   long rw = theUnsafe.getLong(buffer2, offset2Adj + (long) i);
  22.   long diff = lw ^ rw;
  23.  
  24.   if (diff != 0) {
  25.    if (!littleEndian) {
  26.     return (lw + Long.MIN_VALUE) < (rw + Long.MIN_VALUE) ? -1
  27.       : 1;
  28.    }
  29.  
  30.    // Use binary search,一下省略若干代码
  31.    .....
  32.    return (int) (((lw >>> n) & 0xFFL) - ((rw >>> n) & 0xFFL));
  33.   }
  34.  }
  35.  
  36.  // The epilogue to cover the last (minLength % elements.
  37.  for (int i = minWords * Longs.BYTES; i < minLength; i++) {
  38.   int result = UnsignedBytes.compare(buffer1[offset1 + i],
  39.     buffer2[offset2 + i]);
  40.   if (result != 0) {
  41.    return result;
  42.   }
  43.  }
  44.  return length1 - length2;
  45. }

实现比原来复杂了一些。但这次一次可以比较8个字节了。这种getLong函数和系统的字节序是紧紧相关的,如果是小端序操作起来有点麻烦,代码先省略掉。这样操作实际效果如何?我们需要对比测试下。对比两个1M的字节数组,如果使用第一个版本,每次比较平均需要2.5499ms,如果使用第二个版本,需要0.8359ms,提升了3倍。对应这种CPU密集型的操作,这样的提升可是很可观的。

如果要提升性能,使用Unsafe直接访问内存也是不错的选择。



分享到:


评论加载中...
内容:
评论者: 验证码:
  

Copyright@ 2011-2017 版权所有:大连仟亿科技有限公司 辽ICP备11013762-1号   google网站地图   百度网站地图   网站地图

公司地址:大连市沙河口区中山路692号辰熙星海国际2215 客服电话:0411-39943997 QQ:2088827823 42286563

法律声明:未经许可,任何模仿本站模板、转载本站内容等行为者,本站保留追究其法律责任的权利! 隐私权政策声明