On Tuesday, 4 July 2017 at 03:52:39 UTC, Moritz Maxeiner wrote:

First of all thank you for your responses!

On Tuesday, 4 July 2017 at 02:53:00 UTC, Filip Bystricky wrote:
On Tuesday, 4 July 2017 at 01:56:11 UTC, Moritz Maxeiner wrote:
[...]

However, in many cases it is unacceptable to have to prevent the whole block from being freed (especially if the memory is managed by a compacting gc).

Then you must adjust your acceptance parameters, because:
Eventually (down the parent Allocator call chain) you will have to get the memory from the operating system, and AFAIK they only support deallocating via pointers to the beginning of a previously allocated block;

You're right, this would only work for a layer on top of the system allocator, such as regions.

let PartiallyDeallocatableAllocator (PDA) be our Allocator implementation supporting partial deallocations via such a parent Allocator (Parent) that can only (de)allocate in whole blocks. That means even if Parent collects garbage it will not be able to allocate memory from within a block previously allocated (by PDA calling Parent's allocate) until that (whole) block has been been deallocated in Parent, either explicitly (by PDA), or implicitly by being collected. And since PDA will want to track deallocated subblocks and hand them out in its own `allocate`, bypassing Parent when feasible (otherwise what's the point of supporting partial deallocations if you can't reuse the freed memory), it will have to have pointers to these subblocks, i.e. even if Parent collects garbage, PDA will block the collection, anyway (live pointers into the block).
You're right, but I think it's sufficient to allow the PDA to hold on to the whole block, provided it can re-allocate chunks of partially deallocated blocks.

I think the feature would work better as an additional allocator primitive, which should be easy to implement for most allocators that support reallocate (e.g. bitmapped-block and free-list allocators).

I don't see how reallocate helps here, as you can only grow/shrink towards the end of a block, not the beginning (so you can only do mimick partial deallocations at the end of a block).

I meant rather that I think that what makes it possible for allocators to implement reallocate should usually make it possible to implement partialDeallocate. For example, a free-list allocator that tracks blocks by pointer and length will probably realloc by splitting a block into two. Well, you could split a block in similar ways to partially deallocate it. Another example would be a bitmapped block allocator: just clear all the bits of the blocks you want to deallocate (in this case a block is a fixed-length chunk, and the allocation comprises several blocks).

You're right, realloc doesn't help, and nor do any of the other primitives. This is why it would make more sense as its own primitive.


In the meantime I realized that since a child node can only [...]

Good to see that you found an easier solution :)


On Tuesday, 4 July 2017 at 04:11:11 UTC, Moritz Maxeiner wrote:
On Tuesday, 4 July 2017 at 03:13:14 UTC, Filip Bystricky wrote:
Oh and I forgot to mention: another use-case for this would be for arrays. For manually managed arrays like std.container.array, it would make it possible to transfer ownership of individual objects from the array back to the program after the array goes out of scope.

Not sure I understand you here: If an instance of such a manual array implementation goes out of scope it must destruct (if they are objects and not primitives) and deallocate its elements. There is no ownership transfer going on here (and who would be the target, anyway?).

I was thinking of a Range of some large value type T, that stores values in a T[], but that can give ownership of an elements by splitting the backing array into two parts, before the element and after it, then returning a Unique!T pointer to the element. Then whether the range or the Unique!T goes out of scope first, their corresponding "owned" memory will be deallocated. But I realized that in this case it would probably be less overhead to just store the values as a (Unique!T)[].


For gc slices, it could enable some gc implementations to deallocate parts of an array even if there are still references pointing inside that array.

I'm fairly certain the necessary bookkeeping logic for partial deallocations will outweigh any gain from it. In the case of such gc slices, I would rather just memcpy to a new, smaller block and update pointers to it (-> a moving GC).

But sometimes you have a huge chunk of memory and want to free half of it, but don't want to copy the other half (or maybe you don't have enough RAM to do so). One operation is O(n) (n = size of the new, smaller block), the other is O(m) (m = the number of chunks you're splitting the block into). That's why realloc exists, and we don't just always memcpy to a new, smaller array.

Reply via email to