Class IoUringBackend
- All Implemented Interfaces:
TransportBackend, AutoCloseable
This backend leverages Linux's io_uring interface (introduced in kernel 5.1) to achieve
significantly higher throughput and lower latency compared to traditional NIO. It implements the
TransportBackend interface with io_uring-specific optimizations.
Performance Characteristics
- 1.7x throughput improvement vs NIO with registered buffers (pre-validated memory regions eliminate per-operation address validation overhead)
- 100x syscall reduction with batch submission (multiple operations submitted in a single io_uring_submit() call)
- 2-5μs end-to-end latency vs 50-100μs for NIO (measured ping-pong)
- SQPOLL mode (optional): Kernel thread polls submission queue, eliminating submit syscalls entirely for additional 2-5x improvement
Architecture
The backend manages:
- Ring Memory: Shared memory region containing submission and completion queues
- Buffer Pool: Pre-registered memory buffers for zero-copy I/O
- Fixed Files: Pre-registered file descriptors for faster fd lookup
- Pre-allocated Structures: Timespec, sockaddr, iovec to avoid hot-path allocations
Advanced Features
- Zero-Copy Send (SEND_ZC):
sendZeroCopy(RegisteredBuffer, long)avoids user→kernel data copy - Multi-Shot Receive:
receiveMultishot(RegisteredBuffer, long)keeps recv active across completions - Buffer Rings:
initBufferRing(int, int, short)enables kernel-managed buffer selection - Linked Operations:
submitLinkedEcho(MemorySegment, int, long, long)chains recv→send for echo patterns - Batch Operations:
submitBatchRecv(MemorySegment[], int[], long[], int)/submitBatchSend(MemorySegment[], int[], long[], int)for bulk I/O
Usage Example
TransportConfig config = TransportConfig.builder()
.backendType(BackendType.IO_URING)
.registeredBuffers(RegisteredBuffersConfig.builder()
.numBuffers(256)
.bufferSize(65536)
.build())
.sqPollEnabled(true)
.sqPollCpuAffinity(3)
.build();
IoUringBackend backend = new IoUringBackend();
backend.initialize(config);
backend.registerBufferPool(bufferPool);
backend.connect(new InetSocketAddress("localhost", 8080), token);
backend.submitBatch();
backend.waitForCompletion(1000, handler);
Requirements
- Linux kernel 5.1+ (5.6+ recommended, 6.0+ for zero-copy send)
- liburing installed (liburing.so, typically from liburing-dev package)
- Java 21+ with --enable-native-access=ALL-UNNAMED
- See Also:
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic interfaceExtended completion handler that receives CQE flags. -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionvoidaccept(long token) Accepts an incoming connection (server mode).voidbind(SocketAddress localAddress) Binds to a local address for accepting connections (server mode).voidclose()Closes the backend and releases all resources.voidconnect(SocketAddress remoteAddress, long token) Connects to a remote endpoint asynchronously.createFromAccepted(int handle) Creates a new backend instance from an accepted connection handle.intForce a single io_uring_submit syscall after queuing operations.Returns the backend type identifier.shortGet buffer group ID for multishot receive operations.getBufferRingBuffer(int bufferId) Get a buffer from the buffer ring by ID.intGet the fixed file index (if using fixed files).getStats()Returns statistics about backend operations.booleanInitialize buffer ring with default parameters.booleaninitBufferRing(int nentries, int bufSize, short bgid) Initialize a buffer ring for kernel-managed buffer selection.voidinitialize(TransportConfig config) Initializes the backend with the given configuration.booleanCheck if buffer ring is enabled and active.booleanCheck if fixed file optimization is active for this backend.intpoll(CompletionHandler handler) Polls for completions without blocking.voidreceive(RegisteredBuffer buffer, long token) Receives data into a registered buffer (zero-copy).voidreceive(MemorySegment data, int maxLength, long token) Receives data into a raw memory segment.voidreceiveMultishot(RegisteredBuffer buffer, long token) Start a persistent multi-shot receive operation.voidreceiveMultishot(MemorySegment data, int maxLength, long token) Start a persistent multi-shot receive operation.voidrecycleBufferRingBuffer(int bufferId) Recycle a buffer back to the buffer ring after processing.voidRegisters a buffer pool with this backend for zero-copy I/O.voidsend(RegisteredBuffer buffer, long token) Sends data using a registered buffer (zero-copy).voidsend(MemorySegment data, int length, long token) Sends data using a raw memory segment.voidsendZeroCopy(RegisteredBuffer buffer, long token) Send data using zero-copy mechanism (SEND_ZC).voidsendZeroCopy(MemorySegment data, int length, long token) Send data using zero-copy mechanism (SEND_ZC).intSubmits all queued operations in a batch.intsubmitBatchRecv(MemorySegment[] buffers, int[] lengths, long[] tokens, int count) Submit a batch of receive operations for maximum throughput.intsubmitBatchRecvRegistered(short[] bufferIndices, int[] lengths, long[] tokens, int count) Submit a batch of recv operations using registered buffers.intsubmitBatchSend(MemorySegment[] buffers, int[] lengths, long[] tokens, int count) Submit a batch of send operations for maximum throughput.booleansubmitLinkedEcho(MemorySegment recvBuffer, int recvLen, long recvToken, long sendToken) Submit a linked recv+send echo pattern.booleansubmitLinkedEchoSkipRecvCqe(MemorySegment recvBuffer, int recvLen, long sendToken) Submit linked recv+send with skip on success for efficient echo.booleansubmitLinkedRequestResponse(MemorySegment sendBuffer, int sendLen, MemorySegment recvBuffer, int recvLen, long sendToken, long recvToken) Submit linked send+recv for request-response pattern.booleansubmitMultishotRecvWithBufferRing(long token) Submit a multishot receive with buffer ring selection.booleanReturns whether this backend supports batch submission.booleanReturns whether this backend supports registered buffers.booleanReturns whether this backend supports TLS/SSL.intwaitForCompletion(long timeoutMillis, CompletionHandler handler) Waits for at least one operation to complete.
-
Constructor Details
-
IoUringBackend
public IoUringBackend()Creates a new io_uring backend with default configuration.Allocates the io_uring structure and pre-allocated hot-path structures. The backend must be initialized via
initialize(TransportConfig)before use.This constructor creates a backend that owns the io_uring ring and is responsible for cleanup when closed.
-
-
Method Details
-
initialize
Description copied from interface:TransportBackendInitializes the backend with the given configuration.This is called once during transport creation. Implementations should allocate resources, initialize the I/O subsystem, and prepare for operations.
- Specified by:
initializein interfaceTransportBackend- Parameters:
config- the transport configuration
-
registerBufferPool
Description copied from interface:TransportBackendRegisters a buffer pool with this backend for zero-copy I/O.For io_uring, this calls
io_uring_register_buffers()to pre-register memory regions with the kernel. For other backends, this may be a no-op.- Specified by:
registerBufferPoolin interfaceTransportBackend- Parameters:
pool- the buffer pool to register
-
initBufferRing
public boolean initBufferRing(int nentries, int bufSize, short bgid) Initialize a buffer ring for kernel-managed buffer selection.Buffer rings enable the most efficient multishot receive pattern where the kernel automatically selects buffers from a pre-registered pool.
- Parameters:
nentries- number of buffer ring entries (must be power of 2)bufSize- size of each buffer in the ringbgid- buffer group ID (unique per io_uring instance)- Returns:
- true if buffer ring was successfully initialized
-
initBufferRing
public boolean initBufferRing()Initialize buffer ring with default parameters. Uses 256 entries of 8KB each with group ID 0.- Returns:
- true if buffer ring was successfully initialized
-
isBufferRingEnabled
public boolean isBufferRingEnabled()Check if buffer ring is enabled and active.- Returns:
- true if buffer ring is available for use
-
getBufferGroupId
public short getBufferGroupId()Get buffer group ID for multishot receive operations.- Returns:
- buffer group ID or -1 if not enabled
-
getBufferRingBuffer
Get a buffer from the buffer ring by ID. Used after receiving a CQE with IORING_CQE_F_BUFFER set.- Parameters:
bufferId- buffer ID from CQE- Returns:
- memory segment for the buffer, or null if invalid
-
recycleBufferRingBuffer
public void recycleBufferRingBuffer(int bufferId) Recycle a buffer back to the buffer ring after processing.- Parameters:
bufferId- buffer ID to recycle
-
submitMultishotRecvWithBufferRing
public boolean submitMultishotRecvWithBufferRing(long token) Submit a multishot receive with buffer ring selection.This is the most efficient receive pattern - the kernel automatically selects buffers from the ring and continues receiving without resubmission.
- Parameters:
token- user token for completion tracking- Returns:
- true if submission was successful
-
submitLinkedEcho
public boolean submitLinkedEcho(MemorySegment recvBuffer, int recvLen, long recvToken, long sendToken) Submit a linked recv+send echo pattern.This creates two linked SQEs:
- recv - receive data into buffer
- send - send the same data back (linked, executes after recv)
The send only executes after recv completes successfully. If recv fails, the linked send is cancelled automatically.
- Parameters:
recvBuffer- buffer for receiving datarecvLen- max bytes to receiverecvToken- token for recv completionsendToken- token for send completion- Returns:
- true if both SQEs were submitted successfully
-
submitLinkedEchoSkipRecvCqe
Submit linked recv+send with skip on success for efficient echo.Like submitLinkedEcho, but uses CQE_SKIP_SUCCESS on the recv to reduce CQE overhead. Only the send completion is generated on success.
- Parameters:
recvBuffer- buffer for receiving/sending datarecvLen- max bytes to receivesendToken- token for completion (only send generates CQE)- Returns:
- true if submission was successful
-
submitLinkedRequestResponse
public boolean submitLinkedRequestResponse(MemorySegment sendBuffer, int sendLen, MemorySegment recvBuffer, int recvLen, long sendToken, long recvToken) Submit linked send+recv for request-response pattern.Useful for RPC clients that send a request and expect a response. The recv only starts after the send completes.
- Parameters:
sendBuffer- buffer containing request datasendLen- bytes to sendrecvBuffer- buffer for responserecvLen- max response bytessendToken- token for send completionrecvToken- token for recv completion- Returns:
- true if submission was successful
-
isUsingFixedFile
public boolean isUsingFixedFile()Check if fixed file optimization is active for this backend. Fixed files eliminate the fd lookup overhead in each operation.- Returns:
- true if using fixed file index
-
getFixedFileIndex
public int getFixedFileIndex()Get the fixed file index (if using fixed files).- Returns:
- fixed file index or -1 if not using fixed files
-
submitBatchRecv
Submit a batch of receive operations for maximum throughput.This submits multiple recv SQEs in a single call, which is more efficient than submitting them one at a time. Useful for high-throughput scenarios where you want to have multiple outstanding receives.
- Parameters:
buffers- array of receive bufferslengths- array of buffer lengthstokens- array of user tokens for trackingcount- number of operations to submit- Returns:
- number of operations successfully queued
-
submitBatchSend
Submit a batch of send operations for maximum throughput.- Parameters:
buffers- array of send bufferslengths- array of data lengthstokens- array of user tokens for trackingcount- number of operations to submit- Returns:
- number of operations successfully queued
-
submitBatchRecvRegistered
public int submitBatchRecvRegistered(short[] bufferIndices, int[] lengths, long[] tokens, int count) Submit a batch of recv operations using registered buffers.Uses pre-registered buffer indices for zero-copy receive. More efficient than regular recv as it avoids buffer address translation.
- Parameters:
bufferIndices- indices of registered bufferslengths- max lengths for each recvtokens- user tokens for trackingcount- number of operations- Returns:
- number of operations successfully queued
-
forceSubmit
public int forceSubmit()Force a single io_uring_submit syscall after queuing operations.This is useful when you've queued multiple operations and want to submit them all at once for maximum batching efficiency.
- Returns:
- number of SQEs submitted to kernel
-
connect
Description copied from interface:TransportBackendConnects to a remote endpoint asynchronously.- Specified by:
connectin interfaceTransportBackend- Parameters:
remoteAddress- the remote address to connect totoken- the token identifying the operation
-
bind
Description copied from interface:TransportBackendBinds to a local address for accepting connections (server mode).- Specified by:
bindin interfaceTransportBackend- Parameters:
localAddress- the local address to bind to
-
accept
public void accept(long token) Description copied from interface:TransportBackendAccepts an incoming connection (server mode).- Specified by:
acceptin interfaceTransportBackend- Parameters:
token- the token identifying the operation
-
send
Description copied from interface:TransportBackendSends data using a registered buffer (zero-copy).For io_uring, this uses
io_uring_prep_send_fixed()with the buffer index. For other backends, this may fall back to a regular send.- Specified by:
sendin interfaceTransportBackend- Parameters:
buffer- the registered buffer containing data to sendtoken- the token identifying the operation
-
send
Description copied from interface:TransportBackendSends data using a raw memory segment.This is a fallback for non-registered buffers. Less efficient than
TransportBackend.send(RegisteredBuffer, long)but more flexible.- Specified by:
sendin interfaceTransportBackend- Parameters:
data- the memory segment containing data to sendlength- the number of bytes to sendtoken- the token identifying the operation
-
sendZeroCopy
Send data using zero-copy mechanism (SEND_ZC).Zero-copy send avoids copying data from user-space to kernel-space, providing significant performance improvements for large buffers (typically 2KB+).
IMPORTANT:
- You will receive TWO completions: one for send completion, one for notification
- The buffer must NOT be modified until the notification completion is received
- Check
LibUring.isZeroCopyNotification(MemorySegment)in your completion handler
- Parameters:
buffer- the registered buffer to sendtoken- user data token for completion tracking
-
sendZeroCopy
Send data using zero-copy mechanism (SEND_ZC).- Parameters:
data- buffer containing data to sendlength- number of bytes to sendtoken- user data token for completion tracking
-
receive
Description copied from interface:TransportBackendReceives data into a registered buffer (zero-copy).For io_uring, this uses
io_uring_prep_recv_fixed()with the buffer index.- Specified by:
receivein interfaceTransportBackend- Parameters:
buffer- the registered buffer to receive intotoken- the token identifying the operation
-
receive
Description copied from interface:TransportBackendReceives data into a raw memory segment.- Specified by:
receivein interfaceTransportBackend- Parameters:
data- the memory segment to receive intomaxLength- the maximum number of bytes to receivetoken- the token identifying the operation
-
receiveMultishot
Start a persistent multi-shot receive operation.Multi-shot receive keeps the SQE active and generates multiple CQEs until the operation is cancelled or an error occurs. This is ideal for persistent receive loops as it eliminates the need to resubmit after each receive.
IMPORTANT:
- Check CQE flags for IORING_CQE_F_MORE - if set, more completions are coming
- If IORING_CQE_F_MORE is NOT set, the operation has terminated and must be resubmitted
- Use
LibUring.hasMoreCompletions(MemorySegment)to check in completion handler - Requires Linux 5.16+
- Parameters:
buffer- the registered buffer for receiving datatoken- user data token for completion tracking
-
receiveMultishot
Start a persistent multi-shot receive operation.- Parameters:
data- buffer for receiving datamaxLength- maximum bytes to receive per completiontoken- user data token for completion tracking
-
submitBatch
public int submitBatch()Description copied from interface:TransportBackendSubmits all queued operations in a batch.For io_uring, this calls
io_uring_submit()to submit all queued operations with a single syscall. This is the key to achieving 100x syscall reduction.For NIO, this is typically a no-op since NIO doesn't support batching.
- Specified by:
submitBatchin interfaceTransportBackend- Returns:
- the number of operations submitted
-
poll
Description copied from interface:TransportBackendPolls for completions without blocking.For io_uring, this calls
io_uring_peek_cqe()to check for completed operations. For NIO, this may use a Selector with 0 timeout.- Specified by:
pollin interfaceTransportBackend- Parameters:
handler- the handler to call for each completion- Returns:
- the number of operations completed
-
waitForCompletion
Description copied from interface:TransportBackendWaits for at least one operation to complete.This blocks until at least one queued operation completes.
- Specified by:
waitForCompletionin interfaceTransportBackend- Parameters:
timeoutMillis- the maximum time to wait in milliseconds (0 = forever)handler- the handler to call for each completion- Returns:
- the number of operations completed
-
supportsRegisteredBuffers
public boolean supportsRegisteredBuffers()Description copied from interface:TransportBackendReturns whether this backend supports registered buffers.- Specified by:
supportsRegisteredBuffersin interfaceTransportBackend- Returns:
- true if registered buffers are supported
-
supportsBatchSubmission
public boolean supportsBatchSubmission()Description copied from interface:TransportBackendReturns whether this backend supports batch submission.- Specified by:
supportsBatchSubmissionin interfaceTransportBackend- Returns:
- true if batch submission is supported
-
supportsTLS
public boolean supportsTLS()Description copied from interface:TransportBackendReturns whether this backend supports TLS/SSL.- Specified by:
supportsTLSin interfaceTransportBackend- Returns:
- true if TLS is supported
-
getBackendType
Description copied from interface:TransportBackendReturns the backend type identifier.- Specified by:
getBackendTypein interfaceTransportBackend- Returns:
- the backend type (e.g., "io_uring", "nio", "xdp")
-
getStats
Description copied from interface:TransportBackendReturns statistics about backend operations.- Specified by:
getStatsin interfaceTransportBackend- Returns:
- backend statistics including operation counts, errors, etc.
-
close
public void close()Description copied from interface:TransportBackendCloses the backend and releases all resources.- Specified by:
closein interfaceAutoCloseable- Specified by:
closein interfaceTransportBackend
-
createFromAccepted
Description copied from interface:TransportBackendCreates a new backend instance from an accepted connection handle.For io_uring, the handle is the file descriptor. For NIO, the handle is an index or reference to the accepted channel.
- Specified by:
createFromAcceptedin interfaceTransportBackend- Parameters:
handle- the handle returned by the accept completion- Returns:
- a new TransportBackend for the accepted connection
-