**cdunn2001:** _"Pathological"? If dealloc() fragments a large block, then 
repeated allocation of large blocks would definitely cause memory use to 
explode._

I was talking about something that sounded like unused space going up while 
allocated memory remained constant. That would be a pathological case. If I 
misunderstood you and the ratio unused/used doesn't go up indefinitely, then it 
wouldn't be.

**cdunn2001:** _One interesting note: When I briefly tried boehm, it complained 
one or twice early in the program about my large allocations._

That has to do with Boehm being a conservative garbage collector (the Nim GCs 
scan only the stack conservatively, all other roots and the heap are being 
scanned precisely) and thus more vulnerable to integers and other non-pointer 
data being misidentified as pointers.

**cdunn2001:** _I still would like to see freelist sizes._

I'm not sure what you mean by that. Large blocks in the allocator (type 
`BigChunk`) are kept in a single list and are allocated using first-fit. Unless 
you are also allocating a lot of small objects, the amount of free and used 
memory should be a reasonable approximation in your use case.

**cdunn2001:** _Even on Linux, with the simple example I pasted above, you can 
verify that the already speedy program becomes even speedier after total 
allocated memory stabilizes, which indicates to me that allocations from 
existing freelists are quicker than acquiring memory from the system._

That shouldn't be too surprising. The first time, you pay the overhead for mmap 
and a lot of page faults as memory is being initialized. Try allocating a large 
`seq`, for example, then do the same and write zeros into it. It should take 
considerably less than twice the time.

* * *

In any event, this is all a bit beside the point (the above is about diagnosing 
the root causes and doesn't give you a solution). Your problem seems to be that 
large memory allocations lead to more wasted memory that you can justify. The 
primary problem appears to be fragmentation, and that's tricky to solve, 
barring a compacting GC; nothing we are talking about here can automatically 
fix it.

The exact solution will depend on what you need the large memory blocks for. If 
the allocation patterns are relatively simple to manage, then a possible 
solution would be to use `mmap()` with explicit allocation and deallocation 
(virtual memory fragmentation is less of an issue on 64-bit machines, except 
for the 64k limit on mmap()-ed blocks on Linux).

To then avoid manual memory management, these large blocks can be wrapped in 
GCed handlers with finalizers so that deallocation is handled automatically; 
however, last I checked, Nim didn't have something akin to .NET's 
`GC.AddMemoryPressure` to make sure that the GC runs frequently enough. One 
could track the entire mmap()-ed memory, however, and trigger a full GC when it 
exceeds a certain amount (relative to total memory occupied).

Reply via email to