Revert "Revert "relieve gc pressure maybe"" (without caching addresses this time)

This reverts commit 5d2b25bccb.
This commit is contained in:
Loporrit
2024-02-14 05:34:12 +00:00
parent 5d2b25bccb
commit 0f661c9c69
5 changed files with 88 additions and 77 deletions

View File

@@ -136,7 +136,7 @@ public class PlayerDataFactory
totalWaitTime -= 50; totalWaitTime -= 50;
} }
Stopwatch st = Stopwatch.StartNew(); DateTime start = DateTime.UtcNow;
// penumbra call, it's currently broken // penumbra call, it's currently broken
IReadOnlyDictionary<string, string[]>? resolvedPaths; IReadOnlyDictionary<string, string[]>? resolvedPaths;
@@ -225,8 +225,7 @@ public class PlayerDataFactory
} }
} }
st.Stop(); _logger.LogInformation("Building character data for {obj} took {time}ms", objectKind, TimeSpan.FromTicks(DateTime.UtcNow.Ticks - start.Ticks).TotalMilliseconds);
_logger.LogInformation("Building character data for {obj} took {time}ms", objectKind, TimeSpan.FromTicks(st.ElapsedTicks).TotalMilliseconds);
return previousData; return previousData;
} }

View File

@@ -459,7 +459,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
if (string.IsNullOrEmpty(PlayerName)) if (string.IsNullOrEmpty(PlayerName))
{ {
var pc = _dalamudUtil.FindPlayerByNameHash(OnlineUser.Ident); var pc = _dalamudUtil.FindPlayerByNameHash(OnlineUser.Ident);
if (pc == default((string, nint))) return; if (pc.ObjectId == 0) return;
Logger.LogDebug("One-Time Initializing {this}", this); Logger.LogDebug("One-Time Initializing {this}", this);
Initialize(pc.Name); Initialize(pc.Name);
Logger.LogDebug("One-Time Initialized {this}", this); Logger.LogDebug("One-Time Initialized {this}", this);

View File

@@ -1,7 +1,5 @@
using Dalamud.Game.ClientState.Conditions; using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Objects; using Dalamud.Game.ClientState.Objects;
using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Game.ClientState.Objects.SubKinds;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.Game.Control; using FFXIVClientStructs.FFXIV.Client.Game.Control;
@@ -14,16 +12,24 @@ using Microsoft.Extensions.Logging;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject; using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject;
using DalamudGameObject = Dalamud.Game.ClientState.Objects.Types.GameObject;
namespace MareSynchronos.Services; namespace MareSynchronos.Services;
public class DalamudUtilService : IHostedService, IMediatorSubscriber public class DalamudUtilService : IHostedService, IMediatorSubscriber
{ {
internal struct PlayerCharacter
{
public uint ObjectId;
public string Name;
public uint HomeWorldId;
public nint Address;
};
private struct PlayerInfo private struct PlayerInfo
{ {
public String Name; public PlayerCharacter Character;
public uint WorldId; public string Hash;
public String Hash;
}; };
private readonly List<uint> _classJobIdsIgnoredForPets = [30]; private readonly List<uint> _classJobIdsIgnoredForPets = [30];
@@ -40,7 +46,8 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
private string _lastGlobalBlockPlayer = string.Empty; private string _lastGlobalBlockPlayer = string.Empty;
private string _lastGlobalBlockReason = string.Empty; private string _lastGlobalBlockReason = string.Empty;
private ushort _lastZone = 0; private ushort _lastZone = 0;
private Dictionary<string, (string Name, nint Address)> _playerCharas = new(StringComparer.Ordinal); private readonly Dictionary<string, PlayerCharacter> _playerCharas = new(StringComparer.Ordinal);
private readonly List<string> _notUpdatedCharas = [];
private bool _sentBetweenAreas = false; private bool _sentBetweenAreas = false;
private static readonly Dictionary<uint, PlayerInfo> _playerInfoCache = new(); private static readonly Dictionary<uint, PlayerInfo> _playerInfoCache = new();
@@ -70,7 +77,7 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
await RunOnFrameworkThread(() => await RunOnFrameworkThread(() =>
{ {
var addr = GetPlayerCharacterFromCachedTableByIdent(ident); var addr = GetPlayerCharacterFromCachedTableByIdent(ident);
var pc = GetPlayerCharacter(); var pc = _clientState.LocalPlayer!;
var gobj = CreateGameObject(addr); var gobj = CreateGameObject(addr);
// Any further than roughly 55y is out of range for targetting // Any further than roughly 55y is out of range for targetting
if (gobj != null && Vector3.Distance(pc.Position, gobj.Position) < 55.0f) if (gobj != null && Vector3.Distance(pc.Position, gobj.Position) < 55.0f)
@@ -179,12 +186,6 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
return await RunOnFrameworkThread(() => GetPet(playerPointer)).ConfigureAwait(false); return await RunOnFrameworkThread(() => GetPet(playerPointer)).ConfigureAwait(false);
} }
public PlayerCharacter GetPlayerCharacter()
{
EnsureIsOnFramework();
return _clientState.LocalPlayer!;
}
public IntPtr GetPlayerCharacterFromCachedTableByIdent(string characterName) public IntPtr GetPlayerCharacterFromCachedTableByIdent(string characterName)
{ {
if (_playerCharas.TryGetValue(characterName, out var pchar)) return pchar.Address; if (_playerCharas.TryGetValue(characterName, out var pchar)) return pchar.Address;
@@ -372,24 +373,28 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
return _gameGui.WorldToScreen(obj.Position, out var screenPos) ? screenPos : Vector2.Zero; return _gameGui.WorldToScreen(obj.Position, out var screenPos) ? screenPos : Vector2.Zero;
} }
internal (string Name, nint Address) FindPlayerByNameHash(string ident) internal PlayerCharacter FindPlayerByNameHash(string ident)
{ {
_playerCharas.TryGetValue(ident, out var result); _playerCharas.TryGetValue(ident, out var result);
return result; return result;
} }
private PlayerInfo GetPlayerInfo(PlayerCharacter p) private unsafe PlayerInfo GetPlayerInfo(DalamudGameObject chara)
{ {
uint id = p.ObjectId; uint id = chara.ObjectId;
if (!_playerInfoCache.TryGetValue(id, out var info)) if (!_playerInfoCache.TryGetValue(id, out var info))
{ {
info.Name = p.Name.ToString(); info.Character.ObjectId = id;
info.WorldId = p.HomeWorld.Id; info.Character.Name = chara.Name.ToString();
info.Hash = Crypto.GetHash256(info.Name + info.WorldId.ToString()); info.Character.HomeWorldId = ((BattleChara*)chara.Address)->Character.HomeWorld;
info.Character.Address = chara.Address;
info.Hash = Crypto.GetHash256(info.Character.Name + info.Character.HomeWorldId.ToString());
_playerInfoCache[id] = info; _playerInfoCache[id] = info;
} }
info.Character.Address = chara.Address;
return info; return info;
} }
@@ -399,7 +404,6 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
{ {
var gameObj = (GameObject*)p.Address; var gameObj = (GameObject*)p.Address;
var drawObj = gameObj->DrawObject; var drawObj = gameObj->DrawObject;
var playerInfo = GetPlayerInfo(p);
bool isDrawing = false; bool isDrawing = false;
bool isDrawingChanged = false; bool isDrawingChanged = false;
if ((nint)drawObj != IntPtr.Zero) if ((nint)drawObj != IntPtr.Zero)
@@ -411,20 +415,20 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
if (!isDrawing) if (!isDrawing)
{ {
isDrawing = ((CharacterBase*)drawObj)->HasModelFilesInSlotLoaded != 0; isDrawing = ((CharacterBase*)drawObj)->HasModelFilesInSlotLoaded != 0;
if (isDrawing && !string.Equals(_lastGlobalBlockPlayer, playerInfo.Name, StringComparison.Ordinal) if (isDrawing && !string.Equals(_lastGlobalBlockPlayer, p.Name, StringComparison.Ordinal)
&& !string.Equals(_lastGlobalBlockReason, "HasModelFilesInSlotLoaded", StringComparison.Ordinal)) && !string.Equals(_lastGlobalBlockReason, "HasModelFilesInSlotLoaded", StringComparison.Ordinal))
{ {
_lastGlobalBlockPlayer = playerInfo.Name; _lastGlobalBlockPlayer = p.Name;
_lastGlobalBlockReason = "HasModelFilesInSlotLoaded"; _lastGlobalBlockReason = "HasModelFilesInSlotLoaded";
isDrawingChanged = true; isDrawingChanged = true;
} }
} }
else else
{ {
if (!string.Equals(_lastGlobalBlockPlayer, playerInfo.Name, StringComparison.Ordinal) if (!string.Equals(_lastGlobalBlockPlayer, p.Name, StringComparison.Ordinal)
&& !string.Equals(_lastGlobalBlockReason, "HasModelInSlotLoaded", StringComparison.Ordinal)) && !string.Equals(_lastGlobalBlockReason, "HasModelInSlotLoaded", StringComparison.Ordinal))
{ {
_lastGlobalBlockPlayer = playerInfo.Name; _lastGlobalBlockPlayer = p.Name;
_lastGlobalBlockReason = "HasModelInSlotLoaded"; _lastGlobalBlockReason = "HasModelInSlotLoaded";
isDrawingChanged = true; isDrawingChanged = true;
} }
@@ -432,10 +436,10 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
} }
else else
{ {
if (!string.Equals(_lastGlobalBlockPlayer, playerInfo.Name, StringComparison.Ordinal) if (!string.Equals(_lastGlobalBlockPlayer, p.Name, StringComparison.Ordinal)
&& !string.Equals(_lastGlobalBlockReason, "RenderFlags", StringComparison.Ordinal)) && !string.Equals(_lastGlobalBlockReason, "RenderFlags", StringComparison.Ordinal))
{ {
_lastGlobalBlockPlayer = playerInfo.Name; _lastGlobalBlockPlayer = p.Name;
_lastGlobalBlockReason = "RenderFlags"; _lastGlobalBlockReason = "RenderFlags";
isDrawingChanged = true; isDrawingChanged = true;
} }
@@ -444,7 +448,7 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
if (isDrawingChanged) if (isDrawingChanged)
{ {
_logger.LogTrace("Global draw block: START => {name} ({reason})", playerInfo.Name, _lastGlobalBlockReason); _logger.LogTrace("Global draw block: START => {name} ({reason})", p.Name, _lastGlobalBlockReason);
} }
IsAnythingDrawing |= isDrawing; IsAnythingDrawing |= isDrawing;
@@ -464,19 +468,32 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
} }
IsAnythingDrawing = false; IsAnythingDrawing = false;
var playerCharaList = new Dictionary<string, (string Name, nint Address)>(StringComparer.Ordinal);
_performanceCollector.LogPerformance(this, "ObjTableToCharas", _performanceCollector.LogPerformance(this, "ObjTableToCharas",
() => { () =>
foreach (var p in _objectTable.OfType<PlayerCharacter>().Where(o => o.ObjectIndex < 200)) {
_notUpdatedCharas.AddRange(_playerCharas.Keys);
foreach (var chara in _objectTable)
{ {
CheckCharacterForDrawing(p); if (chara.ObjectIndex % 2 != 0 || chara.ObjectIndex >= 200) continue;
var info = GetPlayerInfo(p);
playerCharaList.Add(info.Hash, (info.Name, p.Address)); string charaName = chara.Name.ToString();
uint homeWorldId = ((BattleChara*)chara.Address)->Character.HomeWorld;
var info = GetPlayerInfo(chara);
if (!IsAnythingDrawing)
CheckCharacterForDrawing(info.Character);
_notUpdatedCharas.Remove(info.Hash);
_playerCharas[info.Hash] = info.Character;
} }
}
); foreach (var notUpdatedChara in _notUpdatedCharas)
_playerCharas = playerCharaList; {
_playerCharas.Remove(notUpdatedChara);
}
_notUpdatedCharas.Clear();
});
if (!IsAnythingDrawing && !string.IsNullOrEmpty(_lastGlobalBlockPlayer)) if (!IsAnythingDrawing && !string.IsNullOrEmpty(_lastGlobalBlockPlayer))
{ {

View File

@@ -3,7 +3,6 @@ using MareSynchronos.Utils;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.Text; using System.Text;
@@ -14,7 +13,7 @@ public sealed class PerformanceCollectorService : IHostedService
private const string _counterSplit = "=>"; private const string _counterSplit = "=>";
private readonly ILogger<PerformanceCollectorService> _logger; private readonly ILogger<PerformanceCollectorService> _logger;
private readonly MareConfigService _mareConfigService; private readonly MareConfigService _mareConfigService;
private readonly ConcurrentDictionary<string, RollingList<Tuple<TimeOnly, long>>> _performanceCounters = new(StringComparer.Ordinal); public ConcurrentDictionary<string, RollingList<Tuple<TimeOnly, long>>> PerformanceCounters { get; } = new(StringComparer.Ordinal);
private readonly CancellationTokenSource _periodicLogPruneTask = new(); private readonly CancellationTokenSource _periodicLogPruneTask = new();
public PerformanceCollectorService(ILogger<PerformanceCollectorService> logger, MareConfigService mareConfigService) public PerformanceCollectorService(ILogger<PerformanceCollectorService> logger, MareConfigService mareConfigService)
@@ -23,49 +22,57 @@ public sealed class PerformanceCollectorService : IHostedService
_mareConfigService = mareConfigService; _mareConfigService = mareConfigService;
} }
public T LogPerformance<T>(object sender, string counterName, Func<T> func) public T LogPerformance<T>(object sender, string counterName, Func<T> func, int maxEntries = 10000)
{ {
if (!_mareConfigService.Current.LogPerformance) return func.Invoke(); if (!_mareConfigService.Current.LogPerformance) return func.Invoke();
counterName = sender.GetType().Name + _counterSplit + counterName; counterName = sender.GetType().Name + _counterSplit + counterName;
if (!_performanceCounters.TryGetValue(counterName, out var list)) if (!PerformanceCounters.TryGetValue(counterName, out var list))
{ {
list = _performanceCounters[counterName] = new(10000); list = PerformanceCounters[counterName] = new(maxEntries);
} }
Stopwatch st = Stopwatch.StartNew(); var dt = DateTime.UtcNow.Ticks;
try try
{ {
return func.Invoke(); return func.Invoke();
} }
finally finally
{ {
st.Stop(); var elapsed = DateTime.UtcNow.Ticks - dt;
list.Add(new(TimeOnly.FromDateTime(DateTime.Now), st.ElapsedTicks)); #if DEBUG
if (TimeSpan.FromTicks(elapsed) > TimeSpan.FromMilliseconds(10))
_logger.LogWarning(">10ms spike on {counterName}: {time}", counterName, TimeSpan.FromTicks(elapsed));
#endif
list.Add(new(TimeOnly.FromDateTime(DateTime.Now), elapsed));
} }
} }
public void LogPerformance(object sender, string counterName, Action act) public void LogPerformance(object sender, string counterName, Action act, int maxEntries = 10000)
{ {
if (!_mareConfigService.Current.LogPerformance) { act.Invoke(); return; } if (!_mareConfigService.Current.LogPerformance) { act.Invoke(); return; }
counterName = sender.GetType().Name + _counterSplit + counterName; counterName = sender.GetType().Name + _counterSplit + counterName;
if (!_performanceCounters.TryGetValue(counterName, out var list)) if (!PerformanceCounters.TryGetValue(counterName, out var list))
{ {
list = _performanceCounters[counterName] = new(10000); list = PerformanceCounters[counterName] = new(maxEntries);
} }
Stopwatch st = Stopwatch.StartNew(); var dt = DateTime.UtcNow.Ticks;
try try
{ {
act.Invoke(); act.Invoke();
} }
finally finally
{ {
st.Stop(); var elapsed = DateTime.UtcNow.Ticks - dt;
list.Add(new(TimeOnly.FromDateTime(DateTime.Now), st.ElapsedTicks)); #if DEBUG
if (TimeSpan.FromTicks(elapsed) > TimeSpan.FromMilliseconds(10))
_logger.LogWarning(">10ms spike on {counterName}: {time}", counterName, TimeSpan.FromTicks(elapsed));
#endif
list.Add(new(TimeOnly.FromDateTime(DateTime.Now), elapsed));
} }
} }
@@ -97,7 +104,7 @@ public sealed class PerformanceCollectorService : IHostedService
{ {
sb.AppendLine("Performance metrics over total lifetime of each counter"); sb.AppendLine("Performance metrics over total lifetime of each counter");
} }
var data = _performanceCounters.ToList(); var data = PerformanceCounters.ToList();
var longestCounterName = data.OrderByDescending(d => d.Key.Length).First().Key.Length + 2; var longestCounterName = data.OrderByDescending(d => d.Key.Length).First().Key.Length + 2;
sb.Append("-Last".PadRight(15, '-')); sb.Append("-Last".PadRight(15, '-'));
sb.Append('|'); sb.Append('|');
@@ -169,12 +176,12 @@ public sealed class PerformanceCollectorService : IHostedService
{ {
await Task.Delay(TimeSpan.FromMinutes(10), _periodicLogPruneTask.Token).ConfigureAwait(false); await Task.Delay(TimeSpan.FromMinutes(10), _periodicLogPruneTask.Token).ConfigureAwait(false);
foreach (var entries in _performanceCounters.ToList()) foreach (var entries in PerformanceCounters.ToList())
{ {
try try
{ {
var last = entries.Value.ToList().Last(); var last = entries.Value.ToList().Last();
if (last.Item1.AddMinutes(10) < TimeOnly.FromDateTime(DateTime.Now) && !_performanceCounters.TryRemove(entries.Key, out _)) if (last.Item1.AddMinutes(10) < TimeOnly.FromDateTime(DateTime.Now) && !PerformanceCounters.TryRemove(entries.Key, out _))
{ {
_logger.LogDebug("Could not remove performance counter {counter}", entries.Key); _logger.LogDebug("Could not remove performance counter {counter}", entries.Key);
} }

View File

@@ -1,16 +1,16 @@
using Dalamud.Game.ClientState.Objects.SubKinds; using System.Security.Cryptography;
using System.Security.Cryptography;
using System.Text; using System.Text;
namespace MareSynchronos.Utils; namespace MareSynchronos.Utils;
public static class Crypto public static class Crypto
{ {
#pragma warning disable SYSLIB0021 // Type or member is obsolete
private static readonly Dictionary<string, string> _hashListSHA1 = new(StringComparer.Ordinal); private static readonly Dictionary<string, string> _hashListSHA1 = new(StringComparer.Ordinal);
private static readonly Dictionary<string, string> _hashListSHA256 = new(StringComparer.Ordinal); private static readonly Dictionary<string, string> _hashListSHA256 = new(StringComparer.Ordinal);
private static readonly SHA256CryptoServiceProvider _sha256CryptoProvider = new();
#pragma warning disable SYSLIB0021 // Type or member is obsolete private static readonly SHA1CryptoServiceProvider _sha1CryptoProvider = new();
public static string GetFileHash(this string filePath) public static string GetFileHash(this string filePath)
{ {
@@ -28,21 +28,12 @@ public static class Crypto
return GetOrComputeHashSHA256(stringToHash); return GetOrComputeHashSHA256(stringToHash);
} }
public static string GetHash256(this PlayerCharacter character)
{
var charName = character.Name + character.HomeWorld.Id.ToString();
return GetOrComputeHashSHA256(charName);
}
private static string GetOrComputeHashSHA1(string stringToCompute) private static string GetOrComputeHashSHA1(string stringToCompute)
{ {
if (_hashListSHA1.TryGetValue(stringToCompute, out var hash)) if (_hashListSHA1.TryGetValue(stringToCompute, out var hash))
return hash; return hash;
using SHA1CryptoServiceProvider cryptoProvider = new(); return _hashListSHA1[stringToCompute] = BitConverter.ToString(_sha1CryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(stringToCompute))).Replace("-", "", StringComparison.Ordinal);
var computedHash = BitConverter.ToString(cryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(stringToCompute))).Replace("-", "", StringComparison.Ordinal);
_hashListSHA1[stringToCompute] = computedHash;
return computedHash;
} }
private static string GetOrComputeHashSHA256(string stringToCompute) private static string GetOrComputeHashSHA256(string stringToCompute)
@@ -50,10 +41,7 @@ public static class Crypto
if (_hashListSHA256.TryGetValue(stringToCompute, out var hash)) if (_hashListSHA256.TryGetValue(stringToCompute, out var hash))
return hash; return hash;
using SHA256CryptoServiceProvider cryptoProvider = new(); return _hashListSHA256[stringToCompute] = BitConverter.ToString(_sha256CryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(stringToCompute))).Replace("-", "", StringComparison.Ordinal);
var computedHash = BitConverter.ToString(cryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(stringToCompute))).Replace("-", "", StringComparison.Ordinal);
_hashListSHA256[stringToCompute] = computedHash;
return computedHash;
} }
#pragma warning restore SYSLIB0021 // Type or member is obsolete #pragma warning restore SYSLIB0021 // Type or member is obsolete