News for hackers. Stuff that matters.
Re:Slitter: A slab allocator that trusts, but verifies by Const-me on Thursday August 05, 2021 @06:48AM (#28071995)
I have recently implemented something remotely similar in a totally different way.

Computers are 64-bit by now. For most practical applications, address space is unlimited. This means you can allocate let’s say 1TB of continuous address space at startup, and implement allocator on top that allocates and commits physical pages in that range, implementing a stack allocator. Not only it never moves the data, it also guarantees the complete memory is at continuous range of addresses. For instance, you can view that thing as an infinite std::vector() which never requires reallocations.

I did it for Windows on top of VirtualAlloc() API. Linux has it too, mmap() with PROT_NONE protection flag to reserve address space, then mprotect() to allocate physical pages in that range.

Re:Slitter: A slab allocator that trusts, but verifies by titzer on Thursday August 05, 2021 @10:54AM (#28074566)
Well, unfortunately, address space isn't unlimited. In my experience developing 64-bit wasm engines, the typical approach of reserving 8GB of memory for each Wasm memory leads to address space exhaustion after only about 1000 memories (regardless of how many actual pages those memories use). Therefore for a long living VM that might load and unload hundreds or even thousands of programs (on different webpages for example), it's really important to clean up those reservations promptly.

In V8, cleaning up the reservations for Wasm memories is ultimately tied to garbage collection of the JS heap, since JS objects can root memory. That led to the somewhat clunky need to retry allocation in a loop, running a full GC if allocation fails the first few times (https://github.com/v8/v8/blob/master/src/objects/backing-sto...).

There are also fundamental kernel limitations on virtual memory size. They are system dependent but much lower than 2^64.

Re:Slitter: A slab allocator that trusts, but verifies by Const-me on Thursday August 05, 2021 @11:35AM (#28075180)
On modern windows the limit is 2^47 bytes: https://docs.microsoft.com/en-us/windows/win32/memory/memory... With 8GB of virtual space per engine, should be enough for 16k of these engines. Interesting that you got OOM only after 1k engines, that’s 16x below the limit. Was that on Windows? Actual hardware or some VM substitute?

In my use case, I was lucky as I only need an allocator per hardware thread. They never approach thousands. So far, the maximum is 256 hardware threads, like in dual-CPU EPYC 7713.

Re:Slitter: A slab allocator that trusts, but verifies by titzer on Thursday August 05, 2021 @02:39PM (#28077758)
It seems to depend a lot on current conditions and is ultimately probabilistic. I.e. it would not fail 100% at 1000, but some large enough fraction of the time that that strategy was not shippable for Wasm. IIRC it might only have been 10% of the time, but it quickly increases to 100% along a sliding scale up to the hard limit. Part of that is fragmentation as well, because Chrome uses an allocator underneath that can carve out fairly large reservations that fragment the address space.
Re:Slitter: A slab allocator that trusts, but verifies by psykotic on Thursday August 05, 2021 @08:27PM (#28081838)
Just wanted to cosign that this is absolutely a real-world concern. For that reason I always try to make huge reservations as early in the process lifecycle as possible [1] and then subdivide them from there. Then you can know ahead of time how much space you have to work with. This still has potential composability issues, but partitioning the address space like this before VA fragmentation becomes an issue really helps when you have a greedy VA reserver competing against typical workloads (and their malloc's mmap/VirtualAlloc requests), especially in long-running programs.

[1] Ideally before most shared libraries have been loaded. I should do a simulation to get some quantitative estimates, but my hunch is that ASLR hurts badly even if each loaded library only takes up one 4 kB page. E.g. if you have an empty address space, allocate a 4 kB page at a random address and split the space in two around it, then try to densely pack as many 8 GB ranges in there as possible, you lose one potential 8 GB range (even if the ranges only require 4 kB alignment for their base address). Whenever such splits don't land in the same 8 GB clump, you lose a potential 8 GB range per split.

Re:Slitter: A slab allocator that trusts, but verifies by michielderhaeg on Thursday August 05, 2021 @07:17AM (#28072173)
This is what the Glasgow Haskell Compiler runtime does. If you run a Haskell application, you'll see it has uses 1TB of virtual memory.

This way you never have to talk to the kernel if you need more memory.

Re:Slitter: A slab allocator that trusts, but verifies by weavie on Thursday August 05, 2021 @08:31AM (#28072778)
Also Clozure Common Lisp.
Re:Slitter: A slab allocator that trusts, but verifies by eptcyka on Thursday August 05, 2021 @07:10AM (#28072123)
I can only imagine this working for applications that don't churn through memory or applications that are short lived. But in those cases, it would work really well.
Re:Slitter: A slab allocator that trusts, but verifies by Const-me on Thursday August 05, 2021 @07:46AM (#28072372)
My application is long-lived (at least hours) and uses gigabytes of memory.

It works for me because I have distinct moments in time when the whole stack is no longer needed. I’m working on a CAM/CAE app where work is organized as a sequence of optimization passes. I only need to keep the data within 1 iteration. At the end of every optimization pass I simply reset the stack pointer to the very bottom, and on the next iteration the stack starts growing again from zero reusing these physical memory pages.

BTW I’m using multiple of these stacks, one per thread. Helps with performance because no locks are needed, also L1 and L2 caches are inside cores, only the L3 is shared across the whole CPU.

Re:Slitter: A slab allocator that trusts, but verifies by Joker_vD on Thursday August 05, 2021 @08:22AM (#28072686)
So basically, your use case is the extreme variant of the one described in Appel's "Garbage collection can be faster than stack allocation" paper.
Re:Slitter: A slab allocator that trusts, but verifies by Const-me on Thursday August 05, 2021 @09:57AM (#28073798)
Not sure it deserves to be called garbage collector, too simple.

The approach is pretty close to arena allocation https://en.wikipedia.org/wiki/Region-based_memory_management... The only twist, with modern 64-bit processors there’s no need for linked lists or multiple blocks, can instead add new physical pages into pre-allocated address space.

Re:Slitter: A slab allocator that trusts, but verifies by stefan_ on Thursday August 05, 2021 @07:42AM (#28072344)
Address space is unlimited but pages are still 4K.
Re:Slitter: A slab allocator that trusts, but verifies by Const-me on Thursday August 05, 2021 @07:52AM (#28072417)
Right, I thought about large pages for that, unfortunately on Windows they’re pretty much useless. They require an elevated process, and they are incompatible with swap file i.e. they never paged to disk even when there’s low memory: https://docs.microsoft.com/en-us/windows/win32/memory/large-...
Re:Slitter: A slab allocator that trusts, but verifies by gpderetta on Thursday August 05, 2021 @10:37AM (#28074307)
and TLB is also a scarce resource.