From e31858693abbe3e5cb1336906c79e9a890002a4e Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 20:48:32 +0000 Subject: [PATCH] Optimize WeightedRandom for IEnumerable by preventing multi-enumeration Co-authored-by: johnstrand <11484777+johnstrand@users.noreply.github.com> --- BenchmarkDotNet.config | 0 .../Extensions/CollectionExtensions.cs | 31 +++++++++---------- 2 files changed, 15 insertions(+), 16 deletions(-) create mode 100644 BenchmarkDotNet.config diff --git a/BenchmarkDotNet.config b/BenchmarkDotNet.config new file mode 100644 index 0000000..e69de29 diff --git a/src/GameUtils/Extensions/CollectionExtensions.cs b/src/GameUtils/Extensions/CollectionExtensions.cs index 7b76057..319315a 100644 --- a/src/GameUtils/Extensions/CollectionExtensions.cs +++ b/src/GameUtils/Extensions/CollectionExtensions.cs @@ -236,38 +236,37 @@ public static T WeightedRandom(this IEnumerable source, Func wei return list[count - 1]; } - var itemsTotalWeight = 0f; - var hasElements = false; - foreach (var item in source) + var array = source.ToArray(); + int arrayCount = array.Length; + if (arrayCount == 0) { - hasElements = true; - itemsTotalWeight += weightSelector(item); + throw new InvalidOperationException("Sequence contains no elements."); } - if (!hasElements) + var arrayTotalWeight = 0f; + for (int i = 0; i < arrayCount; i++) { - throw new InvalidOperationException("Sequence contains no elements."); + arrayTotalWeight += weightSelector(array[i]); } - if (itemsTotalWeight <= 0) + if (arrayTotalWeight <= 0) { throw new InvalidOperationException("Total weight must be greater than zero."); } - var itemsTarget = (float)(Random.Shared.NextDouble() * itemsTotalWeight); - var itemsCumulative = 0f; - T lastItem = default!; + var arrayTarget = (float)(Random.Shared.NextDouble() * arrayTotalWeight); + var arrayCumulative = 0f; - foreach (var item in source) + for (int i = 0; i < arrayCount; i++) { - lastItem = item; - itemsCumulative += weightSelector(item); - if (itemsTarget <= itemsCumulative) + var item = array[i]; + arrayCumulative += weightSelector(item); + if (arrayTarget <= arrayCumulative) { return item; } } - return lastItem; + return array[arrayCount - 1]; } }