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.