title: Performance of FirstOrDefault()
body: <p>Looking at a section of the webapp I work on today with a performance profiler. I thought that a Union was causing some delays, but found other surprising results instead.</p>
  
  <p>One of the causes of slowdown appeared to be FirstOrDefault.</p>
  
  <p>It was a very simple LINQ query that looked like this:</p>
  
  <pre><code>foreach(Report r in reports)
      IDTOStudy study = studies.FirstOrDefault(s =&gt; s.StudyID == r.StudyID);
  </code></pre>
  
  <p>I created a small method to duplicate the behaviour I figured FirstOrDefault was doing.</p>
  
  <pre><code>private IDTOStudy GetMatchingStudy(Report report, IList&lt;IDTOStudy&gt; studies)
  {
      foreach (var study in studies)
      if (study.StudyID == report.StudyID)
          return study;
  
      return null;
  }
  </code></pre>
  
  <p>This method replaced the FirstOrDefault to look like this:</p>
  
  <pre><code>foreach(Report r in reports)
      IDTOStudy study = GetMatchingStudy(r, studies);
  </code></pre>
  
  <p>Looking at the new code running with the performance profiler showed <code>FirstOrDefault</code> to take twice as long to complete as my new method. This was a shock to see.</p>
  
  <p>I must be doing something incorrect with the <code>FirstOrDefault()</code> query. What is it? </p>
  
  <p>Does <code>FirstOrDefault()</code> complete the entire query and then take the first element? </p>
  
  <p>How can I speed this up and use <code>FirstOrDefault()</code>?</p>
  
  <p>Edit 1:</p>
  
  <p>One additional point I noticed is that the profiler says I'm maxing out my CPU on both of these implementations. That's also something I don't care for and didn't expect. The additional method I added didn't reduce that spike, just cut its duration in half.</p>
  
  <p>Edit 3:</p>
  
  <p>Putting the studies into a dictionary has improved the run time tremendously. It's definitely going to be how the committed code looks. Doesn't answer the question on FirstOrDefault though.</p>
  
  <p>Edit 2:</p>
  
  <p>Here's the sample code requested in a simple console app. My run still shows that in most cases FirstOrDefault takes longer.</p>
  
  <pre><code>using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;
  using System.Reactive.Linq;
  using System.Reactive.Concurrency;
  using System.Diagnostics;
  
  namespace TestCode
  {
      public class Program
      {
          public List&lt;IntHolder&gt; list;
  
          public static void Main(string[] args)
          {
              var prog = new Program();
              prog.list = new List&lt;IntHolder&gt;();
  
              prog.Add50000Items();
              prog.list.Add(new IntHolder() { Num = 12345 });
              prog.Add50000Items();
  
              var stopwatch = new Stopwatch();
              stopwatch.Start();
              prog.list.FirstOrDefault(n =&gt; n.Num == 12345);
              stopwatch.Stop();
  
              Console.WriteLine("First run took: " + stopwatch.ElapsedTicks);
              var lookingFor = new IntHolder() { Num = 12345 };
  
              stopwatch.Reset();
              stopwatch.Start();
              prog.GetMatching(lookingFor);
              stopwatch.Stop();
              Console.WriteLine("Second run took: " + stopwatch.ElapsedTicks);
              Console.ReadLine();
          }
  
          public void Add50000Items()
          {
              var rand = new Random();
  
              for (int i = 0; i &lt; 50000; i++)
                  list.Add(new IntHolder() { Num = rand.Next(100000) });
          }
  
          public IntHolder GetMatching(IntHolder num)
          {
              foreach (var number in list)
                  if (number.Num == num.Num)
                      return number;
  
              return null;
          }
      }
  
      public class IntHolder
      {
          public int Num { get; set; }
      }
  }
  </code></pre>