www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Constant GC allocations when sending large messages to threads?

reply cc <cc nevernet.com> writes:
Given the sample program at https://pastebin.com/u9sSNtj7
I'm experiencing GC allocations with every call to 
std.concurrency.send when sending larger messages (e.g. multiple 
ulongs).  These do not occur when sending uints in comparison, in 
the provided example.

For example, when the ManyAllocations version is set, I see 
results like:
allocations: 100  bytes: 3280

When commented out, I see:
allocations: 1  bytes: 80

Is there a way to mitigate this memory usage?

Using DMD32 D Compiler v2.089.1-dirty on Windows 10 x64
cmdline: rdmd.exe -m64
Jan 29 2020
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 1/29/20 2:48 PM, cc wrote:
 Given the sample program at https://pastebin.com/u9sSNtj7
 I'm experiencing GC allocations with every call to std.concurrency.send 
 when sending larger messages (e.g. multiple ulongs).  These do not occur 
 when sending uints in comparison, in the provided example.
 
 For example, when the ManyAllocations version is set, I see results like:
 allocations: 100  bytes: 3280
 
 When commented out, I see:
 allocations: 1  bytes: 80
 
 Is there a way to mitigate this memory usage?
 
 Using DMD32 D Compiler v2.089.1-dirty on Windows 10 x64
 cmdline: rdmd.exe -m64
 
I'm pretty sure std.concurrency uses Variant to pass message data, which boxes when it gets over a certain size. You are probably crossing that threshold. The allocations should level out eventually when the GC starts collecting them. -Steve
Jan 29 2020
parent reply cc <cc nevernet.com> writes:
On Wednesday, 29 January 2020 at 21:10:53 UTC, Steven 
Schveighoffer wrote:
 I'm pretty sure std.concurrency uses Variant to pass message 
 data, which boxes when it gets over a certain size. You are 
 probably crossing that threshold.

 The allocations should level out eventually when the GC starts 
 collecting them.

 -Steve
Is there a way to pre-allocate a buffer or something to be used? Ideally I'd like to avoid too many garbage collections happening, in my application these thread messages happen almost every frame and are quickly adding up to 100s of kilobytes in allocations every few seconds.
Jan 30 2020
next sibling parent bauss <jj_1337 live.dk> writes:
On Friday, 31 January 2020 at 07:14:30 UTC, cc wrote:
 On Wednesday, 29 January 2020 at 21:10:53 UTC, Steven 
 Schveighoffer wrote:
 I'm pretty sure std.concurrency uses Variant to pass message 
 data, which boxes when it gets over a certain size. You are 
 probably crossing that threshold.

 The allocations should level out eventually when the GC starts 
 collecting them.

 -Steve
Is there a way to pre-allocate a buffer or something to be used? Ideally I'd like to avoid too many garbage collections happening, in my application these thread messages happen almost every frame and are quickly adding up to 100s of kilobytes in allocations every few seconds.
You can just allocate non-GC memory.
Jan 31 2020
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 1/31/20 2:14 AM, cc wrote:
 On Wednesday, 29 January 2020 at 21:10:53 UTC, Steven Schveighoffer wrote:
 I'm pretty sure std.concurrency uses Variant to pass message data, 
 which boxes when it gets over a certain size. You are probably 
 crossing that threshold.

 The allocations should level out eventually when the GC starts 
 collecting them.
Is there a way to pre-allocate a buffer or something to be used? Ideally I'd like to avoid too many garbage collections happening, in my application these thread messages happen almost every frame and are quickly adding up to 100s of kilobytes in allocations every few seconds.
You could use RefCounted to build a struct that then is sendable with the data you need. RefCounted allocates using C malloc, not the GC. It might actually be reasonable to modify std.concurrency to use RefCounted instead of GC memory (i.e. it needs to be a specialized Variant). -Steve
Jan 31 2020
parent reply cc <cc nevernet.com> writes:
On Friday, 31 January 2020 at 15:47:26 UTC, Steven Schveighoffer 
wrote:
 You could use RefCounted to build a struct that then is 
 sendable with the data you need. RefCounted allocates using C 
 malloc, not the GC.
Thanks for the tips. How exactly would I go about sending a RefCounted value? static struct Foo { int a; disable this(this); } auto t = RefCounted!Foo(Foo(5)); tid.send(t); Gives me: phobos\std\concurrency.d(625): Error: static assert: "Aliases to mutable thread-local data not allowed." Whereas trying to declare it as immutable gives me a pile of errors including: Error: mutable method `std.typecons.RefCounted!(int, cast(RefCountedAutoInitialize)1).RefCounted.__postblit` is not callable using a `immutable` object
Feb 02 2020
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 2/2/20 12:13 PM, cc wrote:
 On Friday, 31 January 2020 at 15:47:26 UTC, Steven Schveighoffer wrote:
 You could use RefCounted to build a struct that then is sendable with 
 the data you need. RefCounted allocates using C malloc, not the GC.
Thanks for the tips.  How exactly would I go about sending a RefCounted value? static struct Foo {     int a;      disable this(this); } auto t = RefCounted!Foo(Foo(5)); tid.send(t); Gives me: phobos\std\concurrency.d(625): Error: static assert: "Aliases to mutable thread-local data not allowed." Whereas trying to declare it as immutable gives me a pile of errors including: Error: mutable method `std.typecons.RefCounted!(int, cast(RefCountedAutoInitialize)1).RefCounted.__postblit` is not callable using a `immutable` object
Ugh, I think it should be doable. But I don't have time right now to try and figure it out. The idea would be to send an immutable/shared piece of data that's refcounted across to another thread. If send is rejecting that, or refCounted is not playing nice, maybe file some bugzilla issues. -Steve
Feb 02 2020