Performance Fish
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
// 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.Threading.Tasks;
|
||||
using SharedAltarCache = PerformanceFish.Cache.ByIndex<RimWorld.Ideo, PerformanceFish.ThoughtWorker_Precept_AltarSharingOptimization.SharedAltarCacheValue>;
|
||||
|
||||
namespace PerformanceFish;
|
||||
|
||||
public class ThoughtWorker_Precept_AltarSharingOptimization : ClassWithFishPatches
|
||||
{
|
||||
public class SharedAltar_Patch : FirstPriorityFishPatch
|
||||
{
|
||||
public override string Description => "Caches results of ideology's shared altar thoughtworker, throttles it to wait a minimum of 128 ticks between refreshes and makes refreshing happen async on a different thread. Quite impactful on performance";
|
||||
public override Expression<Action> TargetMethod => () => default(ThoughtWorker_Precept_AltarSharing)!.SharedAltar(null);
|
||||
public static void AsyncThoughtWorker(ThoughtWorker_Precept_AltarSharing __instance, Pawn pawn)
|
||||
{
|
||||
var allThings = pawn.Map.listerThings.listsByGroup[(int)ThingRequestGroup.BuildingArtificial].ToArray();
|
||||
Task.Run(() =>
|
||||
{
|
||||
var newThing = SharedAltarParallel(pawn, allThings);
|
||||
lock (Lock)
|
||||
SharedAltarCache.GetDirectly[pawn.Ideo] = new(pawn, newThing);
|
||||
});
|
||||
}
|
||||
public static bool Prefix(ThoughtWorker_Precept_AltarSharing __instance, Pawn pawn, ref Thing? __result, out bool __state)
|
||||
{
|
||||
if (!pawn.Spawned)
|
||||
{
|
||||
__result = null;
|
||||
return __state = false;
|
||||
}
|
||||
SharedAltarCacheValue cache;
|
||||
var shouldStartRefreshing = false;
|
||||
lock (Lock)
|
||||
{
|
||||
if (!SharedAltarCache.GetDirectly.TryGetValue(pawn.Ideo, out cache) || (!FishSettings.ThreadingEnabled && cache.ShouldRefreshNow))
|
||||
{
|
||||
return __state = true;
|
||||
}
|
||||
else if (cache.ShouldRefreshNow)
|
||||
{
|
||||
cache.currentlyRefreshing = true;
|
||||
SharedAltarCache.GetDirectly[pawn.Ideo] = cache;
|
||||
shouldStartRefreshing = true;
|
||||
}
|
||||
}
|
||||
|
||||
__result = cache.thing;
|
||||
if (shouldStartRefreshing)
|
||||
AsyncThoughtWorker(__instance, pawn);
|
||||
return __state = false;
|
||||
}
|
||||
public static void Postfix(Pawn pawn, Thing __result, bool __state)
|
||||
{
|
||||
if (!__state)
|
||||
return;
|
||||
lock (Lock)
|
||||
SharedAltarCache.GetDirectly[pawn.Ideo] = new(pawn, __result);
|
||||
}
|
||||
public static Thing? SharedAltarParallel(Pawn pawn, Thing[] allThings)
|
||||
{
|
||||
if (!pawn.Spawned || pawn.Ideo == null)
|
||||
return null;
|
||||
|
||||
for (var i = 0; i < allThings.Length; i++)
|
||||
{
|
||||
var item = allThings[i];
|
||||
var compStyleable = item.TryGetComp<CompStyleable>();
|
||||
if (compStyleable == null || compStyleable.SourcePrecept == null || compStyleable.SourcePrecept.ideo != pawn.Ideo)
|
||||
continue;
|
||||
|
||||
var room = item.GetRoom();
|
||||
if (room == null || room.TouchesMapEdge)
|
||||
continue;
|
||||
|
||||
var containedAndAdjacentThings = room.ContainedAndAdjacentThings;
|
||||
for (var k = 0; k < containedAndAdjacentThings.Count; k++)
|
||||
{
|
||||
var containedAndAdjacentThing = containedAndAdjacentThings[k];
|
||||
if (containedAndAdjacentThing != item)
|
||||
{
|
||||
var compStyleable2 = containedAndAdjacentThing.TryGetComp<CompStyleable>();
|
||||
if (compStyleable2 != null && compStyleable2.SourcePrecept != null && compStyleable2.SourcePrecept.ideo != pawn.Ideo)
|
||||
{
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private static object Lock { get; } = new();
|
||||
}
|
||||
|
||||
public struct SharedAltarCacheValue : Cache.IIsRefreshable<Ideo, SharedAltarCacheValue>
|
||||
{
|
||||
public Thing? thing;
|
||||
public bool currentlyRefreshing;
|
||||
private int _nextLateRefreshTick;
|
||||
private int _nextEarlyRefreshTick;
|
||||
private int _allStructuresListVersion;
|
||||
private ListerThings _lister;
|
||||
|
||||
public SharedAltarCacheValue(Pawn pawn, Thing? result)
|
||||
{
|
||||
thing = result;
|
||||
var ticks = Current.Game.tickManager.TicksGame;
|
||||
_nextLateRefreshTick = ticks + 3072 + Math.Abs(Rand.Int % 2048);
|
||||
_nextEarlyRefreshTick = ticks + 128 + Math.Abs(Rand.Int % 128);
|
||||
_lister = pawn.Map.listerThings;
|
||||
_allStructuresListVersion = _lister.listsByGroup[(int)ThingRequestGroup.BuildingArtificial].Version();
|
||||
currentlyRefreshing = false;
|
||||
}
|
||||
|
||||
public bool ShouldRefreshNow
|
||||
{
|
||||
get
|
||||
{
|
||||
var ticks = Current.Game.tickManager.TicksGame;
|
||||
return !currentlyRefreshing && (_nextLateRefreshTick < ticks
|
||||
|| (_nextEarlyRefreshTick < ticks && _allStructuresListVersion != _lister.listsByGroup[(int)ThingRequestGroup.BuildingArtificial].Version()));
|
||||
}
|
||||
set
|
||||
{
|
||||
var ticks = Current.Game.tickManager.TicksGame;
|
||||
_nextLateRefreshTick = value ? 0 : ticks + 3072 + Math.Abs(Rand.Int % 2048);
|
||||
_nextEarlyRefreshTick = value ? 0 : ticks + 128 + Math.Abs(Rand.Int % 128);
|
||||
}
|
||||
}
|
||||
|
||||
public SharedAltarCacheValue SetNewValue(Ideo key) => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user