Jump to page: 1 2 3
Thread overview
Thread GC non "stop-the-world"
Sep 23, 2014
Oscar Martin
Sep 23, 2014
Rikki Cattermole
Sep 23, 2014
Oscar Martin
Sep 24, 2014
Marc Schütz
Sep 24, 2014
Oscar Martin
Sep 25, 2014
Wyatt
Sep 25, 2014
Oscar Martin
Sep 25, 2014
Sean Kelly
Nov 21, 2014
Kagamin
Nov 21, 2014
Sean Kelly
Nov 21, 2014
Kagamin
Sep 23, 2014
Kagamin
Sep 23, 2014
Marc Schütz
Sep 23, 2014
Kagamin
Sep 23, 2014
Marc Schütz
Sep 23, 2014
Kagamin
Sep 23, 2014
Marc Schütz
Sep 23, 2014
Oscar Martin
Sep 24, 2014
Marc Schütz
Sep 23, 2014
David Nadlinger
Sep 23, 2014
Sean Kelly
Sep 23, 2014
Oscar Martin
Sep 24, 2014
Kagamin
Sep 24, 2014
Sean Kelly
Sep 25, 2014
Kagamin
Sep 24, 2014
Oscar Martin
Sep 25, 2014
Kagamin
Sep 24, 2014
deadalnix
Sep 28, 2014
Rainer Schuetze
September 23, 2014
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?
September 23, 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.
September 23, 2014
On Tuesday, 23 September 2014 at 00:15:51 UTC, Oscar Martin wrote:
> 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.

A use case, which comes to mind: a game saves progress to the server, the main thread prepares data to be saved (a relatively lightweight operation) and hands it over to another thread, which saves the data in background. How would you do it?
September 23, 2014
On Tuesday, 23 September 2014 at 09:04:48 UTC, Kagamin wrote:
> On Tuesday, 23 September 2014 at 00:15:51 UTC, Oscar Martin wrote:
>> 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.
>
> A use case, which comes to mind: a game saves progress to the server, the main thread prepares data to be saved (a relatively lightweight operation) and hands it over to another thread, which saves the data in background. How would you do it?

This can be done without sharing. Of course, a uniqueness concept would be needed.
September 23, 2014
The question is how thread-local GC will account for data passed to another thread.
September 23, 2014
On Tuesday, 23 September 2014 at 10:38:29 UTC, Kagamin wrote:
> The question is how thread-local GC will account for data passed to another thread.

std.concurrency.send() could notify the GC.
September 23, 2014
And what GC does? Pins the allocated blocks for another thread?
September 23, 2014
On Tuesday, 23 September 2014 at 15:23:16 UTC, Kagamin wrote:
> And what GC does? Pins the allocated blocks for another thread?

Assuming there is one thread-local GC per thread, it transfers responsibility of the allocated data from the sender to the receiver. This means, the old GC doesn't need to scan it any more, but the new one does.
September 23, 2014
On Tuesday, 23 September 2014 at 10:38:29 UTC, Kagamin wrote:
> The question is how thread-local GC will account for data passed to another thread.

I was briefly discussing this with Andrei at (I think) DConf 2013. I suggested moving data to a separate global GC heap on casting stuff to shared. Assigning types with indirections to a __gshared variable might also trigger this, unless we can find a better design. IIRC, Andrei dismissed this as impractical due to the overhead and need for precise scanning. I still like to think that it would be worth it, though, even if I can't spare the time for looking into an implementation right now.

David
September 23, 2014
On Tuesday, 23 September 2014 at 16:47:09 UTC, David Nadlinger wrote:
> On Tuesday, 23 September 2014 at 10:38:29 UTC, Kagamin wrote:
>> The question is how thread-local GC will account for data passed to another thread.
>
> I was briefly discussing this with Andrei at (I think) DConf 2013. I suggested moving data to a separate global GC heap on casting stuff to shared.

... and casting to invariant, since invariant is implicitly shared (insert cuss-words here).
« First   ‹ Prev
1 2 3