【问题标题】:Using sun.misc.Unsafe, what is the fastest way to scan bytes from a Direct ByteBuffer?使用 sun.misc.Unsafe,从 Direct ByteBuffer 扫描字节的最快方法是什么?
【发布时间】:2013-08-15 17:43:46
【问题描述】:

背景

假设我有一个直接的 ByteBuffer:

ByteBuffer directBuffer = ByteBuffer.allocateDirect(1024);

并假设我将缓冲区传递给AsynchronousSocketChannel,以一次从该套接字读取数据块,最多 X 字节(此处示例中为 1024)。

从套接字到 direct ByteBuffer 的传输时间非常棒,因为它都发生在本机 OS 内存空间中;我还没穿过JVM“血脑”屏障……

问题

假设我的工作是扫描从直接字节缓冲区读回的所有字节,那么最快的方法是什么?

我最初问“...利用sun.misc.Unsafe”,但也许这是错误的假设。

可能的方法

我目前看到三种方法,我最好奇的一种是#3:

  1. (默认)使用 ByteBuffer 的 bulk-get 将字节直接从本机 OS 空间拉入内部 byte[1024] 构造。
  2. (UNSAFE) 使用 Unsafe 的 getByte 操作将值直接从 ByteBuffer 中提取出来,从而跳过 ByteBuffer 的标准 get 操作的所有边界检查。 Peter Lawrey 的回答here 似乎表明 Unsafe 中的那些原始本机方法甚至可以通过 JIT 编译器(“内在函数”)优化为单个机器指令,从而获得更出色的访问时间。 (===UPDATE=== 有趣,看起来底层的 DirectByteBuffer 类与 get/put ops 完全一样。)
  3. (BANANAS) 以某种反人类犯罪的方式,使用 Unsafe,我可以 copy the memory region 的直接 ByteBuffer 到我的 byte[1024] 存在于 VM 内部的相同内存地址,然后开始访问使用标准 int 索引的数组? (这假设“copyMemory”操作可能会在操作系统级别进行出色的优化。

我确实想到,假设 copyMemory 操作完全符合它所宣传的,即使在更优化的操作系统空间中,上面的#2 方法可能仍然是最优化的,因为我是在开始处理之前不要创建缓冲区的副本。

这与“can I use Unsafe to iterate over a byte[] faster?”问题不同,因为如果没有必要,我什至不打算在内部将字节拉入 byte[]。

感谢您的时间;只是好奇是否有人(彼得?)对 Unsafe 做这样的事情发疯了。

【问题讨论】:

    标签: java performance unsafe bytebuffer


    【解决方案1】:

    ByteBuffer 方法非常快,因为这些方法是内在的,VM 已将它们映射到非常低级的指令。比较这两种方法:

        byte[] bytes = new byte[N];
        for(int m=0; m<M; m++)
            for(int i=0; i<bytes.length; i++)
                sum += bytes[i];
    
        ByteBuffer bb = ByteBuffer.allocateDirect(N);
        for(int m=0; m<M; m++)
            for(int i=0; i<bb.remaining(); i++)
                sum += bb.get(i);
    

    在我的机器上,差异是 0.67ns 和 0.81ns(每个循环)。

    我有点惊讶 ByteBuffer 没有 byte[] 快。但我认为你绝对不应该将它复制到一个 byte[] 然后访问。

    【讨论】:

    • 不知道 ByteBuffer 方法的“内在”属性;您是指 DirectBuffer 类上的“本机”方法吗?在这种情况下,这将与我在上面的帖子中的 #2 完全一样,所以这是个好消息。
    • @RiyadKalla 内在!= 本机。内在方法在 JVM 中是“硬编码”的。
    • @assylias 我明白了;我(错误地)指的是我认为 DirectByteBuffer impl 类 (grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/…) 上的“本机”方法,但现在我查看它,我发现没有“本机”方法,它只是利用 Unsafe 来做那些操作。我说错了,谢谢你的收获。
    • 不安全被视为内在的,而不是 ByteBuffer et.al。例如,请参阅 github.com/airlift/slice 以了解 Java 中缓冲区的替代且非常快速的实现
    • 基准吹毛求疵:您是否做了一些有用的事情(例如打印出)sum 的值?因为否则 JIT 会优化它。我觉得 0.67ns 和 0.81ns 每个循环是非常快的,也许真的那么快,但我有点好奇。
    猜你喜欢
    • 1970-01-01
    • 2011-02-02
    • 2012-07-17
    • 2011-05-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-26
    相关资源
    最近更新 更多