Cache no-op component tick lists in RimWorld 1.6
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -11,6 +11,15 @@ namespace PerformanceFish;
|
|||||||
|
|
||||||
public sealed class CompTickOptimization : ClassWithFishPrepatches
|
public sealed class CompTickOptimization : ClassWithFishPrepatches
|
||||||
{
|
{
|
||||||
|
#if V1_6
|
||||||
|
public struct ThingTickCache
|
||||||
|
{
|
||||||
|
public List<ThingComp>? Source;
|
||||||
|
public int Version;
|
||||||
|
public bool AllTrivial;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
private static readonly MethodInfo _thingCompTickMethod
|
private static readonly MethodInfo _thingCompTickMethod
|
||||||
= AccessTools.DeclaredMethod(typeof(ThingComp), nameof(ThingComp.CompTick));
|
= AccessTools.DeclaredMethod(typeof(ThingComp), nameof(ThingComp.CompTick));
|
||||||
|
|
||||||
@@ -39,8 +48,12 @@ public sealed class CompTickOptimization : ClassWithFishPrepatches
|
|||||||
private static readonly bool _hediffCompPostTickIntervalBaseIsTrivial
|
private static readonly bool _hediffCompPostTickIntervalBaseIsTrivial
|
||||||
= IsTrivialVoidMethod(_hediffCompPostTickIntervalMethod);
|
= IsTrivialVoidMethod(_hediffCompPostTickIntervalMethod);
|
||||||
|
|
||||||
|
#if V1_6
|
||||||
|
private static int _canSkipInheritedBaseThingCompTickState;
|
||||||
|
#else
|
||||||
private static readonly Lazy<bool> _canSkipInheritedBaseThingCompTick
|
private static readonly Lazy<bool> _canSkipInheritedBaseThingCompTick
|
||||||
= new(static () => _thingCompTickBaseIsTrivial && !HasHarmonyPatches(_thingCompTickMethod));
|
= new(static () => _thingCompTickBaseIsTrivial && !HasHarmonyPatches(_thingCompTickMethod));
|
||||||
|
#endif
|
||||||
|
|
||||||
private static readonly Lazy<bool> _canSkipInheritedBaseThingCompTickInterval
|
private static readonly Lazy<bool> _canSkipInheritedBaseThingCompTickInterval
|
||||||
= new(static () => _thingCompTickIntervalBaseIsTrivial && !HasHarmonyPatches(_thingCompTickIntervalMethod));
|
= new(static () => _thingCompTickIntervalBaseIsTrivial && !HasHarmonyPatches(_thingCompTickIntervalMethod));
|
||||||
@@ -89,10 +102,19 @@ public sealed class CompTickOptimization : ClassWithFishPrepatches
|
|||||||
|
|
||||||
public sealed class ThingTickPatch : FishPrepatch
|
public sealed class ThingTickPatch : FishPrepatch
|
||||||
{
|
{
|
||||||
public override bool DefaultState => false;
|
public override bool DefaultState =>
|
||||||
|
#if V1_6
|
||||||
|
true;
|
||||||
|
#else
|
||||||
|
false;
|
||||||
|
#endif
|
||||||
|
|
||||||
public override string? Description { get; }
|
public override string? Description { get; }
|
||||||
|
#if V1_6
|
||||||
|
= "Skips the component tick loop when every component inherits the unpatched, empty base CompTick method.";
|
||||||
|
#else
|
||||||
= "Skips inherited empty ThingComp.CompTick calls when the base method is still unpatched and trivial.";
|
= "Skips inherited empty ThingComp.CompTick calls when the base method is still unpatched and trivial.";
|
||||||
|
#endif
|
||||||
|
|
||||||
public override MethodBase TargetMethodBase { get; }
|
public override MethodBase TargetMethodBase { get; }
|
||||||
= AccessTools.DeclaredMethod(typeof(ThingWithComps), "Tick");
|
= AccessTools.DeclaredMethod(typeof(ThingWithComps), "Tick");
|
||||||
@@ -102,16 +124,61 @@ public sealed class CompTickOptimization : ClassWithFishPrepatches
|
|||||||
|
|
||||||
public static void ReplacementBody(ThingWithComps instance)
|
public static void ReplacementBody(ThingWithComps instance)
|
||||||
{
|
{
|
||||||
|
#if V1_6
|
||||||
|
var comps = instance.comps;
|
||||||
|
if (comps is null)
|
||||||
|
return;
|
||||||
|
#else
|
||||||
var comps = instance.AllComps;
|
var comps = instance.AllComps;
|
||||||
|
#endif
|
||||||
|
|
||||||
var count = comps.Count;
|
var count = comps.Count;
|
||||||
|
#if V1_6
|
||||||
|
// Small lists cost less to dispatch normally than to validate a cache.
|
||||||
|
if (count >= 4 && CanSkipInheritedBaseThingCompTick())
|
||||||
|
{
|
||||||
|
ref var cache = ref instance.ThingTickCache();
|
||||||
|
if (!ReferenceEquals(cache.Source, comps) || cache.Version != comps._version)
|
||||||
|
UpdateThingTickCache(comps, ref cache);
|
||||||
|
|
||||||
|
if (cache.AllTrivial)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#else
|
||||||
var canSkipInheritedBase = CanSkipInheritedBaseThingCompTick();
|
var canSkipInheritedBase = CanSkipInheritedBaseThingCompTick();
|
||||||
|
#endif
|
||||||
for (var i = 0; i < count; i++)
|
for (var i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
|
#if V1_6
|
||||||
|
instance.comps![i].CompTick();
|
||||||
|
#else
|
||||||
var comp = comps[i];
|
var comp = comps[i];
|
||||||
if (!canSkipInheritedBase || !_thingCompTickTypes.GetOrAdd(comp.GetType()))
|
if (!canSkipInheritedBase || !_thingCompTickTypes.GetOrAdd(comp.GetType()))
|
||||||
comp.CompTick();
|
comp.CompTick();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if V1_6
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
private static void UpdateThingTickCache(List<ThingComp> comps, ref ThingTickCache cache)
|
||||||
|
{
|
||||||
|
var allTrivial = true;
|
||||||
|
for (var i = 0; i < comps.Count; i++)
|
||||||
|
{
|
||||||
|
var comp = comps[i];
|
||||||
|
if (comp is null || !_thingCompTickTypes.GetOrAdd(comp.GetType()))
|
||||||
|
{
|
||||||
|
allTrivial = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cache.Source = comps;
|
||||||
|
cache.Version = comps._version;
|
||||||
|
cache.AllTrivial = allTrivial;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class ThingTickIntervalPatch : FishPrepatch
|
public sealed class ThingTickIntervalPatch : FishPrepatch
|
||||||
@@ -267,7 +334,19 @@ public sealed class CompTickOptimization : ClassWithFishPrepatches
|
|||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static bool CanSkipInheritedBaseThingCompTick()
|
private static bool CanSkipInheritedBaseThingCompTick()
|
||||||
=> _canSkipInheritedBaseThingCompTick.Value;
|
{
|
||||||
|
#if V1_6
|
||||||
|
var state = _canSkipInheritedBaseThingCompTickState;
|
||||||
|
if (state == 0)
|
||||||
|
{
|
||||||
|
state = _thingCompTickBaseIsTrivial && !HasHarmonyPatches(_thingCompTickMethod) ? 2 : 1;
|
||||||
|
_canSkipInheritedBaseThingCompTickState = state;
|
||||||
|
}
|
||||||
|
return state == 2;
|
||||||
|
#else
|
||||||
|
return _canSkipInheritedBaseThingCompTick.Value;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static bool CanSkipInheritedBaseThingCompTickInterval()
|
private static bool CanSkipInheritedBaseThingCompTickInterval()
|
||||||
|
|||||||
@@ -39,6 +39,10 @@ public static class PrepatcherFields
|
|||||||
[PrepatcherField]
|
[PrepatcherField]
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static extern ref GeneTrackerOptimization.GeneListVersionState GeneListVersions(this Pawn_GeneTracker geneTracker);
|
public static extern ref GeneTrackerOptimization.GeneListVersionState GeneListVersions(this Pawn_GeneTracker geneTracker);
|
||||||
|
|
||||||
|
[PrepatcherField]
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static extern ref CompTickOptimization.ThingTickCache ThingTickCache(this ThingWithComps thing);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
[PrepatcherField]
|
[PrepatcherField]
|
||||||
|
|||||||
Reference in New Issue
Block a user