Author Topic: Measuring Lazy Execution Time  (Read 1421 times)

0 Members and 1 Guest are viewing this topic.

TheMaster

  • Guest
Measuring Lazy Execution Time
« on: April 12, 2013, 07:35:47 AM »
Code - C#: [Select]
  1.  
  2. public static class LinqDiagnosticExtensions
  3. {
  4.  
  5.    /// <summary>
  6.    ///
  7.    /// Measuring exection time of lazy sequences like those
  8.    /// returned by LINQ extensions is a bit more complicated
  9.    /// than some may think. First, there can be overhead in
  10.    /// both the constructor and the Dispose() method of the
  11.    /// IEnumerator<T> obtained from the object returned by
  12.    /// by LINQ methods or your own block iterators, that will
  13.    /// be included in the measurement resulting from timing
  14.    /// how long a call to Enumerable.Count() takes to return.
  15.    /// This may, or may not be the intent, and in the case of
  16.    /// your own LINQ iterators, you could be doing quite a bit
  17.    /// of work before and/or after the loop containing the
  18.    /// call to yield return.
  19.    ///
  20.    /// Another problem is that simply timing how long the
  21.    /// Enumerable.Count() method takes to return does not
  22.    /// always measure how much work is required to obtain
  23.    /// every element in an IEnumerable<T>. It only tells
  24.    /// how long it takes to count the number of elements
  25.    /// that will be enumerated. The actual work required to
  26.    /// get each element includes both the work done by the
  27.    /// the IEnumerator<T>'s MoveNext() implementation, and
  28.    /// the implementation of the IEnumerator<T>'s Current
  29.    /// property 'getter' method. Enumerable.Count() does not
  30.    /// access the IEnumerator<T>'s Current property at all.
  31.    ///
  32.    /// So, if we want to be precise, then we need to do a bit
  33.    /// more than simply call Enumerable.Count(). Here is a
  34.    /// very simple extension method that returns a TimeSpan
  35.    /// that measures the actual time required to obtain all
  36.    /// elements from an IEnumerable<T>, that will optionally
  37.    /// omit the overhead of the IEnumerator<T>'s constructor
  38.    /// and Dispose() implementations, along with the overhead
  39.    /// of the first call to MoveNext(), which is typically where
  40.    /// Linq iterators do 'very lazy' initialization.
  41.    ///
  42.    /// </summary>
  43.    
  44.    public static TimeSpan GetExecutionTime<T>( this IEnumerable<T> source, bool includeOverhead = true  )
  45.    {
  46.       T last = default( T );
  47.       Stopwatch sw = new Stopwatch();
  48.       if( includeOverhead )
  49.          sw.Start();
  50.       using( var e = source.GetEnumerator() )
  51.       {
  52.          /// linq iterators are state machines that
  53.          /// typically do initialization in the first
  54.          /// call to MoveNext(), so we will include
  55.          /// that in the overhead rather than in the
  56.          /// raw iteration time:
  57.  
  58.          bool flag = e.MoveNext();
  59.          if( !includeOverhead )
  60.             sw.Start();
  61.          // begin 'raw' iteration time:
  62.          while( flag )
  63.          {
  64.             /// Let's make sure that if get_Current() is
  65.             /// doing something significant, we measure
  66.             /// that as well:
  67.             last = e.Current;
  68.             flag = e.MoveNext();
  69.          }
  70.          // end of 'raw' iteration time:
  71.          if( !includeOverhead )
  72.             sw.Stop();
  73.       }
  74.       if( includeOverhead )
  75.          sw.Stop();
  76.       return sw.Elapsed;
  77.    }
  78. }
  79.  
  80.