Skip to content

Commit

Permalink
Don't hold onto references in Consumer (#2191)
Browse files Browse the repository at this point in the history
* Don't store references in Consumer.
Don't box nullable value types.

* Removed reference check as it's no longer necessary.

* Overwrite field to null instead of calling DeadCodeEliminationHelper.KeepAliveWithoutBoxing.
  • Loading branch information
timcassell authored Feb 13, 2023
1 parent d3fbc03 commit ff5dbe6
Showing 1 changed file with 21 additions and 17 deletions.
38 changes: 21 additions & 17 deletions src/BenchmarkDotNet/Engines/Consumer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ private static readonly HashSet<Type> SupportedTypes
.Where(field => !field.IsStatic) // exclude this HashSet itself
.Select(field => field.FieldType));

#pragma warning disable IDE0052 // Remove unread private members
private volatile byte byteHolder;
private volatile sbyte sbyteHolder;
private volatile short shortHolder;
Expand All @@ -30,10 +31,10 @@ private static readonly HashSet<Type> SupportedTypes
private double doubleHolder;
private long longHolder;
private ulong ulongHolder;
private string stringHolder;
private object objectHolder;
private IntPtr ptrHolder;
private UIntPtr uptrHolder;
private volatile object objectHolder;
private volatile IntPtr ptrHolder;
private volatile UIntPtr uptrHolder;
#pragma warning restore IDE0052 // Remove unread private members

[MethodImpl(MethodImplOptions.AggressiveInlining)]
[PublicAPI]
Expand Down Expand Up @@ -84,11 +85,11 @@ private static readonly HashSet<Type> SupportedTypes

[MethodImpl(MethodImplOptions.AggressiveInlining)]
[PublicAPI]
public void Consume(IntPtr intPtrValue) => Volatile.Write(ref ptrHolder, intPtrValue);
public void Consume(IntPtr intPtrValue) => ptrHolder = intPtrValue;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
[PublicAPI]
public void Consume(UIntPtr uintPtrValue) => Volatile.Write(ref uptrHolder, uintPtrValue);
public void Consume(UIntPtr uintPtrValue) => uptrHolder = uintPtrValue;

[CLSCompliant(false)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand All @@ -97,22 +98,28 @@ private static readonly HashSet<Type> SupportedTypes

[MethodImpl(MethodImplOptions.AggressiveInlining)]
[PublicAPI]
public void Consume(string stringValue) => Volatile.Write(ref stringHolder, stringValue);
public void Consume(string stringValue) => Consume((object) stringValue);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
[PublicAPI]
public void Consume(object objectValue) => Volatile.Write(ref objectHolder, objectValue);
public void Consume(object objectValue)
{
// Write to volatile field to prevent dead code elimination and out-of-order execution.
objectHolder = objectValue;
// Overwrite field to null so we aren't holding onto references to affect GC behavior. (#1942)
objectHolder = null;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
[PublicAPI]
public void Consume<T>(T objectValue) where T : class // class constraint prevents from boxing structs
=> Volatile.Write(ref objectHolder, objectValue);
=> Consume((object) objectValue);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void Consume<T>(T* ptrValue) where T: unmanaged => Volatile.Write(ref ptrHolder, (IntPtr)ptrValue);
public unsafe void Consume<T>(T* ptrValue) where T : unmanaged => ptrHolder = (IntPtr) ptrValue;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void Consume(void* ptrValue) => Volatile.Write(ref ptrHolder, (IntPtr)ptrValue);
public unsafe void Consume(void* ptrValue) => ptrHolder = (IntPtr) ptrValue;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Consume<T>(in T value)
Expand Down Expand Up @@ -141,15 +148,12 @@ public void Consume<T>(in T value)
Volatile.Write(ref longHolder, (long)(object)value);
else if (typeof(T) == typeof(ulong))
Volatile.Write(ref ulongHolder, (ulong)(object)value);
else if (default(T) == null)
objectHolder = (object) value;
else if (default(T) == null && !typeof(T).IsValueType)
Consume((object) value);
else
ValueTypesConsumer(value); // non-primitive value types
DeadCodeEliminationHelper.KeepAliveWithoutBoxingReadonly(value); // non-primitive and nullable value types
}

[MethodImpl(MethodImplOptions.NoInlining)]
private void ValueTypesConsumer<T>(in T _) { }

internal static bool IsConsumable(Type type)
=> SupportedTypes.Contains(type) || type.GetTypeInfo().IsClass || type.GetTypeInfo().IsInterface;

Expand Down

0 comments on commit ff5dbe6

Please sign in to comment.