<div dir="auto"><div dir="ltr">Hello,<div><br></div><div>I'm trying to implement something similar to the BufferManager described in 2.1 in [1] for UmbraDB:</div><div><br></div><div>So I wanted to create the first buffer size class with fixed sized pages as follows:<br><br><div style="background-color:rgb(30,31,34);color:rgb(188,190,196)"><pre style="font-family:"JetBrains Mono",monospace;font-size:9.8pt"><span style="color:rgb(122,126,133)">// Allocate the memory<br></span>MemorySegment reservedMemory = bufferManager.allocateMemory(totalMemorySize);<br><br><span style="color:rgb(122,126,133)">// Partition into page-sized chunks<br></span>List<MemorySegment> pages = <span style="color:rgb(207,142,109)">new </span>ArrayList<>();<br><br><span style="color:rgb(207,142,109)">for </span>(<span style="color:rgb(207,142,109)">long </span>offset = <span style="color:rgb(42,172,184)">0</span>; offset + pageSize < totalMemorySize; offset += pageSize) {<br>  MemorySegment pageSegment = reservedMemory.asSlice(offset, pageSize);<br>  pages.add(pageSegment);<br>}</pre></div></div><div>Using `mmap` to create the virtual address mapping for the big chunk allocation like this: <br></div><div><div style="background-color:rgb(30,31,34);color:rgb(188,190,196)"><pre style="font-family:"JetBrains Mono",monospace;font-size:9.8pt"><span style="color:rgb(207,142,109)">public </span>MemorySegment <span style="color:rgb(86,168,245)">allocateMemory</span>(<span style="color:rgb(207,142,109)">long </span>size) <span style="color:rgb(207,142,109)">throws </span>Throwable {<br>  <span style="color:rgb(122,126,133)">// Call mmap to reserve virtual memory<br></span><span style="color:rgb(122,126,133)">  </span>MemorySegment addr = (MemorySegment) <span style="color:rgb(199,125,187)">mmap</span>.invoke(MemorySegment.<span style="color:rgb(199,125,187);font-style:italic">NULL</span>,    <span style="color:rgb(122,126,133)">// Let OS choose the starting address<br></span><span style="color:rgb(122,126,133)">                                                   </span>size,                        <span style="color:rgb(122,126,133)">// Size of the memory to reserve<br></span><span style="color:rgb(122,126,133)">                                                   </span><span style="color:rgb(199,125,187);font-style:italic">PROT_READ </span>| <span style="color:rgb(199,125,187);font-style:italic">PROT_WRITE</span>,      <span style="color:rgb(122,126,133)">// Read and write permissions<br></span><span style="color:rgb(122,126,133)">                                                   </span><span style="color:rgb(199,125,187);font-style:italic">MAP_PRIVATE </span>| <span style="color:rgb(199,125,187);font-style:italic">MAP_ANONYMOUS</span>, <span style="color:rgb(122,126,133)">// Private, anonymous mapping<br></span><span style="color:rgb(122,126,133)">                                                   </span>-<span style="color:rgb(42,172,184)">1</span>,                          <span style="color:rgb(122,126,133)">// No file descriptor<br></span><span style="color:rgb(122,126,133)">                                                   </span><span style="color:rgb(42,172,184)">0                            </span><span style="color:rgb(122,126,133)">// No offset<br></span><span style="color:rgb(122,126,133)">  </span>);<br>  <span style="color:rgb(207,142,109)">if </span>(addr == MemorySegment.<span style="color:rgb(199,125,187);font-style:italic">NULL</span>) {<br>    <span style="color:rgb(207,142,109)">throw new </span>OutOfMemoryError(<span style="color:rgb(106,171,115)">"Failed to allocate memory via mmap"</span>);<br>  }<br>  <span style="color:rgb(207,142,109)">return </span>addr;<br>}</pre></div></div><div>First thing I noticed is that I need addr.reinterpret(size) here, but now I wonder how Arena::allocate is actually implemented for Linux (calling malloc?). I think the native memory shouldn't be allocated in this case up until something is written to the MemorySegment slices, right? Of course we already have a lot of MemorySegment instances on the Java heap which are allocated, in comparison to a C or C++ version.</div><div dir="auto"><br></div><div dir="auto">Next, I'm not sure if I missed something, but it seems ridiculous hard to get a file descriptor (the actual int) for some syscalls, I guess as it's platform specific, but if I didn't miss something I'd have to use reflection, right? For instance if you have a FileChannel.</div><div dir="auto"><br></div><div dir="auto">My idea of using something like this is based on the idea of reducing allocations on the Java heap, as I described the problem a couple of months ago. Up until now I never "recycled/reused" pages when a read from disk was issued and when the page was not cached. I've always created new instances of these potentially big objects after a disk read, so in addition I'd implement something to cache and reuse Java pages (which kind of wrap the MemorySegments):</div><div dir="auto"><br></div><div dir="auto">In addition to the actual native memory I want to buffer page instances which use the MemorySegments on top, which can be reclaimed through filling the underlying MemorySegments with 0 again and some other cleanup stuff. So essentially I'd never unmap or use madvice don't need, as long as the process is running.</div><div dir="auto"><br></div><div dir="auto">Hope that makes sense and hopefully the ConcurrentHashMap to retrieve a page with a certain size, plus taking the first entry from a Deque of free pages... doesn't add more CPU cycles and synchronization overhead, but the allocation rate was 2,7Gb for a single txn.</div><div dir="auto"><br></div><div dir="auto">Kind regards</div><div dir="auto">Johannes</div><div><br></div><div>[1] <a href="https://db.in.tum.de/~freitag/papers/p29-neumann-cidr20.pdf" rel="noreferrer noreferrer" target="_blank">https://db.in.tum.de/~freitag/papers/p29-neumann-cidr20.pdf</a></div></div></div>