Thread overview
Constant GC allocations when sending large messages to threads?
Jan 29, 2020
cc
Jan 31, 2020
cc
Jan 31, 2020
bauss
Feb 02, 2020
cc
January 29, 2020
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

January 29, 2020
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
January 31, 2020
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.
January 31, 2020
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.
January 31, 2020
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
February 02, 2020
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

February 02, 2020
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