Code Sample

Why does NProfiler provide more reliable performance data?

Read the comments if you want to fully understand why NProfiler provides much more accurate times. You can copy the code into an empty .NET console application to execute it.

class Program
{
    static void Main(string[] args)
    {
        //This code sample creates a list of random numbers and subsequently
        //sorts it. It uses Stopwatch to measure the two steps. That way you
        //can execute the code without a profiler to get real world times.
        //You can then profile the application with different tools
        //and compare the profiling results with real world data.

        var stopwatch = new Stopwatch();

        //Start measurement of CreateRandomNumbers().
        stopwatch.Start();

        //Step 1: Create a list filled with 10,000,000 random integers.

        //Profilers will instrument the CreateRandomNumbers() method (see below) with
        //additional instructions to collect line-level times and hit-counts. The added
        //instructions increase the execution time of the method dramatically.

        //In reality, the method takes only about 15% of time, but most profilers
        //will incorrectly attribute 50% of time and more because of their
        //inability to subtract the instrumentation overhead.

        List<int> list = CreateRandomNumbers();

        //How long did it take to create the random numbers?
        TimeSpan createTime = stopwatch.Elapsed;

        //Start measurement of Sort().
        stopwatch.Restart();

        //Step 2: Let's call List<T>.Sort() to sort the numbers in the list. In reality
        //this step takes about 85% of time.

        //Profilers recognize that there is no source code available for the
        //.NET framework method Sort(). It would be pointless to collect line-level
        //performance data. Besides this, profiled applications would become horribly
        //slow if line-level instrumentation is added to all .NET framework methods.
        //Therefore profilers omit line-level instrumentation for methods without source.

        //Now we have an interesting situation:
        // - CreateRandomNumbers() is instrumented and is much slower now
        //   because of the additional instructions.
        // - Sort() is not instrumented and takes the same time as always.

        //If you ignore these distortions - as other profilers do - you will get very
        //misleading times. NProfiler displays approximately 15% and 80% for the two
        //method calls. That is relatively accurate. Other profilers display 50% and 50%
        //or worse.

        list.Sort();

        //How long did it take to sort the list?            
        TimeSpan sortTime = stopwatch.Elapsed;

        //Write Stopwatch times to console.
        double totalTimeTicks = (createTime + sortTime).Ticks;

        Console.WriteLine("CreateList: {0}, {1:P}", createTime, createTime.Ticks / totalTimeTicks);
        Console.WriteLine("Sort:       {0}, {1:P}", sortTime, sortTime.Ticks / totalTimeTicks);
    }

    //Disable inlining, otherwise some profilers may not show
    //line level performance data for this method.
    [MethodImpl(MethodImplOptions.NoInlining)]
    static List<int> CreateRandomNumbers()
    {
        var random = new Random(0);
        var list = new List<int>();

        for (int i = 0; i < 10000000; i++)
        {
            list.Add(random.Next());
        }

        return list;
    }
}