Created
June 16, 2022 17:04
-
-
Save Daniel-Svensson/e53207f0092aa76ca4480941f23702c2 to your computer and use it in GitHub Desktop.
DataContract_GetId
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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