From 8a7690e13b74a91a1abe61575eb11ad1a56e43b9 Mon Sep 17 00:00:00 2001 From: Chomp Date: Fri, 25 Jul 2025 18:26:09 +0100 Subject: [PATCH] Improved performance of `GetRandomElement` when processing IEnnumerables --- .../SPTarkov.Server.Core/Helpers/BotHelper.cs | 4 ++-- .../SPTarkov.Server.Core/Utils/RandomUtil.cs | 20 ++++++++----------- UnitTests/Mock/MockRandomUtil.cs | 8 ++++---- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/Libraries/SPTarkov.Server.Core/Helpers/BotHelper.cs b/Libraries/SPTarkov.Server.Core/Helpers/BotHelper.cs index d28a08d9..a1fc26cf 100644 --- a/Libraries/SPTarkov.Server.Core/Helpers/BotHelper.cs +++ b/Libraries/SPTarkov.Server.Core/Helpers/BotHelper.cs @@ -226,7 +226,7 @@ public class BotHelper( ); // Return a random string from names - return randomUtil.GetCollectionValue(chosenFactionDetails.FirstNames); + return randomUtil.GetRandomElement(chosenFactionDetails.FirstNames); } _pmcNameCache.TryAdd(cacheKey, matchingNames); @@ -234,6 +234,6 @@ public class BotHelper( eligibleNames = matchingNames; } - return randomUtil.GetCollectionValue(eligibleNames); + return randomUtil.GetRandomElement(eligibleNames); } } diff --git a/Libraries/SPTarkov.Server.Core/Utils/RandomUtil.cs b/Libraries/SPTarkov.Server.Core/Utils/RandomUtil.cs index 49a681c9..2ac16cec 100644 --- a/Libraries/SPTarkov.Server.Core/Utils/RandomUtil.cs +++ b/Libraries/SPTarkov.Server.Core/Utils/RandomUtil.cs @@ -115,9 +115,9 @@ public class RandomUtil(ISptLogger _logger, ICloner _cloner) /// /// The collection of strings to select a random value from. /// A randomly selected string from the array. - public virtual T GetCollectionValue(IEnumerable collection) + public virtual T GetRandomElement(IEnumerable collection) { - // We can call `count` directly if it's a list + // Already a List if (collection is IList list) { if (!list.Any()) @@ -128,13 +128,9 @@ public class RandomUtil(ISptLogger _logger, ICloner _cloner) return list[GetInt(0, list.Count - 1)]; } - var count = collection.Count(); // Run query and count elements - if (count == 0) - { - throw new InvalidOperationException("Sequence contains no elements."); - } - - return collection.ElementAt(GetInt(0, count - 1)); + // Faster than Reservoir Sampling or calling collection.Count() and doing above + var toListedCollection = collection.ToList(); + return toListedCollection[GetInt(0, toListedCollection.Count - 1)]; } /// @@ -147,7 +143,7 @@ public class RandomUtil(ISptLogger _logger, ICloner _cloner) public virtual TKey GetKey(Dictionary dictionary) where TKey : notnull { - return GetCollectionValue(dictionary.Keys); + return GetRandomElement(dictionary.Keys); } /// @@ -160,7 +156,7 @@ public class RandomUtil(ISptLogger _logger, ICloner _cloner) public virtual TVal GetVal(Dictionary dictionary) where TKey : notnull { - return GetCollectionValue(dictionary.Values); + return GetRandomElement(dictionary.Values); } /// @@ -467,7 +463,7 @@ public class RandomUtil(ISptLogger _logger, ICloner _cloner) public virtual T? GetArrayValue(IEnumerable list) { - return GetCollectionValue(list); + return GetRandomElement(list); } /// diff --git a/UnitTests/Mock/MockRandomUtil.cs b/UnitTests/Mock/MockRandomUtil.cs index 9ae204ca..c95fb485 100644 --- a/UnitTests/Mock/MockRandomUtil.cs +++ b/UnitTests/Mock/MockRandomUtil.cs @@ -47,7 +47,7 @@ public class MockRandomUtil(ISptLogger _logger, ICloner _cloner) return true; } - public override T GetCollectionValue(IEnumerable collection) + public override T GetRandomElement(IEnumerable collection) { if (!collection.Any()) { @@ -59,12 +59,12 @@ public class MockRandomUtil(ISptLogger _logger, ICloner _cloner) public override TKey GetKey(Dictionary dictionary) { - return GetCollectionValue(dictionary.Keys); + return GetRandomElement(dictionary.Keys); } public override TVal GetVal(Dictionary dictionary) { - return GetCollectionValue(dictionary.Values); + return GetRandomElement(dictionary.Values); } public override double GetNormallyDistributedRandomNumber( @@ -125,7 +125,7 @@ public class MockRandomUtil(ISptLogger _logger, ICloner _cloner) public override T? GetArrayValue(IEnumerable list) where T : default { - return GetCollectionValue(list); + return GetRandomElement(list); } public override bool RollChance(double chance, double scale = 1)