FWIW, you can use DMD's built in profiler so long as the receiving thread is the same as the sending thread:
And here's the trace log after "scope" was added. Notice that there were 61 calls to GCX.fullcollect(). We can also see that there was 1 allocation per send/receive operation, so only an alloc for the message list node.
======== Timer Is 3579545 Ticks/Sec, Times are in Microsecs ========
Num Tree Func Per
Calls Time Time Call
1000000 437709765 220179413 220 void std.concurrency._send!(int)._send(std.concurrency.MsgType, std.concurrency.Tid, int)
1000000 300987757 140736393 140 bool std.concurrency.MessageBox.get!(nothrow @safe void delegate(int), pure @safe void function(std.concurrency.LinkTerminated)*, pure @safe void function(std.concurrency.OwnerTerminated)*, pure @safe void function(std.variant.VariantN!(32u).VariantN)*).get(scope nothrow @safe void delegate(int), scope pure @safe void function(std.concurrency.LinkTerminated)*, scope pure @safe void function(std.concurrency.OwnerTerminated)*, scope pure @safe void function(std.variant.VariantN!(32u).VariantN)*)
1000000 202131609 89479808 89 void* gc.gcx.GC.malloc(uint, uint, uint*)
1 825045422 57556501 57556501 _Dmain
1000033 112651800 52026745 52 void* gc.gcx.GC.mallocNoSync(uint, uint, uint*)
61 53422342 49606106 813214 uint gc.gcx.Gcx.fullcollect(void*)
2000000 160103753 42531732 21 bool std.concurrency.MessageBox.get!(nothrow @safe void delegate(int), pure @safe void function(std.concurrency.LinkTerminated)*, pure @safe void function(std.concurrency.OwnerTerminated)*, pure @safe void function(std.variant.VariantN!(32u).VariantN)*).get(scope nothrow @safe void delegate(int), scope pure @safe void function(std.concurrency.LinkTerminated)*, scope pure @safe void function(std.concurrency.OwnerTerminated)*, scope pure @safe void function(std.variant.VariantN!(32u).VariantN)*).bool scan(ref std.concurrency.List!(std.concurrency.Message).List)
2000000 42018612 39837170 19 int std.variant.VariantN!(32u).VariantN.handler!(int).handler(std.variant.VariantN!(32u).VariantN.OpID, ubyte[32]*, void*)
1000000 117572021 24641771 24 bool std.concurrency.MessageBox.get!(nothrow @safe void delegate(int), pure @safe void function(std.concurrency.LinkTerminated)*, pure @safe void function(std.concurrency.OwnerTerminated)*, pure @safe void function(std.variant.VariantN!(32u).VariantN)*).get(scope nothrow @safe void delegate(int), scope pure @safe void function(std.concurrency.LinkTerminated)*, scope pure @safe void function(std.concurrency.OwnerTerminated)*, scope pure @safe void function(std.variant.VariantN!(32u).VariantN)*).bool onStandardMsg(ref std.concurrency.Message)
1000000 47280794 20418675 20 void std.concurrency.Message.map!(nothrow @safe void delegate(int)).map(nothrow @safe void delegate(int))
1000000 316556767 15569009 15 int std.concurrency.receiveOnly!(int).receiveOnly()
1000000 36317362 13212905 13 @property bool std.variant.VariantN!(32u).VariantN.convertsTo!(int).convertsTo()
1000000 15445906 10879089 10 std.concurrency.Message std.concurrency.Message.__ctor!(int).__ctor(std.concurrency.MsgType, int)
1000000 45649454 9332092 9 @property bool std.concurrency.Message.convertsTo!(int).convertsTo()
1000000 26790032 7875877 7 @property int std.variant.VariantN!(32u).VariantN.get!(int).get()
1000000 444757778 7048013 7 void std.concurrency._send!(int)._send(std.concurrency.Tid, int)
15512 6681162 6657279 429 int gc.gcx.Gcx.allocPage(ubyte)
1000000 450932153 6174374 6 void std.concurrency.send!(int).send(std.concurrency.Tid, int)
1000000 4566817 4566817 4 std.variant.VariantN!(32u).VariantN std.variant.VariantN!(32u).VariantN.opAssign!(int).opAssign(int)
4087 3635735 3518353 860 void gc.gcx.Gcx.mark(void*, void*, int)
2000000 2069965 2069965 1 int std.variant.VariantN!(32u).VariantN.handler!(int).handler(std.variant.VariantN!(32u).VariantN.OpID, ubyte[32]*, void*).bool tryPutting(int*, TypeInfo, void*)
2990271 195287 195287 0 void* gc.gcx.sentinel_add(void*)
1000000 147610 147610 0 bool std.concurrency.MessageBox.get!(nothrow @safe void delegate(int), pure @safe void function(std.concurrency.LinkTerminated)*, pure @safe void function(std.concurrency.OwnerTerminated)*, pure @safe void function(std.variant.VariantN!(32u).VariantN)*).get(scope nothrow @safe void delegate(int), scope pure @safe void function(std.concurrency.LinkTerminated)*, scope pure @safe void function(std.concurrency.OwnerTerminated)*, scope pure @safe void function(std.variant.VariantN!(32u).VariantN)*).bool pty(ref std.concurrency.List!(std.concurrency.Message).List)
1000032 121459 121459 0 void gc.gcx.Gcx.setBits(gc.gcx.Pool*, uint, uint)
2000000 111475 111475 0 int std.variant.VariantN!(32u).VariantN.handler!(int).handler(std.variant.VariantN!(32u).VariantN.OpID, ubyte[32]*, void*).int* getPtr(void*)
1000033 102413 102413 0 ubyte gc.gcx.Gcx.findBin(uint)
1002228 94742 94742 0 @property uint gc.gcx.Pool.shiftBy()
1000000 72086 72086 0 int std.concurrency.receiveOnly!(int).receiveOnly().nothrow @safe void __lambda17(int)
995119 70854 70854 0 void gc.gcx.Gcx.log_free(void*)
1000033 61505 61505 0 void gc.gcx.sentinel_init(void*, uint)
1000033 55070 55070 0 void gc.gcx.Gcx.log_malloc(void*, uint)
995119 51748 51748 0 void gc.gcx.sentinel_Invariant(const(void*))
1 24692 24692 24692 void gc.gcx.Pool.initialize(uint, bool)
3538 3544517 24525 6 void gc.gcx.Gcx.mark(void*, void*)
15511 23883 23522 1 uint gc.gcx.Pool.allocPages(uint)
124447 11615 11615 0 void gc.gcx.Gcx.clrBitsSmallSweep(gc.gcx.Pool*, uint, uint)
1 7051 5921 5921 void gc.gcx.GC.initialize()
1 27490 2797 2797 gc.gcx.Pool* gc.gcx.Gcx.newPool(uint, bool)
61 53424783 2441 40 uint gc.gcx.Gcx.fullcollectshell()
55 2227 2227 40 void gc.gcx.Gcx.addRange(void*, void*)
1 1129 1119 1119 void gc.gcx.Gcx.initialize()
16 360 360 22 uint gc.gcx.Pool.extendPages(uint)
1 2547 319 319 void gc.gcx.GC.addRange(void*, uint)
2196 278 278 0 gc.gcx.Pool* gc.gcx.Gcx.findPool(void*)
1 65 65 65 void gc.gcx.GC.disable()
1 9 9 9 void gc.gcx.Gcx.log_init()
1 5 5 5 void gc.gcx.GC.enable()
1 0 0 0 void gc.gcx.GC.setStackBottom(void*)
1 0 0 0 @property uint gc.gcx.Pool.divisor()