Skip to content

Instantly share code, notes, and snippets.

@xoofx
Created September 21, 2019 16:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xoofx/12f8013782ca9408864c9fb20131e6d7 to your computer and use it in GitHub Desktop.
Save xoofx/12f8013782ca9408864c9fb20131e6d7 to your computer and use it in GitHub Desktop.
Bench of GCHandle reused
// The following benchmark shows a barely known capability of GCHandle to be reused via their .Target
// as it is avoiding to reallocate an expensive entry into the GC table handle in the CLR runtime
/*
BenchmarkDotNet=v0.10.12, OS=Windows 10.0.18362
AMD Ryzen 9 3900X 12-Core Processor, 1 CPU, 24 logical cores and 12 physical cores
.NET Core SDK=3.0.100-rc1-014190
[Host] : .NET Core 2.1.13 (Framework 4.6.28008.01), 64bit RyuJIT
DefaultJob : .NET Core 2.1.13 (Framework 4.6.28008.01), 64bit RyuJIT
Method | Mean | Error | StdDev | Gen 0 | Allocated |
-------------------- |----------:|----------:|----------:|--------:|----------:|
TestGCHandle | 77.69 us | 0.5484 us | 0.5130 us | 14.4043 | 23.44 KB |
TestGCHandleReuse | 37.82 us | 0.6268 us | 0.5863 us | 14.4043 | 23.44 KB |
TestWeakReferencesT | 134.94 us | 1.0771 us | 1.0075 us | 28.8086 | 46.88 KB |
TestWeakReferences | 138.02 us | 0.7480 us | 0.6631 us | 28.8086 | 46.88 KB |
*/
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using BenchmarkDotNet.Attributes;
namespace TestBenchmark
{
[MemoryDiagnoser]
public class BenchGCHandleReused
{
private readonly List<WeakReference<object>> _weakReferencesT;
private readonly GCHandle[] _gcHandles;
private readonly List<WeakReference> _weakReferences;
private const int Count = 1000;
public BenchWeakReferences()
{
_weakReferencesT = new List<WeakReference<object>>();
_gcHandles = new GCHandle[Count];
_weakReferences = new List<WeakReference>();
}
[Benchmark]
public int TestGCHandle()
{
// Clear all GC handles
for (int i = 0; i < _gcHandles.Length; i++)
{
ref var gcHandle = ref _gcHandles[i];
if (gcHandle.IsAllocated)
{
_gcHandles[i].Free();
}
}
for (int i = 0; i < Count; i++)
{
_gcHandles[i] = GCHandle.Alloc(new object(), GCHandleType.Weak);
}
int result = 0;
for (int i = 0; i < Count; i++)
{
if (_gcHandles[i].Target != null)
{
result++;
}
}
return result;
}
[Benchmark]
public int TestGCHandleReuse()
{
// Clear all GC handles
for (int i = 0; i < _gcHandles.Length; i++)
{
ref var gcHandle = ref _gcHandles[i];
if (gcHandle.IsAllocated)
{
_gcHandles[i].Target = null;
}
}
for (int i = 0; i < Count; i++)
{
ref var gcHandle = ref _gcHandles[i];
var obj = new object();
if (gcHandle.IsAllocated)
{
gcHandle.Target = obj;
}
else
{
gcHandle = GCHandle.Alloc(obj, GCHandleType.Weak);
}
}
int result = 0;
for (int i = 0; i < Count; i++)
{
if (_gcHandles[i].Target != null)
{
result++;
}
}
return result;
}
[Benchmark]
public int TestWeakReferencesT()
{
_weakReferencesT.Clear();
for (int i = 0; i < Count; i++)
{
_weakReferencesT.Add(new WeakReference<object>(new object()));
}
int result = 0;
for (int i = 0; i < Count; i++)
{
object obj;
if (_weakReferencesT[i].TryGetTarget(out obj))
{
result++;
}
}
return result;
}
[Benchmark]
public int TestWeakReferences()
{
_weakReferences.Clear();
for (int i = 0; i < Count; i++)
{
_weakReferences.Add(new WeakReference(new object()));
}
int result = 0;
for (int i = 0; i < Count; i++)
{
if (_weakReferences[i].IsAlive)
{
result++;
}
}
return result;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment