View Javadoc
1   /*
2    * Copyright 2018 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.buffer;
17  
18  import io.netty.microbench.util.AbstractMicrobenchmark;
19  import io.netty.util.ByteProcessor;
20  
21  import org.openjdk.jmh.annotations.Benchmark;
22  import org.openjdk.jmh.annotations.Measurement;
23  import org.openjdk.jmh.annotations.Param;
24  import org.openjdk.jmh.annotations.Setup;
25  import org.openjdk.jmh.annotations.TearDown;
26  import org.openjdk.jmh.annotations.Warmup;
27  
28  import static io.netty.buffer.Unpooled.EMPTY_BUFFER;
29  import static io.netty.buffer.Unpooled.directBuffer;
30  import static io.netty.buffer.Unpooled.wrappedBuffer;
31  
32  import java.util.ArrayList;
33  import java.util.List;
34  import java.util.concurrent.TimeUnit;
35  
36  @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
37  @Measurement(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS)
38  public class CompositeByteBufSequentialBenchmark extends AbstractMicrobenchmark {
39  
40      public enum ByteBufType {
41          SMALL_CHUNKS {
42              @Override
43              ByteBuf newBuffer(int length) {
44                  return newBufferSmallChunks(length);
45              }
46          },
47          LARGE_CHUNKS {
48              @Override
49              ByteBuf newBuffer(int length) {
50                  return newBufferLargeChunks(length);
51              }
52          },
53          SMALL_CHUNKS_DIRECT {
54              @Override
55              ByteBuf newBuffer(int length) {
56                  return newBufferSmallChunksDirect(length);
57              }
58          },
59          LARGE_CHUNKS_DIRECT {
60              @Override
61              ByteBuf newBuffer(int length) {
62                  return newBufferLargeChunksDirect(length);
63              }
64          };
65          abstract ByteBuf newBuffer(int length);
66      }
67  
68      @Param({
69              "8",
70              "64",
71              "1024",
72              "10240",
73              "102400",
74              "1024000",
75      })
76      public int size;
77  
78      @Param
79      public ByteBufType bufferType;
80  
81      private ByteBuf buffer;
82  
83      @Setup
84      public void setup() {
85          buffer = bufferType.newBuffer(size);
86          // Pre-fill so readByte benchmarks have readable content
87          buffer.writerIndex(buffer.capacity());
88      }
89  
90      @TearDown
91      public void teardown() {
92          buffer.release();
93      }
94  
95      private static final ByteProcessor TEST_PROCESSOR = new ByteProcessor() {
96          @Override
97          public boolean process(byte value) throws Exception {
98              return value != 'b'; // true for non-'b' bytes, so we scan all bytes
99          }
100     };
101 
102     @Benchmark
103     public int forEachByte() {
104         buffer.setIndex(0, buffer.capacity());
105         buffer.forEachByte(TEST_PROCESSOR);
106         return buffer.forEachByteDesc(TEST_PROCESSOR);
107     }
108 
109     @Benchmark
110     public int sequentialReadBytes() {
111         buffer.readerIndex(0);
112         int result = 0;
113         for (int i = 0, l = buffer.readableBytes(); i < l; i++) {
114             result += buffer.readByte();
115         }
116         return result;
117     }
118 
119     @Benchmark
120     public int sequentialGetBytes() {
121         int result = 0;
122         for (int i = 0, l = buffer.capacity(); i < l; i++) {
123             result += buffer.getByte(i);
124         }
125         return result;
126     }
127 
128     @Benchmark
129     public int sequentialWriteAndRead() {
130         buffer.clear();
131         for (int i = 0, l = buffer.writableBytes(); i < l; i++) {
132             buffer.writeByte('a');
133         }
134         for (int i = 0, l = buffer.readableBytes(); i < l; i++) {
135             if (buffer.readByte() == 'b') {
136                 return -1;
137             }
138         }
139         return 1;
140     }
141 
142     private static ByteBuf newBufferSmallChunks(int length) {
143 
144         List<ByteBuf> buffers = new ArrayList<ByteBuf>(((length + 1) / 45) * 19);
145         for (int i = 0; i < length + 45; i += 45) {
146             for (int j = 1; j <= 9; j++) {
147                 buffers.add(EMPTY_BUFFER);
148                 buffers.add(wrappedBuffer(new byte[j]));
149             }
150             buffers.add(EMPTY_BUFFER);
151         }
152 
153         ByteBuf buffer = wrappedBuffer(Integer.MAX_VALUE, buffers.toArray(new ByteBuf[0]));
154 
155         // Truncate to the requested capacity.
156         return buffer.capacity(length).writerIndex(0);
157     }
158 
159     private static ByteBuf newBufferLargeChunks(int length) {
160 
161         List<ByteBuf> buffers = new ArrayList<ByteBuf>((length + 1) / 512);
162         for (int i = 0; i < length + 1536; i += 1536) {
163             buffers.add(wrappedBuffer(new byte[512]));
164             buffers.add(EMPTY_BUFFER);
165             buffers.add(wrappedBuffer(new byte[1024]));
166         }
167 
168         ByteBuf buffer = wrappedBuffer(Integer.MAX_VALUE, buffers.toArray(new ByteBuf[0]));
169 
170         // Truncate to the requested capacity.
171         return buffer.capacity(length).writerIndex(0);
172     }
173 
174     private static ByteBuf newDirectChunk(int size) {
175         ByteBuf buf = directBuffer(size);
176         buf.writerIndex(size);
177         return buf;
178     }
179 
180     private static ByteBuf newBufferSmallChunksDirect(int length) {
181 
182         List<ByteBuf> buffers = new ArrayList<ByteBuf>(((length + 1) / 45) * 19);
183         for (int i = 0; i < length + 45; i += 45) {
184             for (int j = 1; j <= 9; j++) {
185                 buffers.add(EMPTY_BUFFER);
186                 buffers.add(newDirectChunk(j));
187             }
188             buffers.add(EMPTY_BUFFER);
189         }
190 
191         ByteBuf buffer = wrappedBuffer(Integer.MAX_VALUE, buffers.toArray(new ByteBuf[0]));
192 
193         // Truncate to the requested capacity.
194         return buffer.capacity(length).writerIndex(0);
195     }
196 
197     private static ByteBuf newBufferLargeChunksDirect(int length) {
198 
199         List<ByteBuf> buffers = new ArrayList<ByteBuf>((length + 1) / 512);
200         for (int i = 0; i < length + 1536; i += 1536) {
201             buffers.add(newDirectChunk(512));
202             buffers.add(EMPTY_BUFFER);
203             buffers.add(newDirectChunk(1024));
204         }
205 
206         ByteBuf buffer = wrappedBuffer(Integer.MAX_VALUE, buffers.toArray(new ByteBuf[0]));
207 
208         // Truncate to the requested capacity.
209         return buffer.capacity(length).writerIndex(0);
210     }
211 }