Files
Performance-Fish/Source/PerformanceFish/ThingOwnerUtilityCaching.cs
T
bbradson ca9321add8 Lots of new optimizations utilizing prepatcher
To start, this mod now requires Prepatcher and Fishery.
New optimizations include: gene ticking improvements, world object improvements, hediff optimizations, world pawn gc optimizations, better mothballing, dds support, ContentFinder caching, additions and changes to reflection caching, as well as various other additions. Multiplayer support has been improved too, but should be considered highly experimental. The WorkGiver_DoBill bug is fixed in this build. Multiple patches aiming to improve RimWorld's load time have been added too.
2023-06-03 20:24:38 +02:00

187 lines
5.7 KiB
C#

// Copyright (c) 2023 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 PerformanceFish.Prepatching;
/*using ThingRequestCache
= PerformanceFish.Cache.ByReference<int, byte, System.RuntimeTypeHandle,
PerformanceFish.ThingRequestInfo>;*/
namespace PerformanceFish;
public class ThingOwnerUtilityPreCaching : ClassWithFishPrepatches
{
public class GetAllThingsRecursively_Patch : FishPrepatch
{
public override string Description { get; }
= "Caching of information about mostly unspawned pawns and things. Relatively large performance impact";
public override MethodBase TargetMethodBase { get; }
= methodof(ThingOwnerUtility.GetAllThingsRecursively<Thing>);
public static bool Prefix<T>(Map map, ThingRequest request, List<T>? outThings, bool allowUnreal,
Predicate<IThingHolder>? passCheck, bool alsoGetSpawnedThings, out bool __state)
where T : Thing
{
if (request.singleDef != null
|| request.group == 0
|| !allowUnreal
|| passCheck != null
|| alsoGetSpawnedThings
|| outThings == null)
{
__state = false;
return true;
}
var listsByGroup = map.listerThings.listsByGroup[(int)request.group];
if (listsByGroup is null)
{
__state = false;
return true;
}
var key = new Cache.ByInt<Map, ThingRequestGroup, ThingRequestInfo<T>>(map.uniqueID, (byte)request.group);
ref var cache = ref Cache.ByInt<Map, ThingRequestGroup, ThingRequestInfo<T>>.Get.GetOrAddReference(ref key);
if (cache.Dirty
|| cache.ListsByGroupCount != listsByGroup.Count
|| cache.ThingHolderCount != ThingHolderCount(map))
{
return __state = true;
}
outThings.ReplaceContentsWith(cache.Things!);
return __state = false;
}
public static void Postfix<T>(Map map, ThingRequest request, List<T> outThings, bool __state)
where T : Thing
{
if (!__state)
return;
ref var cache = ref Cache.ByInt<Map, ThingRequestGroup, ThingRequestInfo<T>>.Get.GetReference(new(map.uniqueID,
(byte)request.group));
if (cache.Things is null)
cache.Things = outThings.Copy();
else
cache.Things.ReplaceContentsWith(outThings);
cache.ListsByGroupCount = map.listerThings.listsByGroup[(int)request.group]?.Count ?? -1;
cache.ThingHolderCount = ThingHolderCount(map);
cache.SetDirty(false, map.uniqueID + (int)request.group + cache.ListsByGroupCount + cache.ThingHolderCount);
}
}
public static int ThingHolderCount(Map map)
=> (map.passingShipManager?.passingShips.Count ?? 0)
+ map.components.Count
+ (map.listerThings.listsByGroup[(int)ThingRequestGroup.ThingHolder]?.Count ?? 0);
}
/*public class ThingOwnerUtilityCaching : ClassWithFishPatches
{
public class GetAllThingsRecursively_Patch : FishPatch
{
public override string Description { get; }
= "Caching of information about mostly unspawned pawns and things. Relatively large performance impact";
public override Delegate TargetMethodGroup { get; } = ThingOwnerUtility.GetAllThingsRecursively<Thing>;
public static bool Prefix(Map map, ThingRequest request, IList? outThings, bool allowUnreal,
Predicate<IThingHolder>? passCheck, bool alsoGetSpawnedThings, out bool __state)
{
if (request.singleDef != null
|| request.group == 0
|| !allowUnreal
|| passCheck != null
|| alsoGetSpawnedThings
|| outThings == null)
{
__state = false;
return true;
}
var listByGroup = map.listerThings.listsByGroup[(int)request.group];
if (listByGroup is null)
{
__state = false;
return true;
}
var type = outThings.GetType().GetGenericArguments()[0];
var key = new ThingRequestCache(map.uniqueID, (byte)request.group, type.TypeHandle);
ref var cache = ref ThingRequestCache.Get.GetOrAddReference(ref key);
if (cache.Dirty
|| cache.count != listByGroup.Count
|| cache.thingHolderCount != ThingHolderCount(map))
{
return __state = true;
}
outThings.Clear();
outThings.TryAddRange(cache.things!, type);
return __state = false;
}
public static void Postfix(Map map, ThingRequest request, IList outThings, bool __state)
{
if (!__state)
return;
var type = outThings.GetType().GetGenericArguments()[0];
ref var cache
= ref ThingRequestCache.Get.GetReference(new(map.uniqueID, (byte)request.group, type.TypeHandle));
if (cache.things is null)
{
cache.things = FisheryLib.CollectionExtensions.TryNew(outThings, type);
}
else
{
cache.things.Clear();
cache.things.TryAddRange(outThings, type);
}
cache.count = map.listerThings.listsByGroup[(int)request.group]?.Count ?? -1;
cache.thingHolderCount = ThingHolderCount(map);
cache.SetDirty(false, map.uniqueID + (int)request.group + cache.count + cache.thingHolderCount);
}
}
public static int ThingHolderCount(Map map)
=> (map.passingShipManager?.passingShips.Count ?? 0)
+ map.components.Count
+ (map.listerThings.listsByGroup[(int)ThingRequestGroup.ThingHolder]?.Count ?? 0);
}*/
/*public record struct ThingRequestInfo
{
private int _nextRefreshTick;
public int count;
public int thingHolderCount;
public IList? things;
public void SetDirty(bool value, int offset)
=> _nextRefreshTick = value ? 0 : TickHelper.Add(3072, offset, 2048);
public bool Dirty => _nextRefreshTick < Current.Game.tickManager.TicksGame;
}*/
public record struct ThingRequestInfo<T> where T : Thing
{
private int _nextUpdateTick;
public int ListsByGroupCount;
public int ThingHolderCount;
public List<T>? Things;
public void SetDirty(bool value, int offset)
=> _nextUpdateTick = value ? 0 : TickHelper.Add(3072, offset, 2048);
public bool Dirty => TickHelper.Past(_nextUpdateTick);
}