无需复制即可快速读取缓冲区
Reading a Buffer Fast without Copying
所以我有这个 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 数组并在我的代码中使用它时,我必须采用以下两种方式之一:
方式一:(将缓冲区(ints)的内容复制到一个int数组中)
int[] ar = new int[ib.capacity];
ib.get(ar);
int int_at_index_0 = ar[0];
方式二:(直接从缓冲区读取)
ib.get(0); //reads int at index 0 directly
现在据我了解,方式 1 将存储在直接缓冲区中的数据复制到堆内存中,这是我不想要的,并且违背了使用 Off- 的目的堆存储方式。
方法 2 get(index)
方法花费的时间太长,如下面的数据所示:
从 int[]
数组读取:(从 ByteBuffer 复制)
ar[0]
需要 1928 纳秒。
直接从 ByteBuffer 读取:
ib.get(0)
需要 18915 纳秒。
差别很大。
有谁知道我如何从 directbuffer/mappedbytebuffer FAST 读取并且不复制到堆内存(将其保存在堆外)。
您可以按照 Guava 人员在 ThreadLocal 中存储一个小缓冲区(1024 字节)的方式进行操作,如果足够就使用它并且永远不要在 TL 中放置更大的缓冲区。
只要它可以满足大多数请求,它就可以正常工作。没有一些真正的测试,很难说它是否有帮助。
Google Guava ByteSource 似乎是在内存中缓冲的不错选择。与 ByteArrayOutputStream 或 ByteArrayList(来自 Colt 库)等实现不同,它不会将数据合并到一个巨大的字节数组中,而是单独存储每个块。一个例子:
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]
所以我有这个 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 数组并在我的代码中使用它时,我必须采用以下两种方式之一:
方式一:(将缓冲区(ints)的内容复制到一个int数组中)
int[] ar = new int[ib.capacity];
ib.get(ar);
int int_at_index_0 = ar[0];
方式二:(直接从缓冲区读取)
ib.get(0); //reads int at index 0 directly
现在据我了解,方式 1 将存储在直接缓冲区中的数据复制到堆内存中,这是我不想要的,并且违背了使用 Off- 的目的堆存储方式。
方法 2 get(index)
方法花费的时间太长,如下面的数据所示:
从 int[]
数组读取:(从 ByteBuffer 复制)
ar[0]
需要 1928 纳秒。
直接从 ByteBuffer 读取:
ib.get(0)
需要 18915 纳秒。
差别很大。
有谁知道我如何从 directbuffer/mappedbytebuffer FAST 读取并且不复制到堆内存(将其保存在堆外)。
您可以按照 Guava 人员在 ThreadLocal 中存储一个小缓冲区(1024 字节)的方式进行操作,如果足够就使用它并且永远不要在 TL 中放置更大的缓冲区。
只要它可以满足大多数请求,它就可以正常工作。没有一些真正的测试,很难说它是否有帮助。
Google Guava ByteSource 似乎是在内存中缓冲的不错选择。与 ByteArrayOutputStream 或 ByteArrayList(来自 Colt 库)等实现不同,它不会将数据合并到一个巨大的字节数组中,而是单独存储每个块。一个例子:
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]