Remix.run Logo
pizlonator 5 days ago

Well missing free is just swallowed by the GC - the leak gets fixed without any message.

I have found bugs in the software that I’ve ported, yeah.

purpleidea 5 days ago | parent | next [-]

This is neat work. I noticed on your software page some patches you had to make to get those things to compile. Have you sent those upstream? Eg, I noticed a simple 1 line bash change for example.

writebetterc 5 days ago | parent | prev [-]

To add on top of this: This is a tracing GC. It only ever visits the live data, not the dead data. In other words, it would need a lot more special support if it wanted to report the dead objects.

tomp 5 days ago | parent | next [-]

A non-moving GC must visit dead objects.

pizlonator 5 days ago | parent | next [-]

Not quite.

FUGC used a bit vector SIMD sweep using a bit vector on the side so it doesn’t visit the dead objects at all in the sense that it doesn’t touch their contents. And it only visits them in the sense that a single instruction deals with many dead or alive objects at once.

writebetterc 5 days ago | parent | prev [-]

I forgot that this GC is non-moving (I'm not used to that assumption, and it was a bit of a quick comment).

I do find the statement dubious still, do you mind clearing it up for me?

Given a page { void* addr; size_t size; size_t alignment; BitMap used; } where used's size in bits is page.size / page.alignment, surely we only need to visit the used bitmap for marking a memory slot as free?

kragen 5 days ago | parent | next [-]

Yes, I agree. (This thread continued in https://news.ycombinator.com/item?id=45137286.)

tomp 5 days ago | parent | prev [-]

You’re correct, I forgot about that optimisation!

kragen 5 days ago | parent | prev [-]

Really? How does a non-moving GC make dead objects available for reallocation without visiting them?

torginus 5 days ago | parent | next [-]

Why would it need to visit them? It just marks the address ranges as available in its internal bookkeeping (bitmaps etc).

kragen 5 days ago | parent [-]

In the general case there are as many newly available address ranges as dead objects, so that counts as visiting them in this context.

torginus 5 days ago | parent | next [-]

I don't think that's a definition of 'visit' most people would agree with.

I'm actually working on my own language that has a non-moving GC. It uses size classes (so 16 byte objects, 32 byte objects etc.), each of which is allocated in a continous slab of memory. Occupancy is determined by a bitmap, 1 bit for each slot in the slab.

The GC constructs a liveness bitmap for the size class, and the results are ANDed together, 'freeing' the memory. If you fill the slab with dead objects, then run the GC, it will not walk anywhere on this slab, create an all zero liveness bitmap, and free the memory.

kragen 5 days ago | parent [-]

That's an awesome project! Is your GC generational despite being non-moving? What are your main objectives for the project?

The liveness bitmap approach is pretty widespread at this point; jemalloc works the same way IIRC.

Still, I think that counts as "visiting" in the context of this discussion: https://news.ycombinator.com/item?id=45137139

writebetterc 5 days ago | parent | next [-]

I don't think it counts as visiting, as you never look at the dirtied bitmap during GC, only during allocation. That means, you don't actually know if a dirty bit represents a different object or not (if a 16-byte size class is allowed to have 32-byte objs in it, for example). To know that you'd either have to have strict size classes, or you'd have to have object headers for specifying the start of an object.

I agree that it's easy to add in a visitation pass, where you take the bitmap of live objects after marking and diff it with the currently existing one in order to signal that you might have a leak.

So basically, I think we're like 99% in agreement.

kragen 5 days ago | parent [-]

It's always nice when the impact of collision of opposing opinions gives rise to the spark of mutual understanding rather than merely inflaming base emotions.

Typically bitmap-based allocators don't actually allow a 16-byte size class to have 32-byte objects in it, but I haven't looked at FUGC to see if that's true of it.

torginus 5 days ago | parent | next [-]

I toyed with the idea of allowing this, in bitmaps, it's pretty easy and efficient to find contiguous areas with bit twiddling hacks, for example

//assume free map is the bitmap where 1 means free

uint32_t free_map;

uint32_t free_map_2 = (free_map & (free_map >> 1)); // so on and so forth

I haven't really done anything like this yet, it has certain disadvantages, but you can pack multiple size classes into the same bitmap, you do a bit more work during alloc and resolving interior pointers is a bit more costly (if you have those), in exchange for having less size classes.

kragen 5 days ago | parent [-]

Sure, to find contiguous chunks of 6 slots within a single word, you can do

    t &= t << 1;
    t &= t << 2;
    t &= t << 2;
and that sort of thing is pretty appealing, but you lose the ability to know what size an object is just by looking at an address, and it's still a lot slower than scanning for an open slot in a page of 5× bigger objects.

Should I assume from your use of uint32_t that you're targeting embedded ARM microcontrollers?

pizlonator 5 days ago | parent | prev [-]

FUGC is size segregated. 16 byte size class will only have 16 byte objects.

A bunch of other optimizations fall out from doing that

torginus 5 days ago | parent | prev [-]

It's not generational, because unlike Java, but like C or C++, programs aren't supposed to generate a lot of ephemeral objects while they run. I also wanted to keep things as simple as possible to have a chance of actually shipping something in my lifetime :D

kragen 5 days ago | parent [-]

That sounds like a good approach! Is it public?

torginus 5 days ago | parent [-]

Not yet unfortunately, there are a few thorny issues, and I want to get it into an actually usable state before I dare make any claims about it :)

thomasmg 5 days ago | parent | prev [-]

> there are as many newly available address ranges as dead objects

Well, when using a bitmap (as they seem to do in the article), then multiple subsequent dead objects are considered to be in the same range, because multiple subsequent bits in the bitmap have the value zero. There is no need to visit each zero bit in the bitmap separately.

thomasmg 5 days ago | parent | prev [-]

If you want to use a standard malloc / free implementation (dlmalloc etc.) then dead object need to be known, yes.

But the malloc library could be fully integrated into the GC mechanism. In this case, there is no need. That's probably much easier, and faster, because the malloc can be simplified to bumping a pointer.

kragen 5 days ago | parent [-]

That works if you use a copying garbage collector, but not a non-moving collector like FUGC.

thomasmg 5 days ago | parent [-]

OK, I did not read the source code of the FUGC, but the article mentions "FUGC relies on a sweeping algorithm based on bitvector SIMD." So, assuming there is just one bit per block of memory, then there is no need to visit the memory of the dead objects in the sweep phase. The bit of the dead object is zero, and so that memory block is considered free and available for reallocation. There is no need to visit the free block.

kragen 5 days ago | parent [-]

It's true that it doesn't dirty up your dcache, but it's "visiting" enough that it wouldn't "need a lot more special support if it wanted to report the dead objects," which is the context of the discussion here.