Added stat caching, fixed all the bugs

Added RitualObligationTriggers analyzer category,
Fixed defs from other mods with broken market value breaking fish's storage patches,
Fixed storage caches not updating for quality, rotting and similar changes to stored things,
Fixed storage caches not updating capacity when reinstalling storage,
Fixed prison labor compatibility,
Improved multiplayer compatibility,
Fixed ideology style dominance and altar sharing patches throwing errors instead of defaulting to disabled when not having ideology,
Fixed compatibility with RIMMSqol's world pawn GC patch by disabling that patch,
Fixed rare threading issues throwing errors on startup
This commit is contained in:
bbradson
2024-03-10 20:05:24 +01:00
parent a4ee95fde1
commit be3dae46bb
45 changed files with 1090 additions and 414 deletions
@@ -125,6 +125,9 @@ public static class MiscExtensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsSpawned(this Thing thing) => (uint)thing.mapIndexOrState < (uint)Current.Game.Maps.Count;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetLoadID(this Bill bill) => bill.loadID;
public static void InvokeWhenCellIndicesReady(this Map map, Action<Map> action)
{
if (map.cellIndices != null)
+80 -33
View File
@@ -3,15 +3,10 @@
// 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.Utility;
public record struct PooledArray<T> : IDisposable, IEnumerable<T>
{
private static T[]?[] _buffers = new T[]?[4];
private static int _bufferCount;
private T[] _array;
private int _length;
@@ -19,6 +14,16 @@ public record struct PooledArray<T> : IDisposable, IEnumerable<T>
public int Length => _length;
public int Capacity => _array.Length;
[ThreadStatic]
private static T[]?[]? _buffersThreadStatic;
[ThreadStatic]
private static int _bufferCountThreadStatic;
private static object _lockObject = new();
private static T[]?[] _buffersGlobal = new T[]?[16];
private static int _bufferCountGlobal;
public T this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -41,22 +46,21 @@ public record struct PooledArray<T> : IDisposable, IEnumerable<T>
public PooledArray(int length)
{
if (_bufferCount < 1)
{
NewBuffer(length);
}
else
{
ref var bucket = ref _buffers[--_bufferCount];
_array = bucket!;
bucket = null;
if (_array.Length < length)
ResizeBuffer(length);
}
if (_bufferCountThreadStatic == 0)
FetchThroughLock();
ref var bucket = ref _buffersThreadStatic![--_bufferCountThreadStatic];
_array = bucket!;
bucket = null;
if (_array.Length < length)
ResizeBuffer(length);
_length = length;
}
[MethodImpl(MethodImplOptions.NoInlining)]
private void ResizeBuffer(int length) => Array.Resize(ref _array, Mathf.NextPowerOfTwo(length));
public PooledArray(ICollection<T> collection) : this(collection.Count) => collection.CopyTo(_array, 0);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -66,35 +70,78 @@ public record struct PooledArray<T> : IDisposable, IEnumerable<T>
return ref _array[index];
}
[MethodImpl(MethodImplOptions.NoInlining)]
private void ResizeBuffer(int length) => Array.Resize(ref _array, Mathf.NextPowerOfTwo(length));
[MemberNotNull(nameof(_array))]
[MethodImpl(MethodImplOptions.NoInlining)]
private void NewBuffer(int length) => _array = new T[Mathf.NextPowerOfTwo(length)];
public void Dispose()
{
if (++_bufferCount > _buffers.Length)
ExpandBuffers(_bufferCount);
if (_length > 0)
Array.Clear(_array, 0, _length);
_buffers[_bufferCount - 1] = _array;
var buffersThreadStatic = _buffersThreadStatic ??= InitializeBuffersThreadStatic();
if (_bufferCountThreadStatic == 16)
PushThroughLock();
buffersThreadStatic[_bufferCountThreadStatic++] = _array;
}
public void Sort(IComparer<T> comparer) => _array.AsSpan()[.._length].Sort(comparer);
private static void ExpandBuffers(int minLength)
[MethodImpl(MethodImplOptions.NoInlining)]
private static void FetchThroughLock()
{
var newBuffers = new T[]?[Mathf.NextPowerOfTwo(minLength)];
var buffersThreadStatic = _buffersThreadStatic ??= InitializeBuffersThreadStatic();
var createNew = false;
lock (_lockObject)
{
if (_bufferCountGlobal != 0)
{
for (var i = 8; i-- > 0;)
{
buffersThreadStatic[i] = _buffersGlobal[--_bufferCountGlobal];
_buffersGlobal[_bufferCountGlobal] = default;
}
}
else
{
createNew = true;
}
}
Array.Copy(_buffers, newBuffers, _buffers.Length);
_buffers = newBuffers;
if (createNew)
Array.Fill(buffersThreadStatic, Array.Empty<T>(), 0, 8);
_bufferCountThreadStatic = 8;
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void PushThroughLock()
{
var buffersThreadStatic = _buffersThreadStatic!;
lock (_lockObject)
{
if (_bufferCountGlobal + 8 > _buffersGlobal.Length)
ExpandGlobalBuffers();
for (var i = 16; i-- > 8;)
{
ref var bucket = ref buffersThreadStatic[i];
var buffer = bucket;
bucket = default;
_buffersGlobal[_bufferCountGlobal++] = buffer;
}
}
_bufferCountThreadStatic = 8;
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void ExpandGlobalBuffers() => Array.Resize(ref _buffersGlobal, _buffersGlobal.Length << 1);
[MethodImpl(MethodImplOptions.NoInlining)]
private static T[]?[] InitializeBuffersThreadStatic() => new T[]?[16];
public IEnumerator<T> GetEnumerator()
{
for (var i = 0; i < _length; i++)
@@ -10,6 +10,18 @@ public static class StringExtensions
public static bool NameEqualsCaseInsensitive(this MethodInfo methodInfo, string name)
=> methodInfo.Name.Equals(name, StringComparison.OrdinalIgnoreCase);
public static string AppendNewLineWhen(this string instance, bool predicate, string text)
=> predicate ? instance.AppendNewLine(text) : instance;
public static string AppendNewLineWhenNotNull(this string instance, object? value)
=> value != null ? instance.AppendNewLine(value.ToString()) : instance;
public static string AppendNewLine(this string instance, string text)
=> string.Concat(instance, "\n", text);
public static string AppendWhen(this string instance, bool predicate, string text)
=> predicate ? instance + text : instance;
public static string AppendWhenNotNull(this string instance, object? value)
=> value != null ? instance + value : instance;
}