View Javadoc
1   /*
2    * Copyright 2012 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    *   http://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 org.jboss.netty.channel;
17  
18  import java.util.ArrayList;
19  import java.util.List;
20  
21  /**
22   * The {@link ReceiveBufferSizePredictor} that automatically increases and
23   * decreases the predicted buffer size on feed back.
24   * <p>
25   * It gradually increases the expected number of readable bytes if the previous
26   * read fully filled the allocated buffer.  It gradually decreases the expected
27   * number of readable bytes if the read operation was not able to fill a certain
28   * amount of the allocated buffer two times consecutively.  Otherwise, it keeps
29   * returning the same prediction.
30   */
31  public class AdaptiveReceiveBufferSizePredictor implements
32          ReceiveBufferSizePredictor {
33  
34      static final int DEFAULT_MINIMUM = 64;
35      static final int DEFAULT_INITIAL = 1024;
36      static final int DEFAULT_MAXIMUM = 65536;
37  
38      private static final int INDEX_INCREMENT = 4;
39      private static final int INDEX_DECREMENT = 1;
40  
41      private static final int[] SIZE_TABLE;
42  
43      static {
44          List<Integer> sizeTable = new ArrayList<Integer>();
45          for (int i = 1; i <= 8; i ++) {
46              sizeTable.add(i);
47          }
48  
49          for (int i = 4; i < 32; i ++) {
50              long v = 1L << i;
51              long inc = v >>> 4;
52              v -= inc << 3;
53  
54              for (int j = 0; j < 8; j ++) {
55                  v += inc;
56                  if (v > Integer.MAX_VALUE) {
57                      sizeTable.add(Integer.MAX_VALUE);
58                  } else {
59                      sizeTable.add((int) v);
60                  }
61              }
62          }
63  
64          SIZE_TABLE = new int[sizeTable.size()];
65          for (int i = 0; i < SIZE_TABLE.length; i ++) {
66              SIZE_TABLE[i] = sizeTable.get(i);
67          }
68      }
69  
70      private static int getSizeTableIndex(final int size) {
71          if (size <= 16) {
72              return size - 1;
73          }
74  
75          int bits = 0;
76          int v = size;
77          do {
78              v >>>= 1;
79              bits ++;
80          } while (v != 0);
81  
82          final int baseIdx = bits << 3;
83          final int startIdx = baseIdx - 18;
84          final int endIdx = baseIdx - 25;
85  
86          for (int i = startIdx; i >= endIdx; i --) {
87              if (size >= SIZE_TABLE[i]) {
88                  return i;
89              }
90          }
91  
92          throw new Error("shouldn't reach here; please file a bug report.");
93      }
94  
95      private final int minIndex;
96      private final int maxIndex;
97      private int index;
98      private int nextReceiveBufferSize;
99      private boolean decreaseNow;
100 
101     /**
102      * Creates a new predictor with the default parameters.  With the default
103      * parameters, the expected buffer size starts from {@code 1024}, does not
104      * go down below {@code 64}, and does not go up above {@code 65536}.
105      */
106     public AdaptiveReceiveBufferSizePredictor() {
107         this(DEFAULT_MINIMUM, DEFAULT_INITIAL, DEFAULT_MAXIMUM);
108     }
109 
110     /**
111      * Creates a new predictor with the specified parameters.
112      *
113      * @param minimum  the inclusive lower bound of the expected buffer size
114      * @param initial  the initial buffer size when no feed back was received
115      * @param maximum  the inclusive upper bound of the expected buffer size
116      */
117     public AdaptiveReceiveBufferSizePredictor(int minimum, int initial, int maximum) {
118         if (minimum <= 0) {
119             throw new IllegalArgumentException("minimum: " + minimum);
120         }
121         if (initial < minimum) {
122             throw new IllegalArgumentException("initial: " + initial);
123         }
124         if (maximum < initial) {
125             throw new IllegalArgumentException("maximum: " + maximum);
126         }
127 
128         int minIndex = getSizeTableIndex(minimum);
129         if (SIZE_TABLE[minIndex] < minimum) {
130             this.minIndex = minIndex + 1;
131         } else {
132             this.minIndex = minIndex;
133         }
134 
135         int maxIndex = getSizeTableIndex(maximum);
136         if (SIZE_TABLE[maxIndex] > maximum) {
137             this.maxIndex = maxIndex - 1;
138         } else {
139             this.maxIndex = maxIndex;
140         }
141 
142         index = getSizeTableIndex(initial);
143         nextReceiveBufferSize = SIZE_TABLE[index];
144     }
145 
146     public int nextReceiveBufferSize() {
147         return nextReceiveBufferSize;
148     }
149 
150     public void previousReceiveBufferSize(int previousReceiveBufferSize) {
151         if (previousReceiveBufferSize <= SIZE_TABLE[Math.max(0, index - INDEX_DECREMENT - 1)]) {
152             if (decreaseNow) {
153                 index = Math.max(index - INDEX_DECREMENT, minIndex);
154                 nextReceiveBufferSize = SIZE_TABLE[index];
155                 decreaseNow = false;
156             } else {
157                 decreaseNow = true;
158             }
159         } else if (previousReceiveBufferSize >= nextReceiveBufferSize) {
160             index = Math.min(index + INDEX_INCREMENT, maxIndex);
161             nextReceiveBufferSize = SIZE_TABLE[index];
162             decreaseNow = false;
163         }
164     }
165 }