1 /*
  2  * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 package org.openjdk.bench.java.lang.foreign;
 24 
 25 import java.lang.foreign.Arena;
 26 import java.lang.foreign.MemorySegment;
 27 
 28 import org.openjdk.jmh.annotations.Benchmark;
 29 import org.openjdk.jmh.annotations.BenchmarkMode;
 30 import org.openjdk.jmh.annotations.Fork;
 31 import org.openjdk.jmh.annotations.Measurement;
 32 import org.openjdk.jmh.annotations.Mode;
 33 import org.openjdk.jmh.annotations.OutputTimeUnit;
 34 import org.openjdk.jmh.annotations.Setup;
 35 import org.openjdk.jmh.annotations.State;
 36 import org.openjdk.jmh.annotations.TearDown;
 37 import org.openjdk.jmh.annotations.Warmup;
 38 
 39 import java.nio.ByteBuffer;
 40 import java.nio.ByteOrder;
 41 import java.nio.IntBuffer;
 42 import java.util.Iterator;
 43 import java.util.concurrent.TimeUnit;
 44 
 45 import static java.lang.foreign.ValueLayout.JAVA_INT;
 46 
 47 @BenchmarkMode(Mode.AverageTime)
 48 @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
 49 @Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
 50 @State(org.openjdk.jmh.annotations.Scope.Thread)
 51 @OutputTimeUnit(TimeUnit.MILLISECONDS)
 52 @Fork(value = 3, jvmArgsAppend = { "--enable-preview", "--enable-native-access=ALL-UNNAMED" })
 53 
 54 public class LoopOverSlice {
 55 
 56     static final int ELEM_SIZE = 1_000_000;
 57     static final int CARRIER_SIZE = (int)JAVA_INT.byteSize();
 58     static final int ALLOC_SIZE = ELEM_SIZE * CARRIER_SIZE;
 59 
 60     Arena arena;
 61     MemorySegment nativeSegment, heapSegment;
 62     IntBuffer nativeBuffer, heapBuffer;
 63 
 64     @Setup
 65     public void setup() {
 66         arena = Arena.ofConfined();
 67         nativeSegment = arena.allocate(ALLOC_SIZE, 1);
 68         heapSegment = MemorySegment.ofArray(new int[ELEM_SIZE]);
 69         nativeBuffer = ByteBuffer.allocateDirect(ALLOC_SIZE).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer();
 70         heapBuffer = IntBuffer.wrap(new int[ELEM_SIZE]);
 71     }
 72 
 73     @TearDown
 74     public void tearDown() {
 75         arena.close();
 76     }
 77 
 78     @Benchmark
 79     public void native_segment_slice_loop() {
 80         new NativeSegmentWrapper(nativeSegment).forEach(NativeSegmentWrapper.Element::get);
 81     }
 82 
 83     @Benchmark
 84     public void native_buffer_slice_loop() {
 85         new NativeBufferWrapper(nativeBuffer).forEach(NativeBufferWrapper.Element::get);
 86     }
 87 
 88     @Benchmark
 89     public void heap_segment_slice_loop() {
 90         new HeapSegmentWrapper(heapSegment).forEach(HeapSegmentWrapper.Element::get);
 91     }
 92 
 93     @Benchmark
 94     public void heap_buffer_slice_loop() {
 95         new HeapBufferWrapper(heapBuffer).forEach(HeapBufferWrapper.Element::get);
 96     }
 97 
 98     class HeapSegmentWrapper implements Iterable<HeapSegmentWrapper.Element> {
 99         final MemorySegment segment;
100 
101         public HeapSegmentWrapper(MemorySegment segment) {
102             this.segment = segment;
103         }
104 
105         @Override
106         public Iterator<Element> iterator() {
107             return new Iterator<Element>() {
108 
109                 MemorySegment current = segment;
110 
111                 @Override
112                 public boolean hasNext() {
113                     return current.byteSize() > 4;
114                 }
115 
116                 @Override
117                 public Element next() {
118                     Element element = new Element(current);
119                     current = current.asSlice(4);
120                     return element;
121                 }
122             };
123         }
124 
125         static class Element {
126             final MemorySegment segment;
127 
128             public Element(MemorySegment segment) {
129                 this.segment = segment;
130             }
131 
132             int get() {
133                 return segment.getAtIndex(JAVA_INT, 0);
134             }
135         }
136     }
137 
138     class NativeSegmentWrapper implements Iterable<NativeSegmentWrapper.Element> {
139         final MemorySegment segment;
140 
141         public NativeSegmentWrapper(MemorySegment segment) {
142             this.segment = segment;
143         }
144 
145         @Override
146         public Iterator<Element> iterator() {
147             return new Iterator<Element>() {
148 
149                 MemorySegment current = segment;
150 
151                 @Override
152                 public boolean hasNext() {
153                     return current.byteSize() > 4;
154                 }
155 
156                 @Override
157                 public Element next() {
158                     Element element = new Element(current);
159                     current = current.asSlice(4);
160                     return element;
161                 }
162             };
163         }
164 
165         static class Element {
166             final MemorySegment segment;
167 
168             public Element(MemorySegment segment) {
169                 this.segment = segment;
170             }
171 
172             int get() {
173                 return segment.getAtIndex(JAVA_INT, 0);
174             }
175         }
176     }
177 
178     class NativeBufferWrapper implements Iterable<NativeBufferWrapper.Element> {
179         final IntBuffer buffer;
180 
181         public NativeBufferWrapper(IntBuffer buffer) {
182             this.buffer = buffer;
183         }
184 
185         @Override
186         public Iterator<Element> iterator() {
187             return new Iterator<Element>() {
188 
189                 IntBuffer current = buffer;
190 
191                 @Override
192                 public boolean hasNext() {
193                     return current.position() < current.limit();
194                 }
195 
196                 @Override
197                 public Element next() {
198                     Element element = new Element(current);
199                     int lim = current.limit();
200                     current = current.slice(1, lim - 1);
201                     return element;
202                 }
203             };
204         }
205 
206         static class Element {
207             final IntBuffer buffer;
208 
209             public Element(IntBuffer segment) {
210                 this.buffer = segment;
211             }
212 
213             int get() {
214                 return buffer.get( 0);
215             }
216         }
217     }
218 
219     class HeapBufferWrapper implements Iterable<HeapBufferWrapper.Element> {
220         final IntBuffer buffer;
221 
222         public HeapBufferWrapper(IntBuffer buffer) {
223             this.buffer = buffer;
224         }
225 
226         @Override
227         public Iterator<Element> iterator() {
228             return new Iterator<Element>() {
229 
230                 IntBuffer current = buffer;
231 
232                 @Override
233                 public boolean hasNext() {
234                     return current.position() < current.limit();
235                 }
236 
237                 @Override
238                 public Element next() {
239                     Element element = new Element(current);
240                     int lim = current.limit();
241                     current = current.slice(1, lim - 1);
242                     return element;
243                 }
244             };
245         }
246 
247         static class Element {
248             final IntBuffer buffer;
249 
250             public Element(IntBuffer segment) {
251                 this.buffer = segment;
252             }
253 
254             int get() {
255                 return buffer.get( 0);
256             }
257         }
258     }
259 }