Microsoft & .NETVisual C#Performance Implications of Managed Data

Performance Implications of Managed Data

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

From Kate Gregory’s Codeguru column, “Using Visual C++ .NET“.

When you create a Managed C++ application, you are writing managed code. But you’re not always working with managed data—objects that are created on the garbage-collected heap and whose lifetimes are managed by the runtime. You can work with managed or unmanaged data as you prefer.

In an earlier column, I showed you how to box C++ fundamental types, such as int, into managed types so they could be passed to functions in the Base Class Libraries that don’t handle unmanaged data. I also showed how to unbox that information by just using the dereference operator, *.

But did you stop to wonder about the performance costs of boxing and unboxing, of converting from type to type? I wrote a little sample application that does some arithmetic, then added boxing and unboxing into the loops to get an idea of the performance cost of these conversions.

Here’s the first version of my calculations:

int _tmain(void)
{
    // DateTime is a value class, 
    // so we don't need pointers
    System::DateTime start;
    System::DateTime end;
    System::TimeSpan span;
    int i;
    const int looplimit = 10000000;

    int j;
    System::Int64 x =0;
    start = System::DateTime::Now;
    for (i=0; i<looplimit; i++)
    {
        j = i + 10;
        x += j / 100;
    }
    end = System::DateTime::Now;
    span = end.Subtract(start);
    Console::WriteLine("answer is {0}",__box(x));
    Console::WriteLine("integer math took {0}", span.ToString());

// . . .
    return 0;
}

You’ll notice that looplimit is a really big number: 10,000,000. That’s how many times I needed to run the loop before I got a measurable execution time. The loop is just noodling around doing some integer math. I divide j by 100 to ensure we don’t get a bigger answer than 64 bits can hold.

I ran my sample application as a Debug release with a breakpoint on the return statement. I wanted to be sure that a helpful optimizer didn’t eliminate some of my calculations. I ran it a few times before recording any times, to ensure no JIT delays appeared. On my machine (yours may be faster), this loop consistently took about .75 seconds.

Now, here’s another version of that same loop, just using System::Int32 instead of int variables:

    System::Int32 j2;
    x = 0;
    start = System::DateTime::Now;
    for (i=0; i<looplimit; i++)
    {
        j2 = i + 10;
        x += j2 / 100;
    }
    end = System::DateTime::Now;
    span = end.Subtract(start);
    Console::WriteLine("answer is {0}",__box(x));
    Console::WriteLine("Int32 took {0}", span.ToString());

I didn’t expect this to take any longer than the first loop because System::Int32 is a value type equivalent to int, but since I’m running timing loops anyway, I wanted to be sure. As expected when I ran it, the execution times were the same as the first loop, about 0.75 seconds.

This version of the loop adds boxing and unboxing into the mix:

    __box int*  j3 = __box(j2);
    x = 0;
    start = System::DateTime::Now;
    for (i=0; i<looplimit; i++)
    {
        j3 = __box(i + 10);
        x += (*j3 / 100);
    }
    end = System::DateTime::Now;
    span = end.Subtract(start);
    Console::WriteLine("answer is {0}",__box(x));
    Console::WriteLine("boxing took {0}", span.ToString());

I expected this to take longer, but the question was, how much longer? Consistently, this version of the loop took 1.5 seconds—twice as long as the no-boxing version. So a box and an unbox, roughly speaking, costs as much as an integer multiply, divide, and two adds. That could be a noticeable amount of time in a large program.

Just for the heck of it, I decide to try boxing and unboxing the loop counter, too:

    __box int*  j4 = __box(j2);
    __box int* i3 = __box(i);
    __box int* limit = __box(looplimit);
    x = 0;
    start = System::DateTime::Now;
    for (*i3 = 0; *i3 < *limit; (*i3)++)
    {
        j4 = __box(*i3 + 10);
        x += (*j4 / 100);
    }
    end = System::DateTime::Now;
    span = end.Subtract(start);
    Console::WriteLine("answer is {0}",__box(x));
    Console::WriteLine("ultra boxing took {0}", span.ToString());

This version of the loop takes about 2 seconds on my laptop, which suggests to me that boxing is more expensive than unboxing.

What Does All This Mean?

Working in C++ can let you write faster code than working in C# or VB.NET. If you’re comfortable working in C++, there are real benefits for you. But, if your major motivation is execution speed, be aware that boxing and unboxing values so that you can use the Base Class Libraries carries a performance cost. Design your code carefully, so that you don’t perform wasteful calculations.

About the Author

Kate Gregory is a founding partner of Gregory Consulting Limited (www.gregcons.com). In January 2002, she was appointed MSDN Regional Director for Toronto, Canada. Her experience with C++ stretches back to before Visual C++ existed. She is a well-known speaker and lecturer at colleges and Microsoft events on subjects such as .NET, Visual Studio, XML, UML, C++, Java, and the Internet. Kate and her colleagues at Gregory Consulting specialize in combining software develoment with Web site development to create active sites. They build quality custom and off-the-shelf software components for Web pages and other applications. Kate is the author of numerous books for Que, including Special Edition Using Visual C++ .NET.

# # #

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories