1 /*
2 * Copyright 2025 The Netty Project
3 *
4 * The Netty Project licenses this file to you under the Apache License,
5 * version 2.0 (the "License"); you may not use this file except in compliance
6 * with the License. You may obtain a copy of the License at:
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations
14 * under the License.
15 */
16 package io.netty.channel.uring;
17
18 import io.netty.buffer.ByteBufAllocator;
19 import io.netty.util.internal.AdaptiveCalculator;
20
21 /**
22 * {@link IoUringBufferRingAllocator} implementation which uses an adaptive strategy to allocate buffers, which
23 * will decrease / increase the buffer size depending on if the allocated buffers were completely used or not before.
24 */
25 public final class IoUringAdaptiveBufferRingAllocator extends AbstractIoUringBufferRingAllocator {
26
27 public static final int DEFAULT_MINIMUM = 1024;
28 public static final int DEFAULT_INITIAL = 4096;
29 public static final int DEFAULT_MAXIMUM = 65536;
30
31 private final AdaptiveCalculator calculator;
32
33 public IoUringAdaptiveBufferRingAllocator() {
34 this(ByteBufAllocator.DEFAULT);
35 }
36
37 /**
38 * Creates new instance.
39 *
40 * @param allocator the {@link ByteBufAllocator} to use.
41 */
42 public IoUringAdaptiveBufferRingAllocator(ByteBufAllocator allocator) {
43 this(allocator, DEFAULT_MINIMUM, DEFAULT_INITIAL, DEFAULT_MAXIMUM);
44 }
45
46 /**
47 * Creates new instance.
48 *
49 * @param allocator the {@link ByteBufAllocator} to use for the allocations
50 * @param minimum the inclusive lower bound of the expected buffer size
51 * @param initial the initial buffer size when no feed back was received
52 * @param maximum the inclusive upper bound of the expected buffer size
53 */
54 public IoUringAdaptiveBufferRingAllocator(ByteBufAllocator allocator, int minimum, int initial, int maximum) {
55 this(allocator, minimum, initial, maximum, false);
56 }
57
58 /**
59 * Creates new instance.
60 *
61 * @param allocator the {@link ByteBufAllocator} to use for the allocations
62 * @param minimum the inclusive lower bound of the expected buffer size
63 * @param initial the initial buffer size when no feed back was received
64 * @param maximum the inclusive upper bound of the expected buffer size
65 * @param largeAllocation {@code true} if we should do a large allocation for the whole buffer ring
66 * and then slice out the buffers or {@code false} if we should do one allocation
67 * per buffer.
68 */
69 public IoUringAdaptiveBufferRingAllocator(
70 ByteBufAllocator allocator, int minimum, int initial, int maximum, boolean largeAllocation) {
71 super(allocator, largeAllocation);
72 this.calculator = new AdaptiveCalculator(minimum, initial, maximum);
73 }
74
75 @Override
76 protected int nextBufferSize() {
77 return calculator.nextSize();
78 }
79
80 @Override
81 public void lastBytesRead(int attempted, int actual) {
82 // If we read as much as we asked for we should check if we need to ramp up the size of our next guess.
83 // This helps adjust more quickly when large amounts of data is pending and can avoid going back to
84 // the selector to check for more data. Going back to the selector can add significant latency for large
85 // data transfers.
86 if (attempted == actual) {
87 calculator.record(actual);
88 }
89 }
90 }