Native Code VS Managed Code – Performance and Development

A while ago I received a reply to a comment I made on Kristian’s blog (http://www.mathblog.dk/gcd-faceoff/#comment-106652). I was asked to illustrate the differences between the speed of native code and managed code. Since I haven’t been blogging recently as well (well, examinations are just directly after the June holidays), I’ve decided to write a post illustrating the difference between Native Code and Managed Code.

More specifically, we’ll be comparing the development process as well as the output performances between C++/CLI native wrappers as compared to purely managed C# code, using GCD (Greatest Common Divisors) calculations (of 2 integers) as our comparison basis.

This post will be split into 6 sections :

  • 1. Introduction (above)
  • 2.1. GCD Calculation – C++/CLI native wrapper
  • 2.2. GCD Calculation – Pure managed C#
  • 3. Conclusion
  • 4. Notes

2.1. GCD Calculation – C++/CLI native wrapper

nlib.h : Native class defining GCD method

#pragma once

class nlib
{
public:
	static long gcd(long a, long b) {
		long y, x;

		if (a > b) {
			x = a;
			y = b;
		} else {
			x = b;
			y = a;
		}

		while (x % y != 0) {
			long temp = x;
			x = y;
			y = temp % x;
		}

		return y;
	}
};

NumericalLibrary.h : Forward declarations of wrapper methods

#pragma once

public ref class NumericalLibrary
{
public:
	static long Gcd(long a, long b);
};

NumericalLibrary.cpp : Definitions of wrapper methods

#include "NumericalLibrary.h";
#include "nlib.h";

long NumericalLibrary::Gcd(long a, long b)
{
	return nlib::gcd(a, b);
}

Program.cs : Entry point of test program

using System;
using System.Diagnostics;

namespace ConsoleApplication1
{
    class Program
    {
        const int smallest = 1;
        const int largest = 100000000;

        static void Main(string[] args)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();

            for (int i = 1; i < 100000000; i++)
            {
                NumericalLibrary.Gcd(i, i + 1);
            }

            sw.Stop();

            Console.WriteLine("Rate = " + 100 * (double)sw.ElapsedTicks / (largest - smallest)<code> + </code><code>" Nanoseconds / call"</code>);
            Console.ReadKey();
        }
    }
}

Output:
Rate = 2.932147 Nanoseconds / call

2.2. GCD Calculation – Pure managed C#

Program.cs : Entry point of test program

using System;
using System.Diagnostics;

namespace ConsoleApplication1
{
    class Program
    {
        const int smallest = 1;
        const int largest = 100000000;

        static long gcd(long a, long b)
        {
            long y, x;

            if (a > b)
            {
                x = a;
                y = b;
            }
            else
            {
                x = b;
                y = a;
            }

            while (x % y != 0)
            {
                long temp = x;
                x = y;
                y = temp % x;
            }

            return y;
        }

        static void Main(string[] args)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();

            for (int i = 1; i < 100000000; i++)
            {
                gcd(i, i + 1);
            }

            sw.Stop();

            Console.WriteLine("Rate = " + 100 * (double)sw.ElapsedTicks / (largest - smallest) + " Nanoseconds / call");
            Console.ReadKey();
        }
    }
}

Output:
Rate = 7.461145 Nanoseconds / call

3. Conclusion

Performance-wise, native code performs a lot faster than managed code. From our GCD computation benchmarks, we can conclude that native code runs about twice as fast as managed code.

Development-wise, the amount of native code required is a lot more than the amount of managed code required to complete the same task. Note that we cannot combine the forward deceleration and the definition of wrapper classes because the C++/CLI compiler does not permit us to do so (see : http://stackoverflow.com/questions/6696598/namespace-not-recognized-in-c-cli).

Thus, if we were to work on small projects, using purely managed code will suffice. However, should we be working on larger scale projects (such as a numerical library), we should try as much to use native code as possible.

Once again, thank you Kristian for your wonderful blog!

4. Notes

All projects were built in Visual Studio Professional 2012 under the build configuration “Release”. The “Release” and “Debug” configurations causes performance of compiled programs to vary quite a lot, so please do make sure you are using the correct configurations.

Leave a comment