首页 > 解决方案 > 无需复制即可快速读取缓冲区

问题描述

所以我有这个MappedByteBuffer,其中存储了一个 int 数组(从文件中读取)。

public static void read(String loc) {
    try (FileChannel fileChannel = (FileChannel) Files.newByteChannel(
            Paths.get(loc), EnumSet.of(StandardOpenOption.READ))) {
        MappedByteBuffer mappedByteBuffer = fileChannel
                .map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size());
        if (mappedByteBuffer != null) {
            IntBuffer ib = mappedByteBuffer.asIntBuffer();
            mappedByteBuffer.clear();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

现在,当我想从 Buffer 中读取 int 数组并在我的代码中使用它时,我必须采用以下两种方式:

方式1:(将缓冲区(ints)的内容复制到一个int数组中)

int[] ar = new int[ib.capacity];
ib.get(ar);
int int_at_index_0 = ar[0]; 

方式2:(直接从缓冲区读取)

ib.get(0); //reads int at index 0 directly

现在据我了解,方式 1将存储在直接缓冲区上的数据复制到堆内存中,这是我不想要的,并且违背了使用堆外存储方式的目的。

方式 2 get(index)方法的时间太长了,如下面的数据所示:

从数组中读取int[]:(从 ByteBuffer 复制)

ar[0]需要1928纳秒。

直接从 ByteBuffer 读取:

ib.get(0)需要18915纳秒。

巨大差距。

有谁知道我如何从 directbuffer/mappedbytebuffer FAST中读取而不复制到堆内存(将其保存在堆外)。

标签: javaarraysioheap-memorybytebuffer

解决方案


你可以像 Guava 的家伙在 ThreadLocal 中存储一个小缓冲区(1024 字节)那样做,如果它足够了就使用它,并且永远不要在 TL 中放置一个更大的缓冲区。

只要它可以处理大多数请求,这就会正常工作。没有一些真正的测试,很难判断它是否有帮助。

Google Guava ByteSource 似乎是在内存中缓冲的好选择。与 ByteArrayOutputStream 或 ByteArrayList(来自 Colt Library)之类的实现不同,它不会将数据合并到一个巨大的字节数组中,而是单独存储每个块。一个例子:

List<ByteSource> result = new ArrayList<>();
try (InputStream source = httpRequest.getInputStream()) {
    byte[] cbuf = new byte[CHUNK_SIZE];
    while (true) {
        int read = source.read(cbuf);
        if (read == -1) {
            break;
        } else {
            result.add(ByteSource.wrap(Arrays.copyOf(cbuf, read)));
        }
    }
}
ByteSource body = ByteSource.concat(result);

ByteSource 可以在以后随时读取为 InputStream:

InputStream data = body.openBufferedStream();



[Also you might check this out ][1]

推荐阅读