1 /* 2 * Copyright (c) 2023, 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 24 package org.openjdk.bench.java.lang.foreign; 25 26 import org.openjdk.jmh.annotations.Benchmark; 27 import org.openjdk.jmh.annotations.BenchmarkMode; 28 import org.openjdk.jmh.annotations.Fork; 29 import org.openjdk.jmh.annotations.Measurement; 30 import org.openjdk.jmh.annotations.Mode; 31 import org.openjdk.jmh.annotations.OutputTimeUnit; 32 import org.openjdk.jmh.annotations.Param; 33 import org.openjdk.jmh.annotations.Setup; 34 import org.openjdk.jmh.annotations.State; 35 import org.openjdk.jmh.annotations.TearDown; 36 import org.openjdk.jmh.annotations.Warmup; 37 38 import java.lang.foreign.Arena; 39 import java.lang.foreign.MemorySegment; 40 import java.lang.foreign.MemorySegment.Scope; 41 import java.lang.foreign.SegmentAllocator; 42 import java.lang.foreign.ValueLayout; 43 import java.util.Random; 44 import java.util.concurrent.TimeUnit; 45 46 @BenchmarkMode(Mode.AverageTime) 47 @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) 48 @Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) 49 @State(org.openjdk.jmh.annotations.Scope.Thread) 50 @OutputTimeUnit(TimeUnit.NANOSECONDS) 51 @Fork(value = 3, jvmArgsAppend = { "--enable-native-access=ALL-UNNAMED" }) 52 public class AllocFromTest extends CLayouts { 53 54 Arena arena = Arena.ofConfined(); 55 56 SlicingPool pool = new SlicingPool(); 57 58 @Param({"5", "20", "100", "500", "1000"}) 59 public int size; 60 public byte[] arr; 61 62 @Setup 63 public void setup() { 64 arr = new byte[size]; 65 Random random = new Random(0); 66 random.nextBytes(arr); 67 } 68 69 @Benchmark 70 public MemorySegment alloc_confined() { 71 Arena arena = Arena.ofConfined(); 72 MemorySegment segment = arena.allocateFrom(ValueLayout.JAVA_BYTE, arr); 73 arena.close(); 74 return segment; 75 } 76 77 @Benchmark 78 public MemorySegment alloc_malloc_arena() { 79 MallocArena arena = new MallocArena(); 80 MemorySegment segment = arena.allocateFrom(ValueLayout.JAVA_BYTE, arr); 81 arena.close(); 82 return segment; 83 } 84 85 @Benchmark 86 public MemorySegment alloc_unsafe_arena() { 87 UnsafeArena arena = new UnsafeArena(); 88 MemorySegment segment = arena.allocateFrom(ValueLayout.JAVA_BYTE, arr); 89 arena.close(); 90 return segment; 91 } 92 93 @Benchmark 94 public MemorySegment alloc_pool_arena() { 95 Arena arena = pool.acquire(); 96 MemorySegment segment = arena.allocateFrom(ValueLayout.JAVA_BYTE, arr); 97 arena.close(); 98 return segment; 99 } 100 101 static class SlicingPool { 102 final MemorySegment pool = Arena.ofAuto().allocate(1024); 103 boolean isAcquired = false; 104 105 public Arena acquire() { 106 if (isAcquired) { 107 throw new IllegalStateException("An allocator is already in use"); 108 } 109 isAcquired = true; 110 return new SlicingPoolAllocator(); 111 } 112 113 class SlicingPoolAllocator implements Arena { 114 115 final Arena arena = Arena.ofConfined(); 116 final SegmentAllocator slicing = SegmentAllocator.slicingAllocator(pool); 117 118 public MemorySegment allocate(long byteSize, long byteAlignment) { 119 return slicing.allocate(byteSize, byteAlignment) 120 .reinterpret(arena, null); 121 } 122 123 @Override 124 public Scope scope() { 125 return arena.scope(); 126 } 127 128 public void close() { 129 isAcquired = false; 130 arena.close(); 131 } 132 } 133 } 134 135 public static class MallocArena implements Arena { 136 137 final Arena arena = Arena.ofConfined(); 138 139 @Override 140 public Scope scope() { 141 return arena.scope(); 142 } 143 144 @Override 145 public void close() { 146 arena.close(); 147 } 148 149 @Override 150 public MemorySegment allocate(long byteSize, long byteAlignment) { 151 return CLayouts.allocateMemory(byteSize) 152 .reinterpret(byteSize, arena, CLayouts::freeMemory); 153 } 154 } 155 156 public static class UnsafeArena implements Arena { 157 158 final Arena arena = Arena.ofConfined(); 159 160 @Override 161 public Scope scope() { 162 return arena.scope(); 163 } 164 165 @Override 166 public void close() { 167 arena.close(); 168 } 169 170 @Override 171 public MemorySegment allocate(long byteSize, long byteAlignment) { 172 return MemorySegment.ofAddress(Utils.unsafe.allocateMemory(byteSize)) 173 .reinterpret(byteSize, arena, ms -> Utils.unsafe.freeMemory(ms.address())); 174 } 175 } 176 }