Skip to content

Instantly share code, notes, and snippets.

@Daniel-Svensson
Created June 16, 2022 17:04
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 Daniel-Svensson/e53207f0092aa76ca4480941f23702c2 to your computer and use it in GitHub Desktop.
Save Daniel-Svensson/e53207f0092aa76ca4480941f23702c2 to your computer and use it in GitHub Desktop.
DataContract_GetId
using System;
using System.Collections.Concurrent;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Security.Cryptography;
using System.Xml;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using static System.Runtime.Serialization.DataContract;
namespace MyBenchmarks
{
//[SimpleJob(launchCount: 1, warmupCount: 3, targetCount: 30)]
// [MediumRunJob]
public class Md5VsSha256
{
private static Dictionary<TypeHandleRef, IntRef> s_typeToIDCache = new(new TypeHandleRefEqualityComparer());
private static ConcurrentDictionary<TypeHandleRef, IntRef> s_typeToIDCacheConcurrent = new(new TypeHandleRefEqualityComparer());
private static ConcurrentDictionary<TypeHandleRef, Lazy<int>> s_typeToIDCacheConcurrent2 = new(new TypeHandleRefEqualityComparer());
private static ConcurrentDictionary<RuntimeTypeHandle, Lazy<int>> s_typeToIDCacheConcurrent3 = new(new RuntimeTypeHandleEqualityComparer());
private static ConcurrentDictionary<RuntimeTypeHandle, Lazy<int>> s_typeToIDCacheConcurrent4 = new();
private static DataContract[] s_dataContractCache = new DataContract[32];
private static int s_dataContractID;
private static ConcurrentDictionary<Type, DataContract?> s_typeToBuiltInContract = new();
private static Dictionary<XmlQualifiedName, DataContract?>? s_nameToBuiltInContract;
private static Dictionary<string, string>? s_namespaces;
private static Dictionary<string, XmlDictionaryString>? s_clrTypeStrings;
private static XmlDictionary? s_clrTypeStringsDictionary;
[ThreadStatic]
private static TypeHandleRef? s_typeHandleRefPerThread;
private static readonly TypeHandleRef? s_typeHandleRef = new TypeHandleRef();
private static readonly object s_cacheLock = new object();
private static readonly object s_createDataContractLock = new object();
private static readonly object s_initBuiltInContractsLock = new object();
private static readonly object s_namespacesLock = new object();
private static readonly object s_clrTypeStringsLock = new object();
private static readonly ReaderWriterLockSlim readerWriterLockSlim = new ReaderWriterLockSlim();
private readonly RuntimeTypeHandle[] _typeHandles;
private static TypeHandleRef GetThreadLocalTypeHandleRef(RuntimeTypeHandle runtimeTypeHandle)
{
var typeHandleRef = (s_typeHandleRefPerThread ??= new TypeHandleRef());
typeHandleRef.Value = runtimeTypeHandle;
return typeHandleRef;
}
public Md5VsSha256()
{
_typeHandles = typeof(Program).Assembly.ExportedTypes
.Concat(typeof(int).Assembly.ExportedTypes.Where(s => s.IsPrimitive))
.Select(t => t.TypeHandle)
.ToArray();
}
[GlobalSetup]
public void ResetCaches()
{
s_typeToIDCache = new(new TypeHandleRefEqualityComparer());
s_typeToIDCacheConcurrent = new(new TypeHandleRefEqualityComparer());
s_typeToIDCacheConcurrent2 = new(new TypeHandleRefEqualityComparer());
s_typeToIDCacheConcurrent3 = new(new RuntimeTypeHandleEqualityComparer());
s_typeToIDCacheConcurrent4 = new();
s_dataContractCache = new DataContract[32];
s_dataContractID = 0;
}
[Benchmark]
public int Original()
{
int sum = 0;
for (int i = 0; i < _typeHandles.Length; ++i)
for (int j = 0; j <= i; ++j)
sum += GetIdOriginal(_typeHandles[j]);
return sum;
}
[Benchmark]
public int Id2()
{
int sum = 0;
for (int i = 0; i < _typeHandles.Length; ++i)
for (int j = 0; j <= i; ++j)
sum += GetId2Original(_typeHandles[j]);
return sum;
}
[Benchmark]
public int Lazy()
{
int sum = 0;
for (int i = 0; i < _typeHandles.Length; ++i)
for (int j = 0; j <= i; ++j)
sum += GetIdLazy(_typeHandles[j]);
return sum;
}
[Benchmark]
public int LazyAndValueKey()
{
int sum = 0;
for (int i = 0; i < _typeHandles.Length; ++i)
for (int j = 0; j <= i; ++j)
sum += GetIdLazyValueType(_typeHandles[j]);
return sum;
}
[Benchmark]
public int LazyAndValueKeyWithEqualsComparer()
{
int sum = 0;
for (int i = 0; i < _typeHandles.Length; ++i)
for (int j = 0; j <= i; ++j)
sum += GetIdLazyValueTypeEqualsComparer(_typeHandles[j]);
return sum;
}
[Benchmark]
public int Faulty()
{
int sum = 0;
for (int i = 0; i < _typeHandles.Length; ++i)
for (int j = 0; j <= i; ++j)
sum += GetId(_typeHandles[j]);
return sum;
}
[Benchmark]
public int RwlockSlim()
{
int sum = 0;
for (int i = 0; i < _typeHandles.Length; ++i)
for (int j = 0; j <= i; ++j)
sum += GetIdRWSlim(_typeHandles[j]);
return sum;
}
private int GetIdRWSlim(RuntimeTypeHandle typeHandle)
{
IntRef? id;
var typeHandleRef = GetThreadLocalTypeHandleRef(typeHandle);
readerWriterLockSlim.EnterReadLock();
try
{
if (s_typeToIDCache.TryGetValue(typeHandleRef, out id))
return id.Value;
}
finally
{
readerWriterLockSlim.ExitReadLock();
}
readerWriterLockSlim.EnterWriteLock();
try
{
ref IntRef? idRef
= ref CollectionsMarshal.GetValueRefOrAddDefault(s_typeToIDCache, typeHandleRef, out bool exists);
if (idRef == null)
{
int value = s_dataContractID++;
if (value >= s_dataContractCache.Length)
{
int newSize = (value < int.MaxValue / 2) ? value * 2 : int.MaxValue;
if (newSize <= value)
{
DiagnosticUtility.DebugAssert("DataContract cache overflow");
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SerializationException(SR.DataContractCacheOverflow));
}
Array.Resize<DataContract>(ref s_dataContractCache, newSize);
}
idRef = new IntRef(value);
return value;
}
return idRef.Value;
}
finally
{
readerWriterLockSlim.ExitWriteLock();
}
}
internal static int GetIdOriginal(RuntimeTypeHandle typeHandle)
{
lock (s_cacheLock)
{
IntRef? id;
s_typeHandleRef.Value = typeHandle;
if (!s_typeToIDCache.TryGetValue(s_typeHandleRef, out id))
{
int value = s_dataContractID++;
if (value >= s_dataContractCache.Length)
{
int newSize = (value < int.MaxValue / 2) ? value * 2 : int.MaxValue;
if (newSize <= value)
{
DiagnosticUtility.DebugAssert("DataContract cache overflow");
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SerializationException(SR.DataContractCacheOverflow));
}
Array.Resize<DataContract>(ref s_dataContractCache, newSize);
}
id = new IntRef(value);
try
{
s_typeToIDCache.Add(new TypeHandleRef(typeHandle), id);
}
catch (Exception ex)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperFatal(ex.Message, ex);
}
}
return id.Value;
}
}
internal static int GetId2Original(RuntimeTypeHandle typeHandle)
{
lock (s_cacheLock)
{
s_typeHandleRef.Value = typeHandle;
ref IntRef? idRef
= ref CollectionsMarshal.GetValueRefOrAddDefault(s_typeToIDCache, s_typeHandleRef, out bool exists);
if (!exists)
{
int value = s_dataContractID++;
if (value >= s_dataContractCache.Length)
{
int newSize = (value < int.MaxValue / 2) ? value * 2 : int.MaxValue;
if (newSize <= value)
{
DiagnosticUtility.DebugAssert("DataContract cache overflow");
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SerializationException(SR.DataContractCacheOverflow));
}
Array.Resize<DataContract>(ref s_dataContractCache, newSize);
}
idRef = new IntRef(value);
}
return idRef.Value;
}
}
private static int GetId(RuntimeTypeHandle typeHandle)
{
var typeHandleRef = GetThreadLocalTypeHandleRef(typeHandle);
try
{
var id = s_typeToIDCacheConcurrent.GetOrAdd(typeHandleRef, static (key) =>
{
lock (s_cacheLock)
{
int value = s_dataContractID++;
if (value >= s_dataContractCache.Length)
{
int newSize = (value < int.MaxValue / 2) ? value * 2 : int.MaxValue;
if (newSize <= value)
{
throw new NotImplementedException();
}
Array.Resize<DataContract>(ref s_dataContractCache, newSize);
}
return new IntRef(value);
}
});
return id.Value;
}
catch (Exception)
{
throw;
}
}
private static int GetIdLazy(RuntimeTypeHandle typeHandle)
{
var typeHandleRef = GetThreadLocalTypeHandleRef(typeHandle);
try
{
var lazy = s_typeToIDCacheConcurrent2.GetOrAdd(typeHandleRef, static (key) =>
{
return new Lazy<int>(() =>
{
lock (s_cacheLock)
{
int value = s_dataContractID++;
if (value >= s_dataContractCache.Length)
{
int newSize = (value < int.MaxValue / 2) ? value * 2 : int.MaxValue;
if (newSize <= value)
{
throw new NotImplementedException();
}
Array.Resize<DataContract>(ref s_dataContractCache, newSize);
}
return value;
}
}, isThreadSafe: true);
});
return lazy.Value;
}
catch (Exception)
{
throw;
}
}
private static int GetIdLazyValueType(RuntimeTypeHandle typeHandle)
{
try
{
var lazy = s_typeToIDCacheConcurrent4.GetOrAdd(typeHandle, static (key) =>
{
return new Lazy<int>(() =>
{
lock (s_cacheLock)
{
int value = s_dataContractID++;
if (value >= s_dataContractCache.Length)
{
int newSize = (value < int.MaxValue / 2) ? value * 2 : int.MaxValue;
if (newSize <= value)
{
throw new NotImplementedException();
}
Array.Resize<DataContract>(ref s_dataContractCache, newSize);
}
return value;
}
}, isThreadSafe: true);
});
return lazy.Value;
}
catch (Exception)
{
throw;
}
}
private static int GetIdLazyValueTypeEqualsComparer(RuntimeTypeHandle typeHandle)
{
try
{
var lazy = s_typeToIDCacheConcurrent3.GetOrAdd(typeHandle, static (key) =>
{
return new Lazy<int>(() =>
{
lock (s_cacheLock)
{
int value = s_dataContractID++;
if (value >= s_dataContractCache.Length)
{
int newSize = (value < int.MaxValue / 2) ? value * 2 : int.MaxValue;
if (newSize <= value)
{
throw new NotImplementedException();
}
Array.Resize<DataContract>(ref s_dataContractCache, newSize);
}
return value;
}
}, isThreadSafe: true);
});
return lazy.Value;
}
catch (Exception)
{
throw;
}
}
}
public class Program
{
public static void Main(string[] args)
{
var summary = BenchmarkRunner.Run<Md5VsSha256>();
}
}
public class DataContract { }
internal sealed class RuntimeTypeHandleEqualityComparer : IEqualityComparer<RuntimeTypeHandle>
{
public bool Equals(RuntimeTypeHandle x, RuntimeTypeHandle y)
{
return x.Equals(y);
}
public int GetHashCode(RuntimeTypeHandle obj)
{
return obj.GetHashCode();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment