Skip to content

Support ByteBuffer as a backing storage on JVM #239

Open
@fzhinkin

Description

@fzhinkin

java.nio.ByteBuffer is THE data container in Java NIO APIs. Those who need to use features provided only by the NIO APIs (like non-blocking sockets) are doomed to use ByteBuffer for data transferring. Those who need to achieve better performance or use IO interfaces unavailable in Java StdLib will end up using libraries that might roll out their own data containers but usually still allowing to wrap or directly use ByteBuffer (like Netty or Aeron does).

It's possible to wrap a heap-allocated byte array (the backing storage for kotlinx-io segments) into a HeapByteBuffer, but the use of heap buffers comes with a cost. The majority of NIO API calls eventually perform a native call. If such a call (for example, a native wrapper for POSIX write) needs data, then NIO will supply it in the form of DirectByteBuffer or a memory address extracted from the DirectByteBuffer. If a user had provided DirectByteBuffer, then that buffer will be used, but if it was a HeapByteBuffer, then its content will be copied into an internal cached DirectByteBuffer instance and only then passed to the native API. If the buffer is empty, then the copying cost could be neglected, but as the buffer grows, it starts playing a more significant role in overall performance.

Besides performance issues with NIO API, a buffer residing in native memory is a necessity when it comes to implementing Java API for not yet supported native IO APIs such as io_uring, send w/ MSG_ZEROCOPY flag, epoll in the edge-triggering mode, etc. The only available option for allocating such a buffer and using it in a wide range of JVM versions supported by the Kotlin is by using DirectByteBuffer.

Unfortunately, using direct byte buffers is not always an option:

  • some APIs don't directly support it on JVM (like MessageDigest)
  • manipulations with the buffer itself works significantly slower on Android

So the only viable option might be to support both byte-arrays and ByteBuffers as a backing storage and provide a way to choose what particular implementation to use when starting an app.

Tasks:

  • investigate ByteBuffers advantages/need to support it in kotlinx-io
  • publish results of BB performance investigation
  • evaluate kotlinx-io performance with DirectByteBuffer
  • publish performance characteristics of kotlinx-io w/ BB as a backing storage on JVM
  • refactor the library to allow using different Segment implementations
  • implement DirectByteBuffer-backed segments
  • investigate JDK22 MemorySegments usage instead of BB
  • implement polymorphic segment
  • port some benchmarks to Android
  • evaluate baseline performance on Android
  • evaluate DirectByteBuffers performance on Android
  • investigate R8 features/capabilities/issues
  • finalize and publish a design
  • test-library support for multiple segment types
  • tune performance (rewrite UTF8-manipulation routines, for example)

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions