Performance Fish

This commit is contained in:
bbradson
2022-10-24 18:24:02 +02:00
parent 594b32b4ef
commit 2efa8e466c
69 changed files with 9147 additions and 3 deletions
+196
View File
@@ -0,0 +1,196 @@
// Copyright (c) 2022 bradson
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace PerformanceFish.Cache;
public class ByIndex<T_out> : IList<T_out?>, ICollection
{
private static ByIndex<T_out> _get = new();
[ThreadStatic]
private static ByIndex<T_out>? _getThreadStatic;
public static ByIndex<T_out> Get
=> /*UnityData.IsInMainThread ? _get
:*/ _getThreadStatic ??= Utility.AddNew<ByIndex<T_out>>();
public static ByIndex<T_out> GetDirectly => _get;
static ByIndex() => Utility.All.Add(_get);
protected T_out?[] _items = Array.Empty<T_out>();
protected object? _syncRoot;
public T_out? this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
ResizeIfNecessary(index);
return _items[index];
}
set
{
ResizeIfNecessary(index);
_items[index] = value;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[return: MaybeNull]
public ref T_out GetReference(int index)
{
ResizeIfNecessary(index);
return ref _items[index];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetValue(int index, out T_out? value)
{
value = this[index];
return !value!.Equals<T_out>(default!);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected void ResizeIfNecessary(int checkedIndex)
{
if (checkedIndex >= _items.Length)
ResizeFor(checkedIndex);
}
protected void ResizeFor(int index)
{
Array.Resize(ref _items, Mathf.NextPowerOfTwo(index + 1));
ResizeIfNecessary(index); // just in case
}
public int Count => _items.Length;
public bool IsReadOnly => false;
object ICollection.SyncRoot
=> _syncRoot
??= Interlocked.CompareExchange<object>(ref _syncRoot!, new(), null!);
bool ICollection.IsSynchronized => false;
[DoesNotReturn]
public void Add(T_out? item) => throw new NotSupportedException();
public void Clear()
{
if (_items.Length > 0)
Array.Clear(_items, 0, _items.Length);
}
public bool Contains(T_out? item) => Array.IndexOf(_items, item) >= 0;
public void CopyTo(T_out?[] array, int arrayIndex) => _items.CopyTo(array, arrayIndex);
public IEnumerator<T_out?> GetEnumerator() => _items.AsEnumerable().GetEnumerator();
public int IndexOf(T_out? item) => Array.IndexOf(_items, item);
public void Insert(int index, T_out? item) => this[index] = item;
[DoesNotReturn]
public bool Remove(T_out? item) => throw new NotSupportedException();
public void RemoveAt(int index)
{
if (index < Count)
_items[index] = default;
}
IEnumerator IEnumerable.GetEnumerator() => _items.GetEnumerator();
void ICollection.CopyTo(Array array, int index) => _items.CopyTo(array, index);
}
public class ByIndex<T_in, T_out> : ByIndex<T_out>
where T_in : notnull
{
private static ByIndex<T_in, T_out> _get = new();
[ThreadStatic]
private static ByIndex<T_in, T_out>? _getThreadStatic;
public new static ByIndex<T_in, T_out> Get
=> /*UnityData.IsInMainThread ? _get
:*/ _getThreadStatic ??= Utility.AddNew<ByIndex<T_in, T_out>>();
public new static ByIndex<T_in, T_out> GetDirectly => _get;
static ByIndex() => Utility.All.Add(_get);
public unsafe T_out? this[T_in key]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this[FunctionPointers.IndexGetter<T_in>.Default(key)];
set => this[FunctionPointers.IndexGetter<T_in>.Default(key)] = value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetValue(T_in key, [NotNullWhen(true)] out T_out? value)
{
value = this[key];
return !value!.Equals<T_out>(default!);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[return: MaybeNull]
public unsafe ref T_out GetReference(T_in key)
=> ref GetReference(FunctionPointers.IndexGetter<T_in>.Default(key))!;
}
/// <summary>
/// Seems slower than the dictionary based Cache.ByInt.
/// </summary>
public class ByIndex<T_first, T_second, T_out> : ByIndex<T_first, ByIndex<T_second, T_out>>
where T_first : notnull where T_second : notnull
{
private static ByIndex<T_first, T_second, T_out> _get = new();
[ThreadStatic]
private static ByIndex<T_first, T_second, T_out>? _getThreadStatic;
public new static ByIndex<T_first, T_second, T_out> Get
=> /*UnityData.IsInMainThread ? _get
:*/ _getThreadStatic ??= Utility.AddNew<ByIndex<T_first, T_second, T_out>>();
public new static ByIndex<T_first, T_second, T_out> GetDirectly => _get;
static ByIndex() => Utility.All.Add(_get);
public T_out? this[T_first first, T_second second]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => GetInner(first)[second];
set => GetInner(first)[second] = value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private unsafe ByIndex<T_second, T_out> GetInner(T_first first)
{
var firstIndex = FunctionPointers.IndexGetter<T_first>.Default(first);
ResizeIfNecessary(firstIndex);
ref var inner = ref _items[firstIndex];
inner ??= new();
return inner!;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[return: MaybeNull]
public ref T_out GetReference(T_first first, T_second second)
=> ref GetInner(first).GetReference(second)!;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetValue(T_first first, T_second second, [NotNullWhen(true)] out T_out? value)
{
value = this[first, second];
return !value!.Equals<T_out>(default!);
}
}
+291
View File
@@ -0,0 +1,291 @@
// Copyright (c) 2022 bradson
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
using System.Diagnostics.CodeAnalysis;
namespace PerformanceFish.Cache;
public struct ByInt<T_in, T_result>
where T_in : notnull
where T_result : IIsRefreshable<T_in, T_result>
{
private static Dictionary<ByInt<T_in, T_result>, T_result> _get = new();
[ThreadStatic]
private static Dictionary<ByInt<T_in, T_result>, T_result>? _getThreadStatic;
public static Dictionary<ByInt<T_in, T_result>, T_result> Get
=> /*UnityData.IsInMainThread ? _get
:*/ _getThreadStatic ??= Utility.AddNew<Dictionary<ByInt<T_in, T_result>, T_result>>();
public static Dictionary<ByInt<T_in, T_result>, T_result> GetDirectly => _get;
static ByInt() => Utility.All.Add(_get);
public int Key;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe ByInt(T_in key) => Key = FunctionPointers.IndexGetter<T_in>.Default(key);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T_result GetValue(T_in key)
{
ref var cache = ref Get.TryGetReferenceUnsafe(new(key));
if (Unsafe.IsNullRef(ref cache))
{
Get[new(key)] = Reflection.New<T_result>();
cache = ref Get.GetReference(new(key))!;
goto RefreshCache;
}
if (!cache.ShouldRefreshNow)
return ref cache!;
RefreshCache:
cache.ShouldRefreshNow = false;
Get[new(key)] = cache.SetNewValue(key);
return ref cache!;
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static unsafe ref T_result TryGetReferenceUnsafe(int key)
=> ref Unsafe.AsRef<T_result>(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref Unsafe.As<int, ByInt<T_in, T_result>>(ref key))));
[MethodImpl(MethodImplOptions.NoInlining)]
public static unsafe ref T_result TryGetReferenceChecked(int key, out bool result)
{
ref var value = ref Unsafe.AsRef<T_result>(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref Unsafe.As<int, ByInt<T_in, T_result>>(ref key))));
result = !Unsafe.IsNullRef(ref value) && !value.ShouldRefreshNow;
return ref value!;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryGetValue(T_in key, [MaybeNullWhen(false)] out T_result value)
=> Get.TryGetValue(new(key), out value)
&& !value.ShouldRefreshNow;
public static T_result GetValueAndRefreshNow(T_in key)
{
var cache = Get.TryGetValue(new(key));
cache ??= Reflection.New<T_result>();
cache.ShouldRefreshNow = false;
Get[new(key)] = cache.SetNewValue(key);
return cache;
}
}
public struct ByIntRefreshable<T_first, T_second, T_result> : IEquatable<ByIntRefreshable<T_first, T_second, T_result>>
where T_first : notnull where T_second : notnull
where T_result : IIsRefreshable<ByIntRefreshable<T_first, T_second, T_result>, T_result>
{
private static EqualityComparer<ByIntRefreshable<T_first, T_second, T_result>> _comparer = new Comparer();
private class Comparer : EqualityComparer<ByIntRefreshable<T_first, T_second, T_result>>
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(ByIntRefreshable<T_first, T_second, T_result> x, ByIntRefreshable<T_first, T_second, T_result> y)
=> x.First == y.First
&& x.Second == y.Second;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode(ByIntRefreshable<T_first, T_second, T_result> obj)
=> HashCode.Combine(obj.First, obj.Second);
}
private static Dictionary<ByIntRefreshable<T_first, T_second, T_result>, T_result> _get = new(_comparer);
[ThreadStatic]
private static Dictionary<ByIntRefreshable<T_first, T_second, T_result>, T_result>? _getThreadStatic;
public static Dictionary<ByIntRefreshable<T_first, T_second, T_result>, T_result> Get
=> /*UnityData.IsInMainThread ? _get
:*/ _getThreadStatic ??= Utility.AddNew<Dictionary<ByIntRefreshable<T_first, T_second, T_result>, T_result>>(_comparer);
public static Dictionary<ByIntRefreshable<T_first, T_second, T_result>, T_result> GetDirectly => _get;
static ByIntRefreshable() => Utility.All.Add(_get);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe ref T_result GetValue(ref ByIntRefreshable<T_first, T_second, T_result> key)
{
// CS8347 without this crap
ref var cache = ref Unsafe.AsRef<T_result>(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref key)));
if (Unsafe.IsNullRef(ref cache))
{
Get[key] = Reflection.New<T_result>();
cache = ref Unsafe.AsRef<T_result>(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref key)))!;
goto RefreshCache;
}
if (!cache.ShouldRefreshNow)
return ref cache!;
RefreshCache:
cache.ShouldRefreshNow = false;
Get[key] = cache.SetNewValue(key);
return ref cache!;
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static unsafe ref T_result TryGetReferenceUnsafe(int second, int first)
=> ref Unsafe.AsRef<T_result>(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref Unsafe.As<int, ByIntRefreshable<T_first, T_second, T_result>>(ref first))));
[MethodImpl(MethodImplOptions.NoInlining)]
public static unsafe ref T_result TryGetReferenceChecked(int second, int first, out bool result)
{
ref var value = ref Unsafe.AsRef<T_result>(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref Unsafe.As<int, ByIntRefreshable<T_first, T_second, T_result>>(ref first))));
result = !Unsafe.IsNullRef(ref value) && !value.ShouldRefreshNow;
return ref value!;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryGetValue(ByIntRefreshable<T_first, T_second, T_result> key, [MaybeNullWhen(false)] out T_result value)
=> Get.TryGetValue(key, out value)
&& !value.ShouldRefreshNow;
public static T_result GetValueAndRefreshNow(ByIntRefreshable<T_first, T_second, T_result> key)
{
var cache = Get.TryGetValue(ref key);
cache ??= Reflection.New<T_result>();
cache.ShouldRefreshNow = false;
Get[key] = cache.SetNewValue(key);
return cache;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe ByIntRefreshable(T_first first, T_second second)
{
First = FunctionPointers.IndexGetter<T_first>.Default(first);
Second = FunctionPointers.IndexGetter<T_second>.Default(second);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ByIntRefreshable(int first, int second)
{
First = first;
Second = second;
}
public int First;
public int Second;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(ByIntRefreshable<T_first, T_second, T_result> other)
=> First == other.First
&& Second == other.Second;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode()
=> HashCode.Combine(First, Second);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
=> obj is ByIntRefreshable<T_first, T_second, T_result> cache
&& Equals(cache);
public static bool operator ==(ByIntRefreshable<T_first, T_second, T_result> left, ByIntRefreshable<T_first, T_second, T_result> right)
=> left.Equals(right);
public static bool operator !=(ByIntRefreshable<T_first, T_second, T_result> left, ByIntRefreshable<T_first, T_second, T_result> right)
=> !(left == right);
}
public struct ByInt<T_first, T_second, T_third, T_result> : IEquatable<ByInt<T_first, T_second, T_third, T_result>>
where T_first : notnull where T_second : notnull where T_third : notnull
where T_result : IIsRefreshable<ByInt<T_first, T_second, T_third, T_result>, T_result>
{
private static Dictionary<ByInt<T_first, T_second, T_third, T_result>, T_result> _get = new();
[ThreadStatic]
private static Dictionary<ByInt<T_first, T_second, T_third, T_result>, T_result>? _getThreadStatic;
public static Dictionary<ByInt<T_first, T_second, T_third, T_result>, T_result> Get
=> /*UnityData.IsInMainThread ? _get
:*/ _getThreadStatic ??= Utility.AddNew<Dictionary<ByInt<T_first, T_second, T_third, T_result>, T_result>>();
public static Dictionary<ByInt<T_first, T_second, T_third, T_result>, T_result> GetDirectly => _get;
static ByInt() => Utility.All.Add(_get);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe ref T_result GetValue(ByInt<T_first, T_second, T_third, T_result> key)
{
ref var cache = ref Unsafe.AsRef<T_result>(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref key)));
if (Unsafe.IsNullRef(ref cache))
{
Get[key] = Reflection.New<T_result>();
cache = ref Unsafe.AsRef<T_result>(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref key)))!;
goto RefreshCache;
}
if (!cache.ShouldRefreshNow)
return ref cache!;
RefreshCache:
cache.ShouldRefreshNow = false;
Get[key] = cache.SetNewValue(key);
return ref cache!;
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static unsafe ref T_result TryGetReferenceUnsafe(int second, int first, int third)
=> ref Unsafe.AsRef<T_result>(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref Unsafe.As<int, ByInt<T_first, T_second, T_third, T_result>>(ref first))));
[MethodImpl(MethodImplOptions.NoInlining)]
public static unsafe ref T_result TryGetReferenceChecked(int second, int first, out bool result)
{
ref var value = ref Unsafe.AsRef<T_result>(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref Unsafe.As<int, ByInt<T_first, T_second, T_third, T_result>>(ref first))));
result = !Unsafe.IsNullRef(ref value) && !value.ShouldRefreshNow;
return ref value!;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryGetValue(ByInt<T_first, T_second, T_third, T_result> key, out T_result value)
=> Get.TryGetValue(key, out value)
&& !value.ShouldRefreshNow;
public static T_result GetValueAndRefreshNow(ByInt<T_first, T_second, T_third, T_result> key)
{
var cache = Get.TryGetValue(ref key);
cache ??= Reflection.New<T_result>();
cache.ShouldRefreshNow = false;
Get[key] = cache.SetNewValue(key);
return cache;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe ByInt(T_first first, T_second second, T_third third)
{
First = FunctionPointers.IndexGetter<T_first>.Default(first);
Second = FunctionPointers.IndexGetter<T_second>.Default(second);
Third = FunctionPointers.IndexGetter<T_third>.Default(third);
}
public int First;
public int Second;
public int Third;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(ByInt<T_first, T_second, T_third, T_result> other)
=> First == other.First
&& Second == other.Second
&& Third == other.Third;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode()
=> HashCode.Combine(First, Second, Third);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
=> obj is ByInt<T_first, T_second, T_third, T_result> cache
&& Equals(cache);
public static bool operator ==(ByInt<T_first, T_second, T_third, T_result> left, ByInt<T_first, T_second, T_third, T_result> right)
=> left.Equals(right);
public static bool operator !=(ByInt<T_first, T_second, T_third, T_result> left, ByInt<T_first, T_second, T_third, T_result> right)
=> !(left == right);
}
+46
View File
@@ -0,0 +1,46 @@
// Copyright (c) 2022 bradson
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
namespace PerformanceFish.Cache;
public static class ByMap<T> where T : new()
{
private static List<(Map map, T value)> _cache = new();
[ThreadStatic]
private static List<(Map map, T value)>? _cacheThreadStatic;
public static List<(Map map, T value)> GetCache
=> UnityData.IsInMainThread ? _cache
: _cacheThreadStatic ??= Utility.AddNew<List<(Map map, T value)>>();
public static List<(Map map, T value)> GetCacheDirectly => _cache;
static ByMap() => Utility.All.Add(_cache);
private static Func<Map, T> Initialize { get; }
= typeof(MapComponent).IsAssignableFrom(typeof(T))
? AccessTools.MethodDelegate<Func<Map, T>>(AccessTools.Method(typeof(Map), nameof(Map.GetComponent), Array.Empty<Type>(), new[] { typeof(T) }))
// missing T constraints cause the compiler to complain when trying to just use map => map.GetComponent<T>
: _ => new();
public static T GetFor(Map map)
{
var cache = GetCache;
for (var i = 0; i < cache.Count; i++)
{
var cacheEntry = cache[i];
if (cacheEntry.map == map)
return cacheEntry.value;
if (Find.Maps.Count <= i + 1
&& !Find.Maps.Contains(cacheEntry.map))
{
cache.RemoveAt(i);
i--;
}
}
cache.Add((map, Initialize(map)));
return cache[^1].value;
}
}
+271
View File
@@ -0,0 +1,271 @@
// Copyright (c) 2022 bradson
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
namespace PerformanceFish.Cache;
public static class ByReference<T_first, T_result>
where T_first : notnull
{
private static Dictionary<T_first, T_result> _get = new();
[ThreadStatic]
private static Dictionary<T_first, T_result>? _getThreadStatic;
public static Dictionary<T_first, T_result> Get
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
=> /*UnityData.IsInMainThread ? _get
:*/ _getThreadStatic ??= Utility.AddNew<Dictionary<T_first, T_result>>();
}
public static Dictionary<T_first, T_result> GetDirectly => _get;
static ByReference() => Utility.All.Add(_get);
public static unsafe void Initialize()
{
try
{
_ = FisheryLib.FunctionPointers.Equals<T_first>.Default;
_ = FisheryLib.FunctionPointers.GetHashCode<T_first>.Default;
}
catch (Exception ex)
{
Log.Error($"Performance Fish encountered an exception while trying to initialize {typeof(ByReference<T_first, T_result>).FullDescription()}\n{ex}");
}
}
}
public struct ByReference<T_first, T_second, T_result> : IEquatable<ByReference<T_first, T_second, T_result>>
where T_first : notnull where T_second : notnull
{
private static Dictionary<ByReference<T_first, T_second, T_result>, T_result> _get = new();
[ThreadStatic]
private static Dictionary<ByReference<T_first, T_second, T_result>, T_result>? _getThreadStatic;
public static Dictionary<ByReference<T_first, T_second, T_result>, T_result> Get
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
=> /*UnityData.IsInMainThread ? _get
:*/ _getThreadStatic ??= Utility.AddNew<Dictionary<ByReference<T_first, T_second, T_result>, T_result>>();
}
public static Dictionary<ByReference<T_first, T_second, T_result>, T_result> GetDirectly => _get;
static ByReference() => Utility.All.Add(_get);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ByReference(T_first first, T_second second)
{
First = first;
Second = second;
}
public static unsafe void Initialize()
{
try
{
_ = FisheryLib.FunctionPointers.Equals<T_first>.Default;
_ = FisheryLib.FunctionPointers.Equals<T_second>.Default;
_ = FisheryLib.FunctionPointers.Equals<ByReference<T_first, T_second, T_result>>.Default;
_ = FisheryLib.FunctionPointers.GetHashCode<T_first>.Default;
_ = FisheryLib.FunctionPointers.GetHashCode<T_second>.Default;
_ = FisheryLib.FunctionPointers.GetHashCode<ByReference<T_first, T_second, T_result>>.Default;
}
catch (Exception ex)
{
Log.Error($"Performance Fish encountered an exception while trying to initialize {typeof(ByReference<T_first, T_result>).FullDescription()}\n{ex}");
}
}
public T_first First;
public T_second Second;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(ByReference<T_first, T_second, T_result> other)
=> First.Equals<T_first>(other.First)
&& Second.Equals<T_second>(other.Second);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode()
=> HashCode.Combine(First, Second);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
=> obj is ByReference<T_first, T_second, T_result> cache
&& Equals(cache);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(ByReference<T_first, T_second, T_result> left, ByReference<T_first, T_second, T_result> right)
=> left.Equals(right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(ByReference<T_first, T_second, T_result> left, ByReference<T_first, T_second, T_result> right)
=> !(left == right);
}
public struct ByReference<T_first, T_second, T_third, T_result> : IEquatable<ByReference<T_first, T_second, T_third, T_result>>
where T_first : notnull where T_second : notnull where T_third : notnull
{
private static Dictionary<ByReference<T_first, T_second, T_third, T_result>, T_result> _get = new();
[ThreadStatic]
private static Dictionary<ByReference<T_first, T_second, T_third, T_result>, T_result>? _getThreadStatic;
public static Dictionary<ByReference<T_first, T_second, T_third, T_result>, T_result> Get
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
/*UnityData.IsInMainThread ? _get
:*/ => _getThreadStatic ??= Utility.AddNew<Dictionary<ByReference<T_first, T_second, T_third, T_result>, T_result>>();
}
public static Dictionary<ByReference<T_first, T_second, T_third, T_result>, T_result> GetDirectly => _get;
static ByReference() => Utility.All.Add(_get);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ByReference(T_first first, T_second second, T_third third)
{
First = first;
Second = second;
Third = third;
_hashCode = HashCode.Combine(first, second, third);
}
public static unsafe void Initialize()
{
try
{
_ = FisheryLib.FunctionPointers.Equals<T_first>.Default;
_ = FisheryLib.FunctionPointers.Equals<T_second>.Default;
_ = FisheryLib.FunctionPointers.Equals<T_third>.Default;
_ = FisheryLib.FunctionPointers.Equals<ByReference<T_first, T_second, T_third, T_result>>.Default;
_ = FisheryLib.FunctionPointers.GetHashCode<T_first>.Default;
_ = FisheryLib.FunctionPointers.GetHashCode<T_second>.Default;
_ = FisheryLib.FunctionPointers.GetHashCode<T_third>.Default;
_ = FisheryLib.FunctionPointers.GetHashCode<ByReference<T_first, T_second, T_third, T_result>>.Default;
}
catch (Exception ex)
{
Log.Error($"Performance Fish encountered an exception while trying to initialize {typeof(ByReference<T_first, T_second, T_result>).FullDescription()}\n{ex}");
}
}
public T_first First;
public T_second Second;
public T_third Third;
private int _hashCode;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(ByReference<T_first, T_second, T_third, T_result> other)
=> First.Equals<T_first>(other.First)
&& Second.Equals<T_second>(other.Second)
&& Third.Equals<T_third>(other.Third);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() => _hashCode;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
=> obj is ByReference<T_first, T_second, T_third, T_result> cache
&& Equals(cache);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(ByReference<T_first, T_second, T_third, T_result> left, ByReference<T_first, T_second, T_third, T_result> right)
=> left.Equals(right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(ByReference<T_first, T_second, T_third, T_result> left, ByReference<T_first, T_second, T_third, T_result> right)
=> !(left == right);
}
public struct ByReference<T_first, T_second, T_third, T_fourth, T_result> : IEquatable<ByReference<T_first, T_second, T_third, T_fourth, T_result>>
where T_first : notnull where T_second : notnull where T_third : notnull where T_fourth : notnull
{
private static Dictionary<ByReference<T_first, T_second, T_third, T_fourth, T_result>, T_result> _get = new();
[ThreadStatic]
private static Dictionary<ByReference<T_first, T_second, T_third, T_fourth, T_result>, T_result>? _getThreadStatic;
public static Dictionary<ByReference<T_first, T_second, T_third, T_fourth, T_result>, T_result> Get
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
/*UnityData.IsInMainThread ? _get
:*/ => _getThreadStatic ??= Utility.AddNew<Dictionary<ByReference<T_first, T_second, T_third, T_fourth, T_result>, T_result>>();
}
public static Dictionary<ByReference<T_first, T_second, T_third, T_fourth, T_result>, T_result> GetDirectly => _get;
static ByReference() => Utility.All.Add(_get);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ByReference(T_first first, T_second second, T_third third, T_fourth fourth)
{
First = first;
Second = second;
Third = third;
Fourth = fourth;
_hashCode = HashCode.Combine(first, second, third, fourth);
}
public static unsafe void Initialize()
{
try
{
_ = FisheryLib.FunctionPointers.Equals<T_first>.Default;
_ = FisheryLib.FunctionPointers.Equals<T_second>.Default;
_ = FisheryLib.FunctionPointers.Equals<T_third>.Default;
_ = FisheryLib.FunctionPointers.Equals<T_fourth>.Default;
_ = FisheryLib.FunctionPointers.Equals<ByReference<T_first, T_second, T_third, T_fourth, T_result>>.Default;
_ = FisheryLib.FunctionPointers.GetHashCode<T_first>.Default;
_ = FisheryLib.FunctionPointers.GetHashCode<T_second>.Default;
_ = FisheryLib.FunctionPointers.GetHashCode<T_third>.Default;
_ = FisheryLib.FunctionPointers.GetHashCode<T_fourth>.Default;
_ = FisheryLib.FunctionPointers.GetHashCode<ByReference<T_first, T_second, T_third, T_fourth, T_result>>.Default;
}
catch (Exception ex)
{
Log.Error($"Performance Fish encountered an exception while trying to initialize {typeof(ByReference<T_first, T_second, T_third, T_result>).FullDescription()}\n{ex}");
}
}
public T_first First;
public T_second Second;
public T_third Third;
public T_fourth Fourth;
private int _hashCode;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(ByReference<T_first, T_second, T_third, T_fourth, T_result> other)
=> First.Equals<T_first>(other.First)
&& Second.Equals<T_second>(other.Second)
&& Third.Equals<T_third>(other.Third)
&& Fourth.Equals<T_fourth>(other.Fourth);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() => _hashCode;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
=> obj is ByReference<T_first, T_second, T_third, T_fourth, T_result> cache
&& Equals(cache);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(ByReference<T_first, T_second, T_third, T_fourth, T_result> left, ByReference<T_first, T_second, T_third, T_fourth, T_result> right)
=> left.Equals(right);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(ByReference<T_first, T_second, T_third, T_fourth, T_result> left, ByReference<T_first, T_second, T_third, T_fourth, T_result> right)
=> !(left == right);
}
@@ -0,0 +1,218 @@
// Copyright (c) 2022 bradson
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
namespace PerformanceFish.Cache;
public static class ByReferenceRefreshable<T_first, T_result>
where T_first : notnull
where T_result : IIsRefreshable<T_first, T_result>
{
private static Dictionary<T_first, T_result> _get = new();
[ThreadStatic]
private static Dictionary<T_first, T_result>? _getThreadStatic;
public static Dictionary<T_first, T_result> Get
=> /*UnityData.IsInMainThread ? _get
:*/ _getThreadStatic ??= Utility.AddNew<Dictionary<T_first, T_result>>();
public static Dictionary<T_first, T_result> GetDirectly => _get;
static ByReferenceRefreshable() => Utility.All.Add(_get);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T_result GetValue(T_first key)
{
ref var cache = ref Get.TryGetReferenceUnsafe(key);
if (Unsafe.IsNullRef(ref cache))
{
Get[key] = Reflection.New<T_result>();
cache = ref Get.GetReference(key)!;
goto RefreshCache;
}
if (!cache.ShouldRefreshNow)
return ref cache!;
RefreshCache:
cache.ShouldRefreshNow = false;
Get[key] = cache.SetNewValue(key);
return ref cache!;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryGetValue(T_first key, out T_result value)
=> Get.TryGetValue(key, out value)
&& !value.ShouldRefreshNow;
public static T_result GetValueAndRefreshNow(T_first key)
{
var cache = Get.TryGetValue(key);
cache ??= Reflection.New<T_result>();
cache.ShouldRefreshNow = false;
Get[key] = cache.SetNewValue(key);
return cache;
}
}
public struct ByReferenceRefreshable<T_first, T_second, T_result> : IEquatable<ByReferenceRefreshable<T_first, T_second, T_result>>
where T_first : notnull where T_second : notnull
where T_result : IIsRefreshable<ByReferenceRefreshable<T_first, T_second, T_result>, T_result>
{
internal ByReference<T_first, T_second, T_result> _innerValue;
private static Dictionary<ByReferenceRefreshable<T_first, T_second, T_result>, T_result> _get = new();
[ThreadStatic]
private static Dictionary<ByReferenceRefreshable<T_first, T_second, T_result>, T_result>? _getThreadStatic;
public static Dictionary<ByReferenceRefreshable<T_first, T_second, T_result>, T_result> Get
=> /*UnityData.IsInMainThread ? _get
:*/ _getThreadStatic ??= Utility.AddNew<Dictionary<ByReferenceRefreshable<T_first, T_second, T_result>, T_result>>();
public static Dictionary<ByReferenceRefreshable<T_first, T_second, T_result>, T_result> GetDirectly => _get;
static ByReferenceRefreshable() => Utility.All.Add(_get);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe ref T_result GetValue(ref ByReferenceRefreshable<T_first, T_second, T_result> key)
{
// CS8347 without this crap
ref var cache = ref Unsafe.AsRef<T_result>(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref key)));
if (Unsafe.IsNullRef(ref cache))
{
Get[key] = Reflection.New<T_result>();
cache = ref Unsafe.AsRef<T_result>(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref key)))!;
goto RefreshCache;
}
if (!cache.ShouldRefreshNow)
return ref cache!;
RefreshCache:
cache.ShouldRefreshNow = false;
Get[key] = cache.SetNewValue(key);
return ref cache!;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryGetValue(ByReferenceRefreshable<T_first, T_second, T_result> key, out T_result value)
=> Get.TryGetValue(key, out value)
&& !value.ShouldRefreshNow;
public static T_result GetValueAndRefreshNow(ByReferenceRefreshable<T_first, T_second, T_result> key)
{
var cache = Get.TryGetValue(ref key);
cache ??= Reflection.New<T_result>();
cache.ShouldRefreshNow = false;
Get[key] = cache.SetNewValue(key);
return cache;
}
public ByReferenceRefreshable(T_first first, T_second second) => _innerValue = new(first, second);
public T_first First => _innerValue.First;
public T_second Second => _innerValue.Second;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(ByReferenceRefreshable<T_first, T_second, T_result> other)
=> _innerValue.Equals(other._innerValue);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() => _innerValue.GetHashCode();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
=> obj is ByReferenceRefreshable<T_first, T_second, T_result> cache
&& Equals(cache);
public static bool operator ==(ByReferenceRefreshable<T_first, T_second, T_result> left, ByReferenceRefreshable<T_first, T_second, T_result> right)
=> left.Equals(right);
public static bool operator !=(ByReferenceRefreshable<T_first, T_second, T_result> left, ByReferenceRefreshable<T_first, T_second, T_result> right)
=> !(left == right);
}
public struct ByReferenceRefreshable<T_first, T_second, T_third, T_result> : IEquatable<ByReferenceRefreshable<T_first, T_second, T_third, T_result>>
where T_first : notnull where T_second : notnull where T_third : notnull
where T_result : IIsRefreshable<ByReferenceRefreshable<T_first, T_second, T_third, T_result>, T_result>
{
private ByReference<T_first, T_second, T_third, T_result> _innerValue;
private static Dictionary<ByReferenceRefreshable<T_first, T_second, T_third, T_result>, T_result> _get = new();
[ThreadStatic]
private static Dictionary<ByReferenceRefreshable<T_first, T_second, T_third, T_result>, T_result>? _getThreadStatic;
public static Dictionary<ByReferenceRefreshable<T_first, T_second, T_third, T_result>, T_result> Get
=> /*UnityData.IsInMainThread ? _get
:*/ _getThreadStatic ??= Utility.AddNew<Dictionary<ByReferenceRefreshable<T_first, T_second, T_third, T_result>, T_result>>();
public static Dictionary<ByReferenceRefreshable<T_first, T_second, T_third, T_result>, T_result> GetDirectly => _get;
static ByReferenceRefreshable() => Utility.All.Add(_get);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe ref T_result GetValue(ref ByReferenceRefreshable<T_first, T_second, T_third, T_result> key)
{
ref var cache = ref Unsafe.AsRef<T_result>(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref key)));
if (Unsafe.IsNullRef(ref cache))
{
Get[key] = Activator.CreateInstance<T_result>();
cache = ref Unsafe.AsRef<T_result>(Unsafe.AsPointer(ref Get.TryGetReferenceUnsafe(ref key)))!;
goto RefreshCache;
}
if (!cache.ShouldRefreshNow)
return ref cache!;
RefreshCache:
cache.ShouldRefreshNow = false;
Get[key] = cache.SetNewValue(key);
return ref cache!;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryGetValue(ByReferenceRefreshable<T_first, T_second, T_third, T_result> key, out T_result value)
=> Get.TryGetValue(key, out value)
&& !value.ShouldRefreshNow;
public static T_result GetValueAndRefreshNow(ByReferenceRefreshable<T_first, T_second, T_third, T_result> key)
{
var cache = Get.TryGetValue(ref key);
cache ??= Activator.CreateInstance<T_result>();
cache.ShouldRefreshNow = false;
Get[key] = cache.SetNewValue(key);
return cache;
}
public ByReferenceRefreshable(T_first first, T_second second, T_third third)
=> _innerValue = new(first, second, third);
public T_first First => _innerValue.First;
public T_second Second => _innerValue.Second;
public T_third Third => _innerValue.Third;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(ByReferenceRefreshable<T_first, T_second, T_third, T_result> other)
=> _innerValue.Equals(other._innerValue);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode() => _innerValue.GetHashCode();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
=> obj is ByReferenceRefreshable<T_first, T_second, T_third, T_result> cache
&& Equals(cache);
public static bool operator ==(ByReferenceRefreshable<T_first, T_second, T_third, T_result> left, ByReferenceRefreshable<T_first, T_second, T_third, T_result> right)
=> left.Equals(right);
public static bool operator !=(ByReferenceRefreshable<T_first, T_second, T_third, T_result> left, ByReferenceRefreshable<T_first, T_second, T_third, T_result> right)
=> !(left == right);
}
@@ -0,0 +1,16 @@
// Copyright (c) 2022 bradson
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
namespace PerformanceFish.Cache;
public interface IIsRefreshable<T, V> where V : IIsRefreshable<T, V>
{
public bool ShouldRefreshNow
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get;
set;
}
public V SetNewValue(T key);
}
+43
View File
@@ -0,0 +1,43 @@
// Copyright (c) 2022 bradson
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
using System.Collections.Concurrent;
using System.Linq;
namespace PerformanceFish.Cache;
public static class Utility
{
public static ConcurrentBag<ICollection> All { get; } = new();
public static void Clear()
{
foreach (var cache in All)
{
cache.GetType()
.GetMethods()
.Where(m
=> m.Name == nameof(Array.Clear)
&& m.GetParameters().Length == 0)
.First()
.Invoke(cache, null);
}
}
public static T AddNew<T>() where T : ICollection, new()
{
var newCollection = new T();
All.Add(newCollection);
return newCollection;
}
public static T AddNew<T>(IEqualityComparer comparer) where T : ICollection, new()
{
var newCollection = (T)Activator.CreateInstance(typeof(T), comparer);
All.Add(newCollection);
return newCollection;
}
}
public delegate int IndexGetter<T>(T item);