Last active
April 22, 2026 20:24
-
-
Save udlose/78281c1e1a995ac4e0bf7400c19416bf to your computer and use it in GitHub Desktop.
for vs. foreach iteration over List<Guid>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // LinqPad BenchmarkDotNet script | |
| // Compares for-loop (indexer) vs foreach (struct enumerator) over List<T> | |
| // at sizes relevant to EntryDoor slab definition lists. | |
| // | |
| // Required NuGet: BenchmarkDotNet | |
| // | |
| // NOTE: Set the target framework and runtime in your LinqPad/project settings. | |
| void Main() | |
| { | |
| BenchmarkRunner.Run<ForVsForeachBenchmarks>(DefaultConfig.Instance | |
| .WithOption(ConfigOptions.DisableOptimizationsValidator, true) | |
| .AddColumn(RankColumn.Arabic) | |
| .AddColumn(StatisticColumn.OperationsPerSecond) | |
| .WithSummaryStyle(SummaryStyle.Default.WithRatioStyle(RatioStyle.Trend))); | |
| } | |
| /// <summary> | |
| /// Compares for-loop with indexer access vs foreach (struct enumerator) | |
| /// over a concretely-typed List of reference-type elements. | |
| /// | |
| /// The element type simulates EntryDoorSlabDefinition: a reference type | |
| /// with two Guid fields, matching the access pattern in EnsureIndexBuilt | |
| /// where each iteration reads properties from the element. | |
| /// </summary> | |
| [MemoryDiagnoser] | |
| [HideColumns("Error", "StdDev", "Median")] | |
| public class ForVsForeachBenchmarks | |
| { | |
| // Sizes matching real entry door datasets: | |
| // 68 = Masonite Entry Doors slab count | |
| // 1393 = ThermaTru slab count | |
| // 7744 = Masonite Interior Doors slab count | |
| // 10000 = round number requested by Dave | |
| [Params(68, 1393, 7744, 10_000)] | |
| public int Size { get; set; } | |
| private List<SlabElement> _list; | |
| [GlobalSetup] | |
| public void Setup() | |
| { | |
| _list = new List<SlabElement>(Size); | |
| for (int i = 0; i < Size; i++) | |
| { | |
| _list.Add(new SlabElement(Guid.NewGuid(), Guid.NewGuid())); | |
| } | |
| } | |
| [Benchmark(Baseline = true)] | |
| public Guid Foreach_StructEnumerator() | |
| { | |
| Guid last = Guid.Empty; | |
| foreach (SlabElement item in _list) | |
| { | |
| last = item.QuestionID; | |
| } | |
| return last; | |
| } | |
| [Benchmark] | |
| public Guid For_IndexerAccess() | |
| { | |
| Guid last = Guid.Empty; | |
| for (int i = 0; i < _list.Count; i++) | |
| { | |
| last = _list[i].QuestionID; | |
| } | |
| return last; | |
| } | |
| } | |
| /// <summary> | |
| /// Simulates EntryDoorSlabDefinition or GenericQuestionToAnswer: | |
| /// a reference type with two Guid fields accessed during iteration. | |
| /// </summary> | |
| public class SlabElement | |
| { | |
| public Guid QuestionID { get; set; } | |
| public Guid AnswerID { get; set; } | |
| public SlabElement(Guid questionId, Guid answerId) | |
| { | |
| QuestionID = questionId; | |
| AnswerID = answerId; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| * Summary * | |
| BenchmarkDotNet v0.15.8, Windows 11 (10.0.26200.8246/25H2/2025Update/HudsonValley2) | |
| 12th Gen Intel Core i7-12800H 2.40GHz, 1 CPU, 20 logical and 14 physical cores | |
| [Host] : .NET Framework 4.8.1 (4.8.9325.0), X64 RyuJIT VectorSize=256 | |
| DefaultJob : .NET Framework 4.8.1 (4.8.9325.0), X64 RyuJIT VectorSize=256 | |
| | Method | Size | Mean | Op/s | Ratio | RatioSD | Rank | Allocated | Alloc Ratio | | |
| |------------------------- |------ |-------------:|-------------:|-------------:|--------:|-----:|----------:|------------:| | |
| | Foreach_StructEnumerator | 68 | 163.44 ns | 6,118,292.5 | baseline | | 2 | - | NA | | |
| | For_IndexerAccess | 68 | 56.31 ns | 17,757,451.2 | 2.90x faster | 0.06x | 1 | - | NA | | |
| | | | | | | | | | | | |
| | Foreach_StructEnumerator | 1393 | 3,133.66 ns | 319,115.2 | baseline | | 2 | - | NA | | |
| | For_IndexerAccess | 1393 | 1,227.10 ns | 814,926.6 | 2.55x faster | 0.05x | 1 | - | NA | | |
| | | | | | | | | | | | |
| | Foreach_StructEnumerator | 7744 | 17,631.53 ns | 56,716.6 | baseline | | 2 | - | NA | | |
| | For_IndexerAccess | 7744 | 6,577.42 ns | 152,035.4 | 2.68x faster | 0.07x | 1 | - | NA | | |
| | | | | | | | | | | | |
| | Foreach_StructEnumerator | 10000 | 22,692.85 ns | 44,066.7 | baseline | | 2 | - | NA | | |
| | For_IndexerAccess | 10000 | 8,326.14 ns | 120,103.6 | 2.73x faster | 0.06x | 1 | - | NA | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // * Summary * | |
| BenchmarkDotNet v0.15.8, Windows 11 (10.0.26200.8246/25H2/2025Update/HudsonValley2) | |
| 12th Gen Intel Core i7-12800H 2.40GHz, 1 CPU, 20 logical and 14 physical cores | |
| [Host] : .NET Framework 4.8.1 (4.8.9325.0), X86 LegacyJIT | |
| DefaultJob : .NET Framework 4.8.1 (4.8.9325.0), X86 LegacyJIT | |
| | Method | Size | Mean | Op/s | Ratio | RatioSD | Rank | Allocated | Alloc Ratio | | |
| |- | |
| ------------------------ |------ |-------------:|-------------:|-------------:|--------:|-----:|----------:|------------:| | |
| | Foreach_StructEnumerator | 68 | 171.42 ns | 5,833,701.8 | baseline | | 2 | - | NA | | |
| | For_IndexerAccess | 68 | 80.93 ns | 12,355,835.0 | 2.12x faster | 0.07x | 1 | - | NA | | |
| | | | | | | | | | | | |
| | Foreach_StructEnumerator | 1393 | 3,065.97 ns | 326,161.0 | baseline | | 2 | - | NA | | |
| | For_IndexerAccess | 1393 | 1,622.88 ns | 616,187.1 | 1.89x faster | 0.03x | 1 | - | NA | | |
| | | | | | | | | | | | |
| | Foreach_StructEnumerator | 7744 | 17,128.98 ns | 58,380.6 | baseline | | 2 | - | NA | | |
| | For_IndexerAccess | 7744 | 9,455.26 ns | 105,761.3 | 1.82x faster | 0.09x | 1 | - | NA | | |
| | | | | | | | | | | | |
| | Foreach_StructEnumerator | 10000 | 21,224.53 ns | 47,115.3 | baseline | | 2 | - | NA | | |
| | For_IndexerAccess | 10000 | 12,265.93 ns | 81,526.6 | 1.73x faster | 0.09x | 1 | - | NA | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| * Summary * | |
| BenchmarkDotNet v0.15.8, Windows 11 (10.0.26200.8246/25H2/2025Update/HudsonValley2) | |
| 12th Gen Intel Core i7-12800H 2.40GHz, 1 CPU, 20 logical and 14 physical cores | |
| .NET SDK 10.0.202 | |
| [Host] : .NET 10.0.6 (10.0.6, 10.0.626.17701), X64 RyuJIT x86-64-v3 | |
| | Method | Size | Mean | Op/s | Ratio | RatioSD | Rank | Allocated | Alloc Ratio | | |
| |------------------------- |------ |------------:|-------------:|-------------:|--------:|-----:|----------:|------------:| | |
| | Foreach_StructEnumerator | 68 | 32.48 ns | 30,789,043.5 | baseline | | 1 | - | NA | | |
| | For_IndexerAccess | 68 | 34.70 ns | 28,822,482.8 | 1.07x slower | 0.02x | 2 | - | NA | | |
| | | | | | | | | | | | |
| | Foreach_StructEnumerator | 1393 | 839.80 ns | 1,190,765.6 | baseline | | 2 | - | NA | | |
| | For_IndexerAccess | 1393 | 752.16 ns | 1,329,505.2 | 1.12x faster | 0.02x | 1 | - | NA | | |
| | | | | | | | | | | | |
| | Foreach_StructEnumerator | 7744 | 4,746.08 ns | 210,700.2 | baseline | | 2 | - | NA | | |
| | For_IndexerAccess | 7744 | 4,205.01 ns | 237,811.7 | 1.13x faster | 0.02x | 1 | - | NA | | |
| | | | | | | | | | | | |
| | Foreach_StructEnumerator | 10000 | 9,259.58 ns | 107,996.3 | baseline | | 1 | - | NA | | |
| | For_IndexerAccess | 10000 | 9,679.24 ns | 103,313.9 | 1.09x slower | 0.26x | 1 | - | NA | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment