当前位置: 首页 > 知识库问答 >
问题:

**但是**如何使用sun.misc.unsafe加快byte[]查找速度?

佴飞驰
2023-03-14

我正在尝试:

char aChar = some character

if ((byte) 0 == (unsafe.getByte(base_address + aChar) & mask)){
 // do something
}

而不是:

char aChar = some character

if ((byte) 0 == ( lookup[aChar] & mask )){
 // do something
}

我认为不安全可以比使用常规的数组访问更快地访问内存,并对每个索引进行索引检查...

    null

>

  • 在Oracle java 64位和32位虚拟机上都较慢
  • 无论操作系统和机器体系结构(32位和64位)如何,速度都较慢
  • 即使调用serverJVM选项也会变慢

    Unsafe的速度从9%或更慢(1_GB数组和UnsafeLookup_8B(最快的一个)在32位jvm下面的代码中(64bit甚至更慢??))

    这有什么原因吗?**

    C:\Users\wilf>java -Xms1600m -Xprof -jar "S:\wilf\testing\dist\testing.jar"
    initialize data...
    initialize data done!
    
    use normalLookup()...
    Not found '0'
    time : 1967737 us.
    
    use unsafeLookup_1B()...
    Not found '0'
    time : 2923367 us.
    
    use unsafeLookup_8B()...
    Not found '0'
    time : 2495663 us.
    
    Flat profile of 26.35 secs (2018 total ticks): main
    
      Interpreted + native   Method
      0.0%     1  +     0    test.StackOverflow.main
      0.0%     1  +     0    Total interpreted
    
         Compiled + native   Method
     67.8%  1369  +     0    test.StackOverflow.main
     11.7%   236  +     0    test.StackOverflow.unsafeLookup_8B
     11.2%   227  +     0    test.StackOverflow.unsafeLookup_1B
      9.1%   184  +     0    test.StackOverflow.normalLookup
     99.9%  2016  +     0    Total compiled
    
             Stub + native   Method
      0.0%     0  +     1    sun.misc.Unsafe.getLong
      0.0%     0  +     1    Total stub
    
    
    Flat profile of 0.00 secs (1 total ticks): DestroyJavaVM
    
      Thread-local ticks:
    100.0%     1             Blocked (of total)
    
    
    Global summary of 26.39 seconds:
    100.0%  2023             Received ticks
    
    
    C:\Users\wilf>java -version
    java version "1.7.0_07"
    Java(TM) SE Runtime Environment (build 1.7.0_07-b11)
    Java HotSpot(TM) Client VM (build 23.3-b01, mixed mode, sharing)
    
    initialize data...
    initialize data done!
    
    use normalLookup()...
    Not found '0'
    time : 1631142 us.
    
    use unsafeLookup_1B()...
    Not found '0'
    time : 2365214 us.
    
    use unsafeLookup_8B()...
    Not found '0'
    time : 1783320 us.
    

    在带有Windows 7_64,64位Java的4核AMD64上运行测试:

    use normalLookup()...
    Not found '0'
    time : 655146 us.
    
    use unsafeLookup_1B()...
    Not found '0'
    time : 904783 us.
    
    use unsafeLookup_8B()...
    Not found '0'
    time : 764427 us.
    
    Flat profile of 6.34 secs (13 total ticks): main
    
      Interpreted + native   Method
     23.1%     3  +     0    java.io.PrintStream.println
     23.1%     3  +     0    test.StackOverflow.unsafeLookup_8B
     15.4%     2  +     0    test.StackOverflow.main
      7.7%     1  +     0    java.io.DataInputStream.<init>
     69.2%     9  +     0    Total interpreted
    
         Compiled + native   Method
      7.7%     0  +     1    test.StackOverflow.unsafeLookup_1B
      7.7%     0  +     1    test.StackOverflow.main
      7.7%     0  +     1    test.StackOverflow.normalLookup
      7.7%     0  +     1    test.StackOverflow.unsafeLookup_8B
     30.8%     0  +     4    Total compiled
    
    
    Flat profile of 0.00 secs (1 total ticks): DestroyJavaVM
    
      Thread-local ticks:
    100.0%     1             Blocked (of total)
    
    
    Global summary of 6.35 seconds:
    100.0%    14             Received ticks
     42.9%     6             Compilation
    
  • 共有1个答案

    东方明亮
    2023-03-14

    我认为您发布的两个函数基本相同,因为它们只读取1个字节,然后将其转换为int并进行后续比较。

    每次读取4字节的int或8字节的long要有效得多。我编写了两个函数来做同样的事情:比较两个byte[]的内容,看看它们是否相同:

    职能1:

    public static boolean hadoopEquals(byte[] b1, byte[] b2)
      {
        if(b1 == b2)
        {
          return true;
        }
        if(b1.length != b2.length)
        {
          return false;
        }
        // Bring WritableComparator code local
    
        for(int i = 0;i < b1.length; ++i)
        {
         int a = (b1[i] & 0xff);
         int b = (b2[i] & 0xff);
         if (a != b) 
         {
           return false;
         }
        }
        return true;
      }
    
    public static boolean goodEquals(byte[] b1,byte[] b2)
      {   
        if(b1 == b2)
        {
          return true;
        }
        if(b1.length != b2.length)
        {
          return false;
        }
        int baseOffset = UnSafe.arrayBaseOffset(byte[].class);
    
        int numLongs = (int)Math.ceil(b1.length / 8.0);
    
        for(int i = 0;i < numLongs; ++i)
        {
          long currentOffset = baseOffset + (i * 8);
          long l1 = UnSafe.getLong(b1, currentOffset);
          long l2 = UnSafe.getLong(b2, currentOffset);
          if(0L != (l1 ^ l2))
          {
            return false;
          }
        }
        return true;    
      }
    

    功能1:~670ms

    功能2:~80ms

    2要快得多。

    long l1 = UnSafe.getLong(byteArray, offset);  //8 byte
    if(0L == l1 ^ 0xFF)  //if the lowest byte == 0?
    /* do something */
    if(0L == l1 ^ 0xFF00)  //if the 2nd lowest byte == 0?
    /* do something */
    /* go on... */
    
    package test;
    
    import java.lang.reflect.Field;
    
    import sun.misc.Unsafe;
    
    /**
     * Test the speed in looking up the 1st 0 in a byte array
     * Set -Xms the same as -Xms to avoid Heap reallocation
     * 
     * @author yellowb
     *
     */
    public class StackOverflow
    {
        public static Unsafe UnSafe;
    
        public static Unsafe getUnsafe() throws SecurityException,
                NoSuchFieldException, IllegalArgumentException,
                IllegalAccessException
        {
            Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafe.setAccessible(true);
            Unsafe unsafe = (Unsafe) theUnsafe.get(null);
            return unsafe;
        }
    
        /**
         * use 'byte[index]' form to read 1 byte every time
         * @param buf
         */
        public static void normalLookup(byte[] buf)
        {
            for (int i = 0; i < buf.length; ++i)
            {
                if ((byte) 0 == buf[i])
                {
                    System.out.println("The 1st '0' is at position : " + i);
                    return;
                }
            }
            System.out.println("Not found '0'");
        }
    
        /**
         * use Unsafe.getByte to read 1 byte every time directly from the memory
         * @param buf
         */
        public static void unsafeLookup_1B(byte[] buf)
        {
            int baseOffset = UnSafe.arrayBaseOffset(byte[].class);
            for (int i = 0; i < buf.length; ++i)
            {
                byte b = UnSafe.getByte(buf, (long) (baseOffset + i));
                if (0 == ((int) b & 0xFF))
                {
                    System.out.println("The 1st '0' is at position : " + i);
                    return;
                }
    
            }
            System.out.println("Not found '0'");
        }
    
        /**
         * use Unsafe.getLong to read 8 byte every time directly from the memory
         * @param buf
         */
        public static void unsafeLookup_8B(byte[] buf)
        {
            int baseOffset = UnSafe.arrayBaseOffset(byte[].class);
    
            //The first (numLongs * 8) bytes will be read by Unsafe.getLong in below loop
            int numLongs = buf.length / 8;
            long currentOffset = 0L;
            for (int i = 0; i < numLongs; ++i)
            {
                currentOffset = baseOffset + (i * 8);  //the step is 8 bytes
                long l = UnSafe.getLong(buf, currentOffset);
                //Compare each byte(in the 8-Byte long) to 0
                //PS:x86 cpu is little-endian mode
                if (0L == (l & 0xFF))
                {
                    System.out.println("The 1st '0' is at position : " + (i * 8));
                    return;
                }
                if (0L == (l & 0xFF00L))
                {
                    System.out.println("The 1st '0' is at position : " + (i * 8 + 1));
                    return;
                }
                if (0L == (l & 0xFF0000L))
                {
                    System.out.println("The 1st '0' is at position : " + (i * 8 + 2));
                    return;
                }
                if (0L == (l & 0xFF000000L))
                {
                    System.out.println("The 1st '0' is at position : " + (i * 8 + 3));
                    return;
                }
                if (0L == (l & 0xFF00000000L))
                {
                    System.out.println("The 1st '0' is at position : " + (i * 8 + 4));
                    return;
                }
                if (0L == (l & 0xFF0000000000L))
                {
                    System.out.println("The 1st '0' is at position : " + (i * 8 + 5));
                    return;
                }
                if (0L == (l & 0xFF000000000000L))
                {
                    System.out.println("The 1st '0' is at position : " + (i * 8 + 6));
                    return;
                }
                if (0L == (l & 0xFF00000000000000L))
                {
                    System.out.println("The 1st '0' is at position : " + (i * 8 + 7));
                    return;
                }
            }
    
            //If some rest bytes exists
            int rest = buf.length % 8;
            if(0 != rest)
            {
                currentOffset = currentOffset + 8;
                //Because the length of rest bytes < 8,we have to read them one by one
                for(; currentOffset < (baseOffset + buf.length); ++currentOffset)
                {
                    byte b = UnSafe.getByte(buf, (long)currentOffset);
                    if (0 == ((int) b & 0xFF))
                    {
                        System.out.println("The 1st '0' is at position : " + (currentOffset - baseOffset));
                        return;
                    }
                }
            }
            System.out.println("Not found '0'");
        }
    
        public static void main(String[] args) throws SecurityException,
                NoSuchFieldException, IllegalArgumentException,
                IllegalAccessException
        {
            UnSafe = getUnsafe();
    
            int len = 1024 * 1024 * 1024;  //1G
            long startTime = 0L;
            long endTime = 0L;
    
            System.out.println("initialize data...");
            byte[] byteArray1 = new byte[len];
            for (int i = 0; i < len; ++i)
            {
                byteArray1[i] = (byte) (i % 128 + 1);  //No byte will equal to 0
            }
            //If you want to set one byte to 0,uncomment the below statement
    //      byteArray1[2500] = (byte)0;
            System.out.println("initialize data done!");
    
            System.out.println("use normalLookup()...");
            startTime = System.nanoTime();
            normalLookup(byteArray1);
            endTime = System.nanoTime();
            System.out.println("time : " + ((endTime - startTime) / 1000) + " us.");
    
            System.out.println("use unsafeLookup_1B()...");
            startTime = System.nanoTime();
            unsafeLookup_1B(byteArray1);
            endTime = System.nanoTime();
            System.out.println("time : " + ((endTime - startTime) / 1000) + " us.");
    
            System.out.println("use unsafeLookup_8B()...");
            startTime = System.nanoTime();
            unsafeLookup_8B(byteArray1);
            endTime = System.nanoTime();
            System.out.println("time : " + ((endTime - startTime) / 1000) + " us.");
        }
    }
    
    initialize data...
    initialize data done!
    use normalLookup()...
    Not found '0'
    time : 1271781 us.
    use unsafeLookup_1B()...
    Not found '0'
    time : 716898 us.
    use unsafeLookup_8B()...
    Not found '0'
    time : 591689 us.
    
     类似资料:
    • 问题内容: 我正在编写一个小程序,该程序创建目录中所有文件的索引。它基本上遍历磁盘上的每个文件,并将其存储到可搜索的数据库中,就像Unix的locate。问题是,由于我有大约一百万个文件,因此索引生成非常慢。 一旦生成索引,是否可以快速找到自上次运行以来已在磁盘上添加或删除了哪些文件? 编辑 :我不想监视文件系统事件。我认为风险太高而无法同步,我更喜欢进行快速重新扫描之类的操作,以快速找到添加/删

    • 问题内容: 我有以下查询: 目前,此查询大约需要93分钟才能完成。我想找到使它更快一点的方法。 该表大约有506,000行,其中大约490,000行包含的值,因此我怀疑我是否可以利用此处的任何索引。 该表(未压缩时)中包含约46 gigs的数据,但是该数据的大部分位于名为的文本字段中。我相信简单地加载和卸载许多页面会导致速度下降。一个想法是做一个新表 只是 在和现场,并保持尽可能小。但是,测试该理

    • 主要内容:UnionFind1.java 文件代码:本小节基于上一小节并查集的结构介绍基础操作,查询和合并和判断是否连接。 查询元素所在的集合编号,直接返回 id 数组值,O(1) 的时间复杂度。 ... private int find ( int p ) {     assert p >= 0 && p < count ;     return id [p ] ; } ... 合并元素 p 和元素 q 所属的集合, 合并过程需要遍历一遍所有元素

    • 问题内容: 这是我的代码,但显示了进度。这段代码有什么错误吗?请提供一些想法来解决此问题,或者提供一些与此相关的链接。 问题答案: 更新的答案: 要关闭ProgressHUD:

    • 问题内容: 我有一个MySQL查询(Ubu 10.04,Innodb,Core i7、16Gb RAM,SSD驱动器,优化的MySQL参数): 表em_link_data有大约700万行,em_link有数千行。此查询大约需要 18秒 才能完成。但是,如果我替换子查询的结果并执行以下操作: 那么查询将在不到1毫秒的时间内运行。仅子查询在不到1毫秒的时间内运行,因此索引了列linkid。 如果我将查

    • 我有一个PySpark数据帧,如下所示: 我想在表上进行查找,看看是否存在特定的行。例如,对于,测试,代码应返回,对于,测试,代码应返回。 我试过这个: 不幸的是,这段代码需要很长时间才能执行,而且由于这是一个将执行多次的查找(针对不同的a和B值),我希望有一个更快的方法来完成这项任务。 我正在考虑的其他解决方案有: 将PySpark数据帧转换为Pandas数据帧,因为行查找更快 使用或虽然从我所