Thread GC non "stop-the-world"

Rikki Cattermole via Digitalmars-d digitalmars-d at puremagic.com
Mon Sep 22 18:58:48 PDT 2014


On Tuesday, 23 September 2014 at 00:15:51 UTC, Oscar Martin wrote:
> The cost of using the current GC in D, although beneficial for 
> many types of programs, is unaffordable for programs such as 
> games, etc... that need to perform repetitive tasks every short 
> periods of time. The fact that a GC.malloc/realloc on any 
> thread can trigger a memory collection that stop ALL threads of 
> the program for a variable time prevents it. Conversations in 
> the forum as "RFC: reference Counted Throwable", "Escaping the 
> Tyranny of the GC: std.rcstring, first blood" and the @nogc 
> attribute show that this is increasingly perceived as a problem.
> Besides the ever-recurring "reference counting", many people 
> propose to improve the current implementation of GC. Rainer 
> Schuetze developed a concurrent GC in Windows:
>
>    http://rainers.github.io/visuald/druntime/concurrentgc.html
>
> With some/a lot of work and a little help compiler (currently 
> it indicates by a flag if a class/structure contains 
> pointers/references to other classes/structures, it could 
> increase this support to indicate which fields are 
> pointers/references) we could implement a 
> semi-incremental-generational-copying GC-conservative like:
>
>    http://www.hboehm.info/gc/
> or
>    http://www.ravenbrook.com/project/mps/
>
> Being incremental, they try to minimize the "stop-the-world" 
> phase. But even with an advanced GC, as programs become more 
> complex and use more memory, pause time also increases. See for 
> example (I know it's not normal case, but in a few years ...)
>
>    
> http://blog.mgm-tp.com/2014/04/controlling-gc-pauses-with-g1-collector
>
> (*) What if:
> - It is forbidden for "__gshared" have references/pointers to 
> objects allocated by the GC (if the compiler can help with this 
> prohibition, perfect, if not the developer have to know what he 
> is doing)
> - "shared" types are not allocated by the GC (they could be 
> reference counted or manually released or ...)
> - "immutable" types are no longer implicitly "shared"
>
> In short, the memory accessible from multiple threads is not 
> managed by the GC.
>
> With these restrictions each thread would have its 
> "I_Allocator", whose default implementation would be an 
> incremental-generational-semi-conservative-copying GC, with no 
> inteference with any of the other program threads (it should be 
> responsible only for the memory reserved for that thread). 
> Other implementations of "I_Allocator" could be based on 
> Andrei's allocators. With "setThreadAllocator" (similar to 
> current gc_setProxy) you could switch between the different 
> implementations if you need. Threads with critical time 
> requirements could work with an implementation of "I_Allocator" 
> not based on the GC. It would be possible simulate scoped 
> classes:
>
> {
> 	setThreadAllocator(I_Allocator_pseudo_stack)
> 	scope(exit) {
> 		I_Allocator_pseudo_stack.deleteAll();
> 		setThreadAllocator(I_Allocator_gc);
> 	}
> 	auto obj = MyClass();
> 	...
> 	// Destructor are called and memory released
> }
>
> Obviously changes (*) break compatibility with existing code, 
> and therefore maybe they are not appropriate for D2. Also these 
> are general ideas, sure these changes lead to other problems. 
> But the point I want to convey is that in my opinion, while 
> these problems are solvable, a language for "system 
> programming" is incompatible with shared data managed by a GC
>
> Thoughts?

Short, I dislike pretty much all changes to __gshared/shared. 
Breaks too many things.
Atleast with Cmsed, (I'm evil here) where I use __gshared 
essentially as a read only variable but modifiable when starting 
up (to modify need synchronized, to read doesn't).

I have already suggested before in threads something similar to 
what your suggesting with regards to setting allocator except:

The memory manager is in a stack. Default is GC e.g. the current 
one.
Compiler knows which pointers escapes. Can pass to pure functions 
however.

with(myAllocator) { // myAllocator.opWithIn
   ...//allocate
} // myAllocator.opWithCanFree
// myAllocator.opWithOut

class MyAllocator : Allocator {
   override void opWithIn(string func = __FUNCTION__, int line = 
__LINE__) {
     GC.pushAllocator(this);
   }

   override void opWithCanFree(void** freeablePointers) {
     //...
   }

   override void opWithOut(string func = __FUNCTION__, int line = 
__LINE__) {
     GC.popAllocator();
   }

   void* alloc(size_t amount) {
     return ...;
   }

   void free(void*) {
      //...
   }
}

You may have something about thread allocators though. Humm 
druntime would already need changes so maybe.

Ehh this really needs a DIP instead of me whining. If I do it, 
ETA December.


More information about the Digitalmars-d mailing list