views:

38

answers:

1

I have this code, that when swapping the order of UsingAs and UsingCast, their performance also swaps.

using System;
using System.Diagnostics;
using System.Linq;

using System.IO;

class Test
{
    const int Size = 30000000;

    static void Main()
    {
        object[] values = new MemoryStream[Size];



        UsingAs(values);
        UsingCast(values);


        Console.ReadLine();
    }

    static void UsingCast(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {
            if (o is MemoryStream)
            {
                var m = (MemoryStream)o;
                sum += (int)m.Length;
            }
        }
        sw.Stop();
        Console.WriteLine("Cast: {0} : {1}", sum,
                          (long)sw.ElapsedMilliseconds);
    }

    static void UsingAs(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {

            if (o is MemoryStream)
            {
                var m = o as MemoryStream;
                sum += (int)m.Length;
            }
        }
        sw.Stop();
        Console.WriteLine("As: {0} : {1}", sum,
                          (long)sw.ElapsedMilliseconds);
    }


}

Outputs:

As: 0 : 322
Cast: 0 : 281

When doing this...

UsingCast(values);
UsingAs(values);

...Results to this:

Cast: 0 : 322
As: 0 : 281

When doing just this...

UsingAs(values);

...Results to this:

As: 0 : 322

When doing just this:

UsingCast(values);

...Results to this:

Cast: 0 : 322

Aside from running them independently, how to invalidate the cache so the second code being benchmarked won't receive the cached memory of first code?

Benchmarking aside, just loved the fact that modern processors do this caching magic :-)

[EDIT]

As advised to try this faster code(supposedly)...

static void UsingAsAndNullTest(object[] values)
{        
    Stopwatch sw = Stopwatch.StartNew();
    int sum = 0;
    foreach (object o in values)
    {
        var m = o as MemoryStream;
        if (m != null)
        {                
            sum += (int)m.Length;
        }
    }
    sw.Stop();
    Console.WriteLine("As and null test: {0} : {1}", sum,
                      (long)sw.ElapsedMilliseconds);
}

...The result is this:

As and null test: 0 : 342

Slower than the two codes above

[EDIT]:

As advised to hand each routine their own copy...

static void UsingAs(object[] values)
{
    object[] a = values.ToArray();

    Stopwatch sw = Stopwatch.StartNew();
    int sum = 0;
    foreach (object o in a)
    {

        if (o is MemoryStream)
        {
            var m = o as MemoryStream;
            sum += (int)m.Length;
        }
    }
    sw.Stop();
    Console.WriteLine("As: {0} : {1}", sum,
                      (long)sw.ElapsedMilliseconds);
}

static void UsingCast(object[] values)
{
    object[] a = values.ToArray();

    Stopwatch sw = Stopwatch.StartNew();
    int sum = 0;
    foreach (object o in a)
    {
        if (o is MemoryStream)
        {
            var m = (MemoryStream)o;
            sum += (int)m.Length;
        }
    }
    sw.Stop();
    Console.WriteLine("Cast: {0} : {1}", sum,
                      (long)sw.ElapsedMilliseconds);
}

...Outputs:

Cast: 0 : 282
As: 0 : 282

Now they have the same results, thanks Remus!

Running Cast and As independently, they also yield the same result(i.e. 282). Now, as for why they become faster (from 322 down to 282 milliseconds) when they are handed their own copy of array, I can't make anything out of it :-) That's entirely another story

+1  A: 

If you want to take out of the picture the L2 cache and the TLB misses then simply call the second test on a different MemoryStream of the same size.

Remus Rusanu