View Javadoc
1   /*
2   * Copyright 2019 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 java.nio.ByteBuffer;
19  import java.util.concurrent.TimeUnit;
20  
21  import io.netty.util.internal.CleanableDirectBuffer;
22  import org.openjdk.jmh.annotations.Benchmark;
23  import org.openjdk.jmh.annotations.BenchmarkMode;
24  import org.openjdk.jmh.annotations.Fork;
25  import org.openjdk.jmh.annotations.Measurement;
26  import org.openjdk.jmh.annotations.Mode;
27  import org.openjdk.jmh.annotations.OutputTimeUnit;
28  import org.openjdk.jmh.annotations.Param;
29  import org.openjdk.jmh.annotations.Setup;
30  import org.openjdk.jmh.annotations.TearDown;
31  import org.openjdk.jmh.annotations.Warmup;
32  
33  import io.netty.microbench.util.AbstractMicrobenchmark;
34  import io.netty.util.internal.PlatformDependent;
35  import org.openjdk.jmh.infra.Blackhole;
36  
37  @Warmup(iterations = 10, time = 1)
38  @Measurement(iterations = 10, time = 1)
39  @Fork(2)
40  @BenchmarkMode(Mode.AverageTime)
41  @OutputTimeUnit(TimeUnit.NANOSECONDS)
42  public class ByteBufAccessBenchmark extends AbstractMicrobenchmark {
43  
44      static final class NioFacade extends WrappedByteBuf {
45          private final ByteBuffer byteBuffer;
46          private final CleanableDirectBuffer cleanable;
47  
48          NioFacade(CleanableDirectBuffer buffer) {
49              super(Unpooled.EMPTY_BUFFER);
50              byteBuffer = buffer.buffer();
51              cleanable = buffer;
52          }
53          @Override
54          public ByteBuf setLong(int index, long value) {
55              byteBuffer.putLong(index, value);
56              return this;
57          }
58          @Override
59          public long getLong(int index) {
60              return byteBuffer.getLong(index);
61          }
62          @Override
63          public byte readByte() {
64              return byteBuffer.get();
65          }
66          @Override
67          public ByteBuf touch() {
68              // hack since WrappedByteBuf.readerIndex(int) is final
69              byteBuffer.position(0);
70              return this;
71          }
72          @Override
73          public boolean release() {
74              cleanable.clean();
75              return true;
76          }
77      }
78  
79      public enum ByteBufType {
80          UNSAFE {
81              @Override
82              ByteBuf newBuffer() {
83                  return new UnpooledUnsafeDirectByteBuf(
84                          UnpooledByteBufAllocator.DEFAULT, 64, 64).setIndex(0, 64);
85              }
86          },
87          DIRECT {
88              @Override
89              ByteBuf newBuffer() {
90                  return new UnpooledDirectByteBuf(
91                          UnpooledByteBufAllocator.DEFAULT, 64, 64).setIndex(0, 64);
92              }
93          },
94          UNSAFE_SLICE {
95              @Override
96              ByteBuf newBuffer() {
97                  return UNSAFE.newBuffer().slice(16, 48);
98              }
99          },
100         UNSAFE_RETAINED_SLICE {
101             @Override
102             ByteBuf newBuffer() {
103                 ByteBuf pooledBuffer = PooledByteBufAllocator.DEFAULT.directBuffer(64, 64)
104                         .setIndex(0, 64);
105                 if (!(pooledBuffer instanceof PooledUnsafeDirectByteBuf)) {
106                     throw new IllegalStateException("Expected PooledUnsafeDirectByteBuf");
107                 }
108                 try {
109                     return pooledBuffer.retainedSlice(16, 48);
110                 } finally {
111                     pooledBuffer.release();
112                 }
113             }
114         },
115         HEAP {
116             @Override
117             ByteBuf newBuffer() {
118                 if (PlatformDependent.hasUnsafe()) {
119                     return new UnpooledUnsafeHeapByteBuf(
120                             UnpooledByteBufAllocator.DEFAULT, 64, 64).setIndex(0, 64);
121                 } else {
122                     return new UnpooledHeapByteBuf(
123                             UnpooledByteBufAllocator.DEFAULT, 64, 64).setIndex(0, 64);
124                 }
125             }
126         },
127         COMPOSITE {
128             @Override
129             ByteBuf newBuffer() {
130                 return Unpooled.wrappedBuffer(UNSAFE.newBuffer(), HEAP.newBuffer());
131             }
132         },
133         NIO {
134             @Override
135             ByteBuf newBuffer() {
136                 return new NioFacade(PlatformDependent.allocateDirect(64));
137             }
138         };
139         abstract ByteBuf newBuffer();
140     }
141 
142     @Param
143     public ByteBufType bufferType;
144 
145     @Param({
146             "true",
147             "false",
148     })
149     public String checkAccessible;
150 
151     @Param({
152             "true",
153             "false",
154     })
155     public String checkBounds;
156 
157     @Param({
158             "8",
159     })
160     public int batchSize; // applies only to readBatch benchmark
161 
162     @Setup
163     public void setup() {
164         System.setProperty("io.netty.buffer.checkAccessible", checkAccessible);
165         System.setProperty("io.netty.buffer.checkBounds", checkBounds);
166         buffer = bufferType.newBuffer();
167     }
168 
169     private ByteBuf buffer;
170     private byte byteToWrite;
171     private int intToWrite;
172     private long longToWrite;
173     private short shortToWrite;
174 
175     @TearDown
176     public void tearDown() {
177         buffer.release();
178         System.clearProperty("io.netty.buffer.checkAccessible");
179         System.clearProperty("io.netty.buffer.checkBounds");
180     }
181 
182     @Benchmark
183     public long setGetLong() {
184         return buffer.setLong(0, 1).getLong(0);
185     }
186 
187     @Benchmark
188     public ByteBuf setLong() {
189         return buffer.setLong(0, 1);
190     }
191 
192     @Benchmark
193     public int readBatch() {
194         buffer.readerIndex(0);
195         int result = 0;
196         // WARNING!
197         // Please do not replace this sum loop with a BlackHole::consume loop:
198         // BlackHole::consume could prevent the JVM to perform certain optimizations
199         // forcing ByteBuf::readByte to be executed in order.
200         // The purpose of the benchmark is to mimic accesses on ByteBuf
201         // as in a real (single-threaded) case ie without (compiler) memory barriers that would
202         // disable certain optimizations or would make bounds checks (if enabled)
203         // to happen on each access.
204         for (int i = 0, size = batchSize; i < size; i++) {
205             result += buffer.readByte();
206         }
207         return result;
208     }
209 
210     @Benchmark
211     public void getByteBatch(Blackhole bh) {
212         ByteBuf buffer = this.buffer;
213         for (int i = 0, size = batchSize; i < size; i++) {
214             bh.consume(buffer.getByte(i));
215         }
216     }
217     @Benchmark
218     public void setByteBatch(Blackhole bh) {
219         ByteBuf buffer = this.buffer;
220         byte byteToWrite = this.byteToWrite;
221         buffer.resetWriterIndex();
222         for (int i = 0, size = batchSize; i < size; i++) {
223             bh.consume(buffer.setByte(i, byteToWrite));
224         }
225     }
226 
227     @Benchmark
228     public void readByteBatch(Blackhole bh) {
229         ByteBuf buffer = this.buffer;
230         buffer.readerIndex(0);
231         for (int i = 0, size = batchSize; i < size; i++) {
232             bh.consume(buffer.readByte());
233         }
234     }
235 
236     @Benchmark
237     public void setBytes(Blackhole bh) {
238         ByteBuf buffer = this.buffer;
239         byte byteToWrite = this.byteToWrite;
240         int intToWrite = this.intToWrite;
241         long longToWrite = this.longToWrite;
242         short shortToWrite = this.shortToWrite;
243         buffer.resetWriterIndex();
244         int index = buffer.writerIndex();
245         bh.consume(buffer.setByte(index, byteToWrite));
246         index += 1;
247         bh.consume(buffer.setShortLE(index, shortToWrite));
248         index += 2;
249         bh.consume(buffer.setIntLE(index, intToWrite));
250         index += 4;
251         bh.consume(buffer.setLongLE(index, longToWrite));
252     }
253 
254     @Benchmark
255     public void getBytes(Blackhole bh) {
256         ByteBuf buffer = this.buffer;
257         int readerIndex = buffer.readerIndex();
258         bh.consume(buffer.getByte(readerIndex));
259         readerIndex += 1;
260         bh.consume(buffer.getShortLE(readerIndex));
261         readerIndex += 2;
262         bh.consume(buffer.getIntLE(readerIndex));
263         readerIndex += 4;
264         bh.consume(buffer.getLongLE(readerIndex));
265     }
266 
267     @Benchmark
268     public void setBytesConstantOffset(Blackhole bh) {
269         ByteBuf buffer = this.buffer;
270         buffer.resetWriterIndex();
271         byte byteToWrite = this.byteToWrite;
272         int intToWrite = this.intToWrite;
273         long longToWrite = this.longToWrite;
274         short shortToWrite = this.shortToWrite;
275         bh.consume(buffer.setByte(0, byteToWrite));
276         bh.consume(buffer.setShortLE(1, shortToWrite));
277         bh.consume(buffer.setIntLE(3, intToWrite));
278         bh.consume(buffer.setLongLE(7, longToWrite));
279     }
280 
281     @Benchmark
282     public void getBytesConstantOffset(Blackhole bh) {
283         ByteBuf buffer = this.buffer;
284         bh.consume(buffer.getByte(0));
285         bh.consume(buffer.getShortLE(1));
286         bh.consume(buffer.getIntLE(3));
287         bh.consume(buffer.getLongLE(7));
288     }
289 
290     @Benchmark
291     public void readBytes(Blackhole bh) {
292         buffer.readerIndex(0);
293         ByteBuf buffer = this.buffer;
294         bh.consume(buffer.readByte());
295         bh.consume(buffer.readShortLE());
296         bh.consume(buffer.readIntLE());
297         bh.consume(buffer.readLongLE());
298     }
299 }