mariachiacero.com

Optimizing Array Iteration in .NET: A Performance Analysis

Written on

Chapter 1: Introduction to Performance Benchmarking

Recently, I have been examining an older system to explore ways to enhance its performance. While there are numerous approaches to boost efficiency, I aimed to implement minor changes that could collectively lead to significant improvements, without undertaking extensive refactoring which could introduce risks. Although upgrading the entire system would be ideal, these incremental adjustments may be the only viable options for developers.

This exploration has sparked my curiosity about identifying the most effective methods for executing routine tasks. I am gradually compiling a series of evaluations, which may involve contrasting various libraries against native features or directly comparing common tasks. Regardless, this journey promises both valuable insights and enjoyment.

Iterating and Accessing Arrays

Today, I focused on various techniques for iterating through an array. While it may seem straightforward, software engineers frequently work with both lists and arrays in C#, making it worthwhile to address these methods.

A crucial aspect of accurate benchmarking is assigning the array item to a variable. Simply iterating without accessing the value can lead to misleading results. Below are the methods utilized and evaluated with BenchmarkDotNet:

  • ForEachArray
  • ForEach
  • GetEnumerator
  • Span
  • ForEachTheory

Given the similarities in previous tests, I suspect that the Span methods will emerge as the top performers. Conversely, I anticipate the GetEnumerator method to be the least efficient. I also expect the for loop to perform closely to the Span methods, with foreach trailing slightly behind.

Setup for Benchmarking

Using BenchmarkDotNet to create a console application streamlines the process of running these tests. To cover legacy systems, tests will be conducted in both .NET Framework 4.8 and .NET 6.0, representing the current Long Term Support versions. This dual approach allows for a comparison between older and newer frameworks.

The testing process involves creating an array of strings with two different sizes: 100 and 10,000 elements. The tests will repeatedly iterate through each item until BenchmarkDotNet provides satisfactory results, yielding insights into the most efficient methods for future use.

Methods for Iteration

Next, I developed a method for each array iteration technique that I plan to test. Each method performs a single task to maintain a fair benchmark. Specifically, each method will iterate through the array, access each item, and assign it to a variable. Upon processing all items, the method concludes and logs the time taken for completion.

Results of the Benchmarking

Fortunately, BenchmarkDotNet includes a Rank column to clearly indicate which test iteration performed best. The Mean column allows for straightforward comparisons of runtime across tests. Some methods reveal clear winners, while others show negligible differences.

.NET Framework 4.8 — Iteration Results

Copy| Rank | Method | N | Mean |

---: | ------------- | ----- | -------------: |
1 | ForEachLoop | 100 | 305.9 ns |
2 | ForLoop | 100 | 343.9 ns |
3 | ArrayForEach | 100 | 451.1 ns |
4 | GetEnumerator | 100 | 10,561.3 ns |
5 | ForEachLoop | 10000 | 30,064.2 ns |
6 | ForLoop | 10000 | 33,541.9 ns |
7 | ArrayForEach | 10000 | 43,825.3 ns |
8 | GetEnumerator | 10000 | 1,055,741.5 ns |

.NET 6.0 — Iteration Results

Copy| Rank | Method | N | Mean |

---: | ------------- | ----- | -----------: |
1 | SpanForEach | 100 | 141.9 ns |
2 | ForLoop | 100 | 173.6 ns |
2 | ForEachLoop | 100 | 175.2 ns |
2 | SpanFor | 100 | 175.5 ns |
3 | ArrayForEach | 100 | 370.7 ns |
4 | GetEnumerator | 100 | 6,190.5 ns |
5 | SpanForEach | 10000 | 12,333.7 ns |
6 | ForLoop | 10000 | 16,181.6 ns |
6 | SpanFor | 10000 | 16,187.0 ns |
6 | ForEachLoop | 10000 | 16,251.8 ns |
7 | ArrayForEach | 10000 | 36,155.1 ns |
8 | GetEnumerator | 10000 | 611,853.5 ns |

Insights from the Results

.NET Framework 4.8 Insights

Surprisingly, the foreach method emerged as the top performer. Given its poor performance with lists, this outcome was unexpected. It outperformed the for loop in both array sizes. Clearly, if you're using arrays, it's time to refactor your code to utilize foreach instead of Array.ForEach.

.NET 6.0 Insights

In .NET 6.0, the Span foreach methods took the top spot, closely followed by the for, foreach, and Span for methods. This indicates that major refactoring might not be necessary, as the gains are marginal. However, it's advisable to use Span combined with foreach in new code, while Array.ForEach has fallen behind significantly.

Conclusion: Choosing the Right Iterator

Although the differences may not be groundbreaking, the foreach loop stands out as the preferred choice for both .NET 6.0 and .NET Framework 4.8 applications. Just remember to pair it with Span in .NET 6.0 for optimal performance.

Code Repository

All the code used in these benchmarks is open source and available on GitHub. The results were generated through a GitHub Action that runs the benchmark tests, and you can find detailed outputs in that repository.

Explore shocking iterator performance benchmarks in C# and gain insights into efficient coding practices.

Uncover the fastest way to iterate a List in C#, which defies common assumptions.

Share the page:

Twitter Facebook Reddit LinkIn

-----------------------

Recent Post:

Rediscovering the Joy of Childhood: A Guide to Playful Living

Explore ways to embrace a childlike perspective and infuse joy into your daily life through simple, playful actions.

Unveiling Leonardo Da Vinci: Ten Surprising Insights

Explore ten surprising facts about Leonardo Da Vinci, revealing his lesser-known interests and achievements beyond art.

Unlocking the Secrets of SEC Filings for Savvy Investors

Learn how to leverage SEC filings to make informed investment decisions and understand a company's financial health.

Protecting Your Inner Sanctuary: Steps for Personal Growth

Discover how to safeguard your inner self from negativity and external influences through education, conscience, and purpose.

Unlocking the Mysteries of the Fibonacci Sequence

Discover the Fibonacci sequence, its significance in nature, and how to code it in Python through various methods.

Embracing Life Lessons in Your 30s: A Reflective Journey

Discover valuable insights and reflections gained from experiences in your 30s, emphasizing personal growth and self-acceptance.

Crafting a Lasting Legacy: The Firefighter's Journey of Impact

Explore how firefighters create a meaningful legacy through service, mentorship, and personal growth.

Understanding True Scarcity in Investment for Success

Discover how recognizing true scarcity can elevate your investment strategies and enhance your financial future.