Curious. Article re:speed - only because I started down the Linq path..

joseguiaCES
Participant
Participant

Curious. Article re:speed - only because I started down the Linq path..

joseguiaCES
Participant
Participant

So the article here,  if I read it right,  is saying  a simple for each is faster than say LinQ. 

 

I'm just curious, I haven't ran any independent tests on my own just yet, but I've seen enough code shared here to hazard a guess that someone's compared varying methods before and could shine some light on the varying approaches for speeds sake in accessing the dwg dB and it's enormous collection of objects.

 

Here's a blurb from the article in a TLDR fashion:

...

At NDepend we value your time, so here are our key findings:

  • When iterating over a C# array, the foreach way is the quickest.
  • In a for loop, storing the array.Length or list.Count in a variable does not improve performance and may actually decrease it.
  • for loop is most efficient on a List<T> than foreach.
  • However, the most efficient way to loop over a List<T> involves accessing the internal array via a Span<T> using Span<int> span = CollectionsMarshal.AsSpan(list). This approach is beneficial in performance-critical situations, provided that no elements are added or removed from the list.
  • Using a lambda for iterating the LINQ way, like in Array.Foreach(array, item => ...) or list.ForEach(item => ...) is unsurprisingly, way slower than regular for and foreach loops.

...

0 Likes
Reply
Accepted solutions (1)
328 Views
4 Replies
Replies (4)

ActivistInvestor
Advisor
Advisor
Accepted solution

The authors of that article are making an apples-verses oranges comparison.

 

When one uses a local variable to store the count of a list and then iterate over the list using the local variable as the upper bounds, they're neglecting to consider that the list can change while iterating over it using for(), including adding or removing elements. In contrast to that, when one uses the Count property of the list directly in a for() loop, it is evaluated on each iteration. That means that if the size of the list changes while you are iterating over it, it will iterate over the elements added to it.

 

public static void ListIteration()
{
   List<int> list1 = Enumerable.Range(0, 100).ToList();
   List<int> list2 = new List<int>();
   CollectionsMarshal.SetCount(list2, list1.Count);
   Span<int> span1 = CollectionsMarshal.AsSpan(list1);
   Span<int> span2 = CollectionsMarshal.AsSpan(list2);
   span1.CopyTo(span2);

   /// Using a variable as the upper range:
   /// 
   int count = list1.Count;
   int total = 0;
   for(int i = 0; i < count; i++)
   {
      if(i < 10)
         list1.Add(i + 101);
      total += list1[i];
   }

   Console.WriteLine($"list1.Count = {list1.Count} Total = {total}");

   /// Using the list's Count property as the upper range,
   /// adding elements during iteration, all elements in
   /// the list are iterated over, rather than only those
   /// that were in the list at the start of iteration:

   total = 0;

   for(int i= 0; i < list2.Count; i++)
   {
      if(i < 10)
         list2.Add(i + 101);
      total += list2[i];
   }

   Console.WriteLine($"list2.Count = {list2.Count} Total = {total}");

}

 

While removing elements from the list below the position of the current iterator variable will really screw things up and likely cause a failure, it is not too uncommon to append elements to a list while iterating over it, since all of the elements added will be iterated over as well. That is a common practice in cases where recursive patterns are converted to iterative patterns.

 

I don't have any comments on the performance observations other than that I know that Enumerable.ToArray() performance has improved dramatically since the early days of .NET.

 


Using a lambda for iterating the LINQ way, like in Array.Foreach(array, item => ...) or list.ForEach(item => ...) is unsurprisingly, way slower than regular for and foreach loops.

...


The ForEach() methods of Array and List<T> aren't Linq.  They just use delegates as most Linq methods require. Invoking a delegate has overhead (it uses callvirt, which makes it roughly equivalent to invoking a virtual method of an instance of a class), and doing that once for each element in an array or list definitely isn't going to be competitive with iterating using for() or foreach(), so the results they observed really isn't surprising, and because Linq is also delegate-intensive, it is also slower than using language constructs. With Linq, you not only have the overhead of delegates, you also have the overhead of IEnumerable<T>, which requires two method invocations to retrieve each element. And, add to those the overhead of non-static variable captures in delegates.

0 Likes

joseguiaCES
Participant
Participant

I could. not. agree. more with everything you shared. I've lost track of how many times I got burned by the modification of elements in a collection, and not realizing which language/technology I was using at the time as they don't all handle modifications the same, at least in my experience. 

 

I'm only asking/curious because I am about to dive into a large project and started laying the ground work for object/item collection iteration, etc .. and spent a bit of time re-working the acad-2-linq project on github for 2025, and was operating under the assumption (i know ..  assuming) that between that and some of the updated .net features, I shouldn't have to worry about the speed  .. too much.

 

Anyhow - your feedback is greatly appreciated, gonna keep marching forward and .. for - each .. where it makes sense. I really don't want to spend the time to speed-test unless the application becomes noticeably slow, however I would have if the replies here warranted it.

0 Likes

joseguiaCES
Participant
Participant

I could. not. agree. more with everything you shared. I've lost track of how many times I got burned by the modification of elements in a collection, and not realizing which language/technology I was using at the time as they don't all handle modifications the same, at least in my experience. 

 

I'm only asking/curious because I am about to dive into a large project and started laying the ground work for object/item collection iteration, etc .. and spent a bit of time re-working the acad-2-linq project on github for 2025, and was operating under the assumption (i know ..  assuming) that between that and some of the updated .net features, I shouldn't have to worry about the speed  .. too much.

 

Anyhow - your feedback is greatly appreciated, I may have to time-test a handful of the operations, where as I wasn't all that concerned on the onset. 

 

I appreciate the feedback, if I do end up testing speed differences, I'll share my results here. 

0 Likes

joseguiaCES
Participant
Participant

what in the world is going on, I've replied twice now. so weird .. it was a lengthy reply, but perhaps unnecessarily so.

 

guess I'll be short in case this one doesn't make it either - 

  • @ActivistInvestor thank you for your reply. I very much agree with all you stated and shared.
  • I'm starting a large project - so re-worked acad-2-linq for acad2025 on github and now second guessing .. maybe
  • if I notice ANY speed delays or issues, I'm going to test and re-work

 

thanks again

 

0 Likes