<div dir="ltr">Hi,<br><br>Thanks for the amazing work on project Panama. I've been using it to develop a Java library that provides bindings to Linux's io_uring, and the API has been a joy to work with.<br><br>During development, I encountered some performance characteristics around memory allocation that I wanted to share and get your thoughts on. While working on the library, I discovered that arena.allocate() became a performance bottleneck compared to malloc + fill(0) or calloc operations (and creating a MemorySegment out of the return value). Here are the benchmark results using JDK 24 build 31 (2025/1/9):<br><br>[Zeroed memory allocation comparison]                          (allocation size)<br>Benchmarks.memory.MemoryAllocationBench.arenaAlloc     512   thrpt    5  15147.191 ±  943.526  ops/ms<br>Benchmarks.memory.MemoryAllocationBench.arenaAlloc     1024  thrpt    5  10551.065 ± 1005.304  ops/ms<br>Benchmarks.memory.MemoryAllocationBench.arenaAlloc     4096  thrpt    5   3248.519 ±    3.210  ops/ms<br><br>Benchmarks.memory.MemoryAllocationBench.callocAlloc    512   thrpt    5  13718.615 ±  994.042  ops/ms<br>Benchmarks.memory.MemoryAllocationBench.callocAlloc    1024  thrpt    5  10104.415 ±  128.425  ops/ms<br>Benchmarks.memory.MemoryAllocationBench.callocAlloc    4096  thrpt    5   4883.802 ±  212.922  ops/ms<br><br>Benchmarks.memory.MemoryAllocationBench.mallocAlloc   512   thrpt    5  20054.526 ± 1844.846  ops/ms<br>Benchmarks.memory.MemoryAllocationBench.mallocAlloc   1024  thrpt    5  12370.954 ± 1859.726  ops/ms<br>Benchmarks.memory.MemoryAllocationBench.mallocAlloc   4096  thrpt    5   3332.564 ±  142.788  ops/ms<div><br></div><div>Arena is fast, but never faster than the other options, which made performing better than the Filechannel API difficult. For my use case, where a filling a memorySegment with zeros isn't required, the performance gap widens even further:</div><div><div><br>[malloc performance]<br>Benchmarks.memory.MemoryAllocationBench.mallocAlloc         512   thrpt    5  27901.228 ± 1300.511  ops/ms<br>Benchmarks.memory.MemoryAllocationBench.mallocAlloc         1024  thrpt    5  19637.654 ± 1548.356  ops/ms<br>Benchmarks.memory.MemoryAllocationBench.mallocAlloc         4096  thrpt    5   5780.638 ±  332.062  ops/ms<br><br>I stopped using Arena in performance-critical paths, though this meant giving up some of the convenient memory management features. I have two questions, and I'd love to learn more about:<br><br>1. Are there any performance improvements planned for memorySegment allocation using Arena?<br>2. Would implementing a custom Arena be the recommended approach for cases where allocation performance is important?<br><br>Additionally, regarding JExtract: Is there a way to configure the default access modifier for generated code? Currently, all generated classes and methods are public, which requires manual modification to make them package-private when trying to control the API surface.<br><br></div><div>Thank you for your time and feedback.<br><br>Kind regards,<br>David<br><br></div><div>For reference, here's part of the benchmark code:<br><br>@BenchmarkMode(Mode.Throughput)<br>@OutputTimeUnit(TimeUnit.MILLISECONDS)<br>@Threads(1)</div><div>@Benchmark<br>public void callocAlloc(Blackhole blackhole, ExecutionPlanMemoryVariables vars) throws Throwable {<br>     MemorySegment segment = ((MemorySegment) calloc.invokeExact(1L, (long)vars.blockSize)).reinterpret(vars.blockSize);<br><br>    segment.fill((byte) 3);<br>    blackhole.consume(segment);<br><br>    free.invokeExact(segment);<br>}</div><div><br></div><div>@BenchmarkMode(Mode.Throughput)<br>@OutputTimeUnit(TimeUnit.MILLISECONDS)<br>@Threads(1)</div><div>@Benchmark</div>public void arenaAllocWithArenaCreation(Blackhole blackhole, ExecutionPlanMemoryVariables vars) {<br>    try (Arena arena = Arena.ofConfined()) {<br>        MemorySegment allocate = arena.allocate(vars.blockSize);<br>        allocate.fill((byte) 3);<br>        blackhole.consume(allocate);<br>    }<br>}<div><br>public class LibCWrapper {<br><br>    public static final MethodHandle free;<br>    public static final MethodHandle malloc;<br>    public static final MethodHandle calloc;<br><br>    static {<br>        Linker linker = Linker.nativeLinker();<br><br>        free = linker.downcallHandle(<br>                linker.defaultLookup().find("free").orElseThrow(),<br>                FunctionDescriptor.ofVoid(ADDRESS)<br>        );<br><br>        malloc = linker.downcallHandle(<br>                linker.defaultLookup().find("malloc").orElseThrow(),<br>                FunctionDescriptor.of(ADDRESS, JAVA_LONG)<br>        );<br><br>        calloc = linker.downcallHandle(<br>                linker.defaultLookup().find("calloc").orElseThrow(),<br>                FunctionDescriptor.of(ADDRESS, JAVA_LONG,JAVA_LONG)<br>        );<br><br>    }<br><br>    private LibCWrapper() {<br>    }<br><br>}</div></div></div>