1

C#: For Vs ForEach Vs While

C#: For Vs ForEach Vs While

In this article we’ll benchmark iterating over commonly used collections and data structures with the following loop constructs in C#: For Vs ForEach Vs While.

A List of strings needs to be written to a flat-file; every element in a dictionary object needs to be updated; every row in a DataTable with values from a database needs to be processed.

Every programmer has their own subconscious favorite way of looping over a collection. You can see it in their code. I tend to use a For loop over an Array, List, but usually implement a ForEach iterating over a Dictionary or DataTable.

But that’s when this curious consultant started wondering what construct is best? Hence a showdown in C#: For Vs ForEach Vs While.

Getting Started:

I wrote a C# Console application to test common scenarios.

The code is written in Visual Studio 2013 targeting .Net Framework version 4.5 x64. The source code is available at the end of this blog so you can benchmark it on your own system.

In a nutshell, the code does the following:
1) Creates 6 types of data structures:

2) Populates them with 100, 50000, 250000 or 5000000 values.

3) Uses one of the following looping constructs:

to iterate over each structure, summing up values, which also verifies every loop ends up with the same total and thus looped the exact same number of iterations.

The code assumes all summations will happen with no exception testing.

The single threaded For/ForEach/While loops are benchmarked as a group; the Parallel loops are benchmarked as their own group.

The exe file was installed and run on an Alienware M17X R3 running Windows 7 64-bit with 32 GB memory on an i7-2820QM processor.

Start Your Engines!

Before starting, I thought the For loop construct, when it could be used, would win out (although not always by a huge margin).

Let’s see what happened on my machine.

All times are indicated in seconds.milliseconds format and are the averages over 3 runs.

Lower numbers indicate faster performance. Winner marked in green.

Looping over an int[]:

# loops:

100

50,000

250,000

5,000,000

For

0.0000003

0.0001231

0.0006048

0.0120585

ForEach

0.0000003

0.0001038

0.0005160

0.0104156

While

0.0000003

0.0001230

0.0006072

0.0120238

 

 

 

 

Parallel For

0.0001180

0.0002005

0.0003407

0.0048868

Parallel ForEach

0.0000252

0.0001993

0.0003707

0.0048805

Looping over a List<string>:

# loops:

100

50,000

250,000

5,000,000

For

0.0000315

0.0074959

0.0385195

0.8088743

ForEach

0.0000161

0.0076712

0.0397261

0.8358611

While

0.0000165

0.0073865

0.0379197

0.8056663

 

 

 

 

Parallel For

0.0001085

0.0010435

0.0051182

0.1053082

Parallel ForEach

0.0000426

0.0011157

0.0053677

0.1112027

Looping over Dictionary<int, string>:

# loops:

100

50,000

250,000

5,000,000

For

0.0000481

0.0087858

0.0415127

0.8800884

ForEach

0.0000165

0.0079057

0.0395421

0.8451354

While

0.0000181

0.0081552

0.0416730

0.8938068

 

 

 

 

Parallel For

0.0005274

0.0011904

0.0056796

0.1167537

Parallel ForEach

0.0001318

0.0060366

0.0177860

0.3203043

Looping over List<ForVsForeachVsWhileTestClass>:

# loops:

100

50,000

250,000

5,000,000

For

0.0000019

0.0009697

0.0049369

0.0996637

ForEach

0.0000027

0.0012800

0.0052461

0.1239720

While

0.0000019

0.0009898

0.0049500

0.0996278

 

 

 

 

Parallel For

0.0000213

0.0002819

0.0009811

0.0170327

Parallel ForEach

0.0000280

0.0003510

0.0013625

0.0247584

Looping over DataRow[]:

# loops:

100

50,000

250,000

5,000,000

For

0.0000781

0.0091159

0.0259026

0.5005515

ForEach

0.0000142

0.0053539

0.0372900

0.5069411

While

0.0000153

0.0053526

0.0208661

0.4759353

 

 

 

 

Parallel For

0.0000548

0.0041410

0.0077805

0.2391097

Parallel ForEach

0.0000473

0.0007628

0.0201420

0.0735779

Looping over DataTable:

# loops:

100

50,000

250,000

5,000,000

For

0.0000485

0.0133429

0.0708401

1.7711373

ForEach

0.0000272

0.0066912

0.0317441

0.6381469

While

0.0000240

0.0132004

0.0706376

1.7711429

 

 

 

 

Parallel For

0.0000560

0.0014620

0.0082812

0.2065083

Parallel ForEach

0.0005665

0.0103189

0.0518601

0.8435395

A Few Surprises!

I certainly wouldn’t have picked that a ForEach loop (both single and Parallel) is consistently the fastest way to loop over an int[]! All these years I’ve always used a For loop over something so simple. That may have to chance now. I can only think that this is because it takes longer for a For loop to calculate and access a particular array index than it does a ForEach loop to assign the next value to allocated memory.

I’m perplexed as to why the While loop had the best times over a List and DataRow[]. It also held up well over List.

Obviously the single threaded ForEach is best over a Dictionary<int,string> and DataTable.

On the Parallel side of things, Parallel.For won out most of the time, which doesn’t surprise me with the multiple simultaneous memory allocations and assignments the Parallel.ForEach has to perform. The anomaly seems to be the DataRow[], where I can only surmise the reasoning is the same as with the basic int[].

What’s interesting is with each of these tests, there always appeared to be one clear-cut winner regardless of the number of loop iterations performed. That is, one looping construct that performed well in a lower number of iterations didn’t falter and held its own up to the 5,000,000 threshold.

Final Say and What You Should Use:

The greatest significant time difference, both single and multi-threaded, was looping over a DataTable: ForEach single threaded or Parallel.For multi-threaded. They proved to be at least 50% faster on average.

The only other significant time differences occurred on the Parallel side of things:

  • Looping over the Dictionary<int,string> with 50,000+ values, Parallel.For ran in 1/3 the time or faster than a Parallel.ForEach.
  • Conversely, over a DataRow[] the Parallel.ForEach proved dominant by a wide time difference in most cases.

With everything else, unless the need is there to micro-optimize every single millisecond or you perform your own benchmarking, do yourself the favor and stick with what’s easiest.

The Code:

  • Roman Pokrovskij

    I predict that “for” will show better performacne then “foreach” in case when you need to use the length after or before the loop.