当前位置: 首页 > 面试题库 >

比较直接和非直接ByteBuffer的获取/输入操作

殷宾白
2023-03-14
问题内容

从非直接字节缓冲区获取/输入比从直接字节缓冲区获取/输入更快吗?

如果我必须从直接字节缓冲区读取/写入,最好先读取/写入线程本地字节数组,然后再用字节数组完全更新(用于写入)直接字节缓冲区吗?


问题答案:

从非直接字节缓冲区获取/输入比从直接字节缓冲区获取/输入更快吗?

如果将堆缓冲区与不使用本机字节顺序的直接缓冲区进行比较(大多数系统为低字节序,直接字节缓冲区的默认值为大字节序),则性能非常相似。

如果使用本机有序字节缓冲区,则对于多字节值,性能可能会明显更好。因为byte无论您做什么,它都没什么区别。

在HotSpot /
OpenJDK中,ByteBuffer使用Unsafe类,并且许多native方法都被视为内在函数。这是依赖于JVM的,并且AFAIK
Android VM将其视为最新版本中的固有特性。

如果转储生成的程序集,则可以在一条机器代码指令中看到“不安全”中的内在函数。即,它们没有JNI调用的开销。

实际上,如果您要进行微调,则可能会发现ByteBuffer getXxxx或setXxxx的大部分时间都用于边界检查,而不是实际的内存访问。因此,我仍然
必须在必须达到高性能的情况下直接使用Unsafe (注意:Oracle不鼓励这样做)

如果我必须从直接字节缓冲区读取/写入,最好先读取/写入线程本地字节数组,然后再用字节数组完全更新(用于写入)直接字节缓冲区吗?

我不愿看到比这更好的东西。;)听起来很复杂。

通常,最简单的解决方案会更好,更快。

您可以使用此代码自己对此进行测试。

public static void main(String... args) {
    ByteBuffer bb1 = ByteBuffer.allocateDirect(256 * 1024).order(ByteOrder.nativeOrder());
    ByteBuffer bb2 = ByteBuffer.allocateDirect(256 * 1024).order(ByteOrder.nativeOrder());
    for (int i = 0; i < 10; i++)
        runTest(bb1, bb2);
}

private static void runTest(ByteBuffer bb1, ByteBuffer bb2) {
    bb1.clear();
    bb2.clear();
    long start = System.nanoTime();
    int count = 0;
    while (bb2.remaining() > 0)
        bb2.putInt(bb1.getInt());
    long time = System.nanoTime() - start;
    int operations = bb1.capacity() / 4 * 2;
    System.out.printf("Each putInt/getInt took an average of %.1f ns%n", (double) time / operations);
}

版画

Each putInt/getInt took an average of 83.9 ns
Each putInt/getInt took an average of 1.4 ns
Each putInt/getInt took an average of 34.7 ns
Each putInt/getInt took an average of 1.3 ns
Each putInt/getInt took an average of 1.2 ns
Each putInt/getInt took an average of 1.3 ns
Each putInt/getInt took an average of 1.2 ns
Each putInt/getInt took an average of 1.2 ns
Each putInt/getInt took an average of 1.2 ns
Each putInt/getInt took an average of 1.2 ns

我很确定JNI调用花费的时间超过1.2 ns。

为了证明它不是“ JNI”调用,而是引起延迟的周围信号。您可以直接使用Unsafe编写相同的循环。

public static void main(String... args) {
    ByteBuffer bb1 = ByteBuffer.allocateDirect(256 * 1024).order(ByteOrder.nativeOrder());
    ByteBuffer bb2 = ByteBuffer.allocateDirect(256 * 1024).order(ByteOrder.nativeOrder());
    for (int i = 0; i < 10; i++)
        runTest(bb1, bb2);
}

private static void runTest(ByteBuffer bb1, ByteBuffer bb2) {
    Unsafe unsafe = getTheUnsafe();
    long start = System.nanoTime();
    long addr1 = ((DirectBuffer) bb1).address();
    long addr2 = ((DirectBuffer) bb2).address();
    for (int i = 0, len = Math.min(bb1.capacity(), bb2.capacity()); i < len; i += 4)
        unsafe.putInt(addr1 + i, unsafe.getInt(addr2 + i));
    long time = System.nanoTime() - start;
    int operations = bb1.capacity() / 4 * 2;
    System.out.printf("Each putInt/getInt took an average of %.1f ns%n", (double) time / operations);
}

public static Unsafe getTheUnsafe() {
    try {
        Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafe.setAccessible(true);
        return (Unsafe) theUnsafe.get(null);
    } catch (Exception e) {
        throw new AssertionError(e);
    }
}

版画

Each putInt/getInt took an average of 40.4 ns
Each putInt/getInt took an average of 44.4 ns
Each putInt/getInt took an average of 0.4 ns
Each putInt/getInt took an average of 0.3 ns
Each putInt/getInt took an average of 0.3 ns
Each putInt/getInt took an average of 0.3 ns
Each putInt/getInt took an average of 0.3 ns
Each putInt/getInt took an average of 0.3 ns
Each putInt/getInt took an average of 0.3 ns
Each putInt/getInt took an average of 0.3 ns

因此,您可以看到该native调用比JNI调用的预期要快得多。此延迟的主要原因可能是二级缓存速度。;)

全部在i3 3.3 GHz上运行



 类似资料:
  • 我正在寻找一种拦截所有发送到所有已定义方法的请求的方法。 (定义=JpaRepository接口上的任何内容)。 因此,例如,当有人调用repo.findAll()时,我将能够在之前和之后运行通用代码。 (通用=所有实体的相同代码)。 所以我做的是创建一个泛型类并在JpaRepository中实现方法,然后拦截所有请求。 这是注入服务的接口: 这就是豆子 问题是 中,并将其传递给父级。但是它不满足

  • 我想直接使用多步骤输入,例如用户选择F1-并开始步骤选择项目。 目前我找到了下面的示例,并删除了quickOpen和basic输入 https://github.com/microsoft/vscode-extension-samples/blob/master/quickinput-sample/src/extension.ts 我不想从这个开始 我想从这个开始当用户使用F1

  • 直接下载并用 <script> 标签引入,Vue 会被注册为一个全局变量。 在开发环境下不要使用压缩版本,不然你就失去了所有常见错误相关的警告! 开发版本: 包含完整的警告和调试模式 生产版本: 删除了警告,30.67KB min+gzip CDN 我们推荐链接到一个你可以手动更新的指定版本号: <script src="https://cdn.jsdelivr.net/npm/vue@2.5.

  • 问题内容: 我对块数据存储有特殊需要。我的数据是大小为4096的格式化数据块。为了提高效率,我想直接在硬盘扇区上操作该块,并且不想将数据块视为文件。我认为一种方法是将设备视为/ dev / sda1之类的文件,并使用lseek()read()和write()读取和写入数据。但是我不知道文件的头是否是硬盘的第一个扇区。我也怀疑这种方法的效率。 我正在使用Linux OS和C编程语言。 处理硬盘扇区的

  • 问题内容: 如果我使用像这样的比较(a是int,b和c是float / double)是否安全: 它可能听起来很荒谬,但是在我的旧编程语言中,有时1 + 2 == 3是错误的(因为左侧返回2.99999999999 …)。而且,这呢: 问题答案: 通常,由于不能精确地将许多十进制数字表示为or 值,因此这样做并不安全。经常陈述的解决方案是测试数字之间的差异是否小于某个“小”值(在数学文献中通常用希