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 }