relieve gc pressure maybe
This commit is contained in:
		| @@ -136,7 +136,7 @@ public class PlayerDataFactory | ||||
|             totalWaitTime -= 50; | ||||
|         } | ||||
|  | ||||
|         Stopwatch st = Stopwatch.StartNew(); | ||||
|         DateTime start = DateTime.UtcNow; | ||||
|  | ||||
|         // penumbra call, it's currently broken | ||||
|         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(st.ElapsedTicks).TotalMilliseconds); | ||||
|         _logger.LogInformation("Building character data for {obj} took {time}ms", objectKind, TimeSpan.FromTicks(DateTime.UtcNow.Ticks - start.Ticks).TotalMilliseconds); | ||||
|  | ||||
|         return previousData; | ||||
|     } | ||||
|   | ||||
| @@ -459,7 +459,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase | ||||
|         if (string.IsNullOrEmpty(PlayerName)) | ||||
|         { | ||||
|             var pc = _dalamudUtil.FindPlayerByNameHash(OnlineUser.Ident); | ||||
|             if (pc == default((string, nint))) return; | ||||
|             if (pc.ObjectId == 0) return; | ||||
|             Logger.LogDebug("One-Time Initializing {this}", this); | ||||
|             Initialize(pc.Name); | ||||
|             Logger.LogDebug("One-Time Initialized {this}", this); | ||||
|   | ||||
| @@ -1,7 +1,5 @@ | ||||
| using Dalamud.Game.ClientState.Conditions; | ||||
| using Dalamud.Game.ClientState.Objects; | ||||
| using Dalamud.Game.ClientState.Objects.Enums; | ||||
| using Dalamud.Game.ClientState.Objects.SubKinds; | ||||
| using Dalamud.Plugin.Services; | ||||
| using FFXIVClientStructs.FFXIV.Client.Game.Character; | ||||
| using FFXIVClientStructs.FFXIV.Client.Game.Control; | ||||
| @@ -14,16 +12,24 @@ using Microsoft.Extensions.Logging; | ||||
| using System.Numerics; | ||||
| using System.Runtime.CompilerServices; | ||||
| using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject; | ||||
| using DalamudGameObject = Dalamud.Game.ClientState.Objects.Types.GameObject; | ||||
|  | ||||
| namespace MareSynchronos.Services; | ||||
|  | ||||
| public class DalamudUtilService : IHostedService, IMediatorSubscriber | ||||
| { | ||||
|     internal struct PlayerCharacter | ||||
|     { | ||||
|         public uint ObjectId; | ||||
|         public string Name; | ||||
|         public uint HomeWorldId; | ||||
|         public nint Address; | ||||
|     }; | ||||
|  | ||||
|     private struct PlayerInfo | ||||
|     { | ||||
|         public String Name; | ||||
|         public uint WorldId; | ||||
|         public String Hash; | ||||
|         public PlayerCharacter Character; | ||||
|         public string Hash; | ||||
|     }; | ||||
|  | ||||
|     private readonly List<uint> _classJobIdsIgnoredForPets = [30]; | ||||
| @@ -40,7 +46,8 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber | ||||
|     private string _lastGlobalBlockPlayer = string.Empty; | ||||
|     private string _lastGlobalBlockReason = string.Empty; | ||||
|     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 static readonly Dictionary<uint, PlayerInfo> _playerInfoCache = new(); | ||||
|  | ||||
| @@ -70,7 +77,7 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber | ||||
|             await RunOnFrameworkThread(() => | ||||
|             { | ||||
|                 var addr = GetPlayerCharacterFromCachedTableByIdent(ident); | ||||
|                 var pc = GetPlayerCharacter(); | ||||
|                 var pc = _clientState.LocalPlayer!; | ||||
|                 var gobj = CreateGameObject(addr); | ||||
|                 // Any further than roughly 55y is out of range for targetting | ||||
|                 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); | ||||
|     } | ||||
|  | ||||
|     public PlayerCharacter GetPlayerCharacter() | ||||
|     { | ||||
|         EnsureIsOnFramework(); | ||||
|         return _clientState.LocalPlayer!; | ||||
|     } | ||||
|  | ||||
|     public IntPtr GetPlayerCharacterFromCachedTableByIdent(string characterName) | ||||
|     { | ||||
|         if (_playerCharas.TryGetValue(characterName, out var pchar)) return pchar.Address; | ||||
| @@ -372,21 +373,23 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber | ||||
|         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); | ||||
|         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)) | ||||
|         { | ||||
|             info.Name = p.Name.ToString(); | ||||
|             info.WorldId = p.HomeWorld.Id; | ||||
|             info.Hash = Crypto.GetHash256(info.Name + info.WorldId.ToString()); | ||||
|             info.Character.ObjectId = id; | ||||
|             info.Character.Name = chara.Name.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; | ||||
|         } | ||||
|  | ||||
| @@ -399,7 +402,6 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber | ||||
|         { | ||||
|             var gameObj = (GameObject*)p.Address; | ||||
|             var drawObj = gameObj->DrawObject; | ||||
|             var playerInfo = GetPlayerInfo(p); | ||||
|             bool isDrawing = false; | ||||
|             bool isDrawingChanged = false; | ||||
|             if ((nint)drawObj != IntPtr.Zero) | ||||
| @@ -411,20 +413,20 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber | ||||
|                     if (!isDrawing) | ||||
|                     { | ||||
|                         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)) | ||||
|                         { | ||||
|                             _lastGlobalBlockPlayer = playerInfo.Name; | ||||
|                             _lastGlobalBlockPlayer = p.Name; | ||||
|                             _lastGlobalBlockReason = "HasModelFilesInSlotLoaded"; | ||||
|                             isDrawingChanged = true; | ||||
|                         } | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         if (!string.Equals(_lastGlobalBlockPlayer, playerInfo.Name, StringComparison.Ordinal) | ||||
|                         if (!string.Equals(_lastGlobalBlockPlayer, p.Name, StringComparison.Ordinal) | ||||
|                             && !string.Equals(_lastGlobalBlockReason, "HasModelInSlotLoaded", StringComparison.Ordinal)) | ||||
|                         { | ||||
|                             _lastGlobalBlockPlayer = playerInfo.Name; | ||||
|                             _lastGlobalBlockPlayer = p.Name; | ||||
|                             _lastGlobalBlockReason = "HasModelInSlotLoaded"; | ||||
|                             isDrawingChanged = true; | ||||
|                         } | ||||
| @@ -432,10 +434,10 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     if (!string.Equals(_lastGlobalBlockPlayer, playerInfo.Name, StringComparison.Ordinal) | ||||
|                     if (!string.Equals(_lastGlobalBlockPlayer, p.Name, StringComparison.Ordinal) | ||||
|                         && !string.Equals(_lastGlobalBlockReason, "RenderFlags", StringComparison.Ordinal)) | ||||
|                     { | ||||
|                         _lastGlobalBlockPlayer = playerInfo.Name; | ||||
|                         _lastGlobalBlockPlayer = p.Name; | ||||
|                         _lastGlobalBlockReason = "RenderFlags"; | ||||
|                         isDrawingChanged = true; | ||||
|                     } | ||||
| @@ -444,7 +446,7 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber | ||||
|  | ||||
|             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; | ||||
| @@ -464,19 +466,32 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber | ||||
|         } | ||||
|  | ||||
|         IsAnythingDrawing = false; | ||||
|  | ||||
|         var playerCharaList = new Dictionary<string, (string Name, nint Address)>(StringComparer.Ordinal); | ||||
|         _performanceCollector.LogPerformance(this, "ObjTableToCharas", | ||||
|             () => { | ||||
|                 foreach (var p in _objectTable.OfType<PlayerCharacter>().Where(o => o.ObjectIndex < 200)) | ||||
|             () => | ||||
|             { | ||||
|                     CheckCharacterForDrawing(p); | ||||
|                     var info = GetPlayerInfo(p); | ||||
|                     playerCharaList.Add(info.Hash, (info.Name, p.Address)); | ||||
|                 _notUpdatedCharas.AddRange(_playerCharas.Keys); | ||||
|  | ||||
|                 foreach (var chara in _objectTable) | ||||
|                 { | ||||
|                     if (chara.ObjectIndex % 2 != 0 || chara.ObjectIndex >= 200) continue; | ||||
|  | ||||
|                     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.Remove(notUpdatedChara); | ||||
|                 } | ||||
|         ); | ||||
|         _playerCharas = playerCharaList; | ||||
|  | ||||
|                 _notUpdatedCharas.Clear(); | ||||
|             }); | ||||
|  | ||||
|         if (!IsAnythingDrawing && !string.IsNullOrEmpty(_lastGlobalBlockPlayer)) | ||||
|         { | ||||
|   | ||||
| @@ -3,7 +3,6 @@ using MareSynchronos.Utils; | ||||
| using Microsoft.Extensions.Hosting; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Collections.Concurrent; | ||||
| using System.Diagnostics; | ||||
| using System.Globalization; | ||||
| using System.Text; | ||||
|  | ||||
| @@ -14,7 +13,7 @@ public sealed class PerformanceCollectorService : IHostedService | ||||
|     private const string _counterSplit = "=>"; | ||||
|     private readonly ILogger<PerformanceCollectorService> _logger; | ||||
|     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(); | ||||
|  | ||||
|     public PerformanceCollectorService(ILogger<PerformanceCollectorService> logger, MareConfigService mareConfigService) | ||||
| @@ -23,49 +22,57 @@ public sealed class PerformanceCollectorService : IHostedService | ||||
|         _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(); | ||||
|  | ||||
|         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 | ||||
|         { | ||||
|             return func.Invoke(); | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|             st.Stop(); | ||||
|             list.Add(new(TimeOnly.FromDateTime(DateTime.Now), st.ElapsedTicks)); | ||||
|             var elapsed = DateTime.UtcNow.Ticks - dt; | ||||
| #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; } | ||||
|  | ||||
|         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 | ||||
|         { | ||||
|             act.Invoke(); | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|             st.Stop(); | ||||
|             list.Add(new(TimeOnly.FromDateTime(DateTime.Now), st.ElapsedTicks)); | ||||
|             var elapsed = DateTime.UtcNow.Ticks - dt; | ||||
| #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"); | ||||
|         } | ||||
|         var data = _performanceCounters.ToList(); | ||||
|         var data = PerformanceCounters.ToList(); | ||||
|         var longestCounterName = data.OrderByDescending(d => d.Key.Length).First().Key.Length + 2; | ||||
|         sb.Append("-Last".PadRight(15, '-')); | ||||
|         sb.Append('|'); | ||||
| @@ -169,12 +176,12 @@ public sealed class PerformanceCollectorService : IHostedService | ||||
|         { | ||||
|             await Task.Delay(TimeSpan.FromMinutes(10), _periodicLogPruneTask.Token).ConfigureAwait(false); | ||||
|  | ||||
|             foreach (var entries in _performanceCounters.ToList()) | ||||
|             foreach (var entries in PerformanceCounters.ToList()) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     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); | ||||
|                     } | ||||
|   | ||||
| @@ -7,10 +7,12 @@ namespace MareSynchronos.Utils; | ||||
|  | ||||
| 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> _hashListSHA256 = new(StringComparer.Ordinal); | ||||
|  | ||||
| #pragma warning disable SYSLIB0021 // Type or member is obsolete | ||||
|     private static readonly SHA256CryptoServiceProvider _sha256CryptoProvider = new(); | ||||
|     private static readonly SHA1CryptoServiceProvider _sha1CryptoProvider = new(); | ||||
|  | ||||
|     public static string GetFileHash(this string filePath) | ||||
|     { | ||||
| @@ -39,10 +41,7 @@ public static class Crypto | ||||
|         if (_hashListSHA1.TryGetValue(stringToCompute, out var hash)) | ||||
|             return hash; | ||||
|  | ||||
|         using SHA1CryptoServiceProvider cryptoProvider = new(); | ||||
|         var computedHash = BitConverter.ToString(cryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(stringToCompute))).Replace("-", "", StringComparison.Ordinal); | ||||
|         _hashListSHA1[stringToCompute] = computedHash; | ||||
|         return computedHash; | ||||
|         return _hashListSHA1[stringToCompute] = BitConverter.ToString(_sha1CryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(stringToCompute))).Replace("-", "", StringComparison.Ordinal); | ||||
|     } | ||||
|  | ||||
|     private static string GetOrComputeHashSHA256(string stringToCompute) | ||||
| @@ -50,10 +49,7 @@ public static class Crypto | ||||
|         if (_hashListSHA256.TryGetValue(stringToCompute, out var hash)) | ||||
|             return hash; | ||||
|  | ||||
|         using SHA256CryptoServiceProvider cryptoProvider = new(); | ||||
|         var computedHash = BitConverter.ToString(cryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(stringToCompute))).Replace("-", "", StringComparison.Ordinal); | ||||
|         _hashListSHA256[stringToCompute] = computedHash; | ||||
|         return computedHash; | ||||
|         return _hashListSHA256[stringToCompute] = BitConverter.ToString(_sha256CryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(stringToCompute))).Replace("-", "", StringComparison.Ordinal); | ||||
|     } | ||||
|  | ||||
| #pragma warning restore SYSLIB0021 // Type or member is obsolete | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 rootdarkarchon
					rootdarkarchon