Managed vs. Unmanaged :: Round 1 - Theoretical

Posted Monday, August 11, 2008 4:06 PM by Nathan Zaugg

Managed vs Unmanaged I am asked all of the time about the performance of managed vs. unmanaged code and "how much slower is it?".  This is one of the questions I am going to attempt to answer using experimentation.  In this post I'll talk about some of the theory and make some predictions (I haven't written any code yet) and we'll see how close the theory matches the experiments. 

Managed Code Defined

Managed code is somewhat an misunderstood concept.  Managed code is simply code that targets the CLR runtime.  The CLR runtime is a bit more complex to explain and people often see the tem "virtual machine" and misunderstand the meaning of that statement.  When understanding the flow of execution for a managed application there is a three-step process.  First, the code is compiled to MSIL (Microsoft Intermediate Language).  This could be any code, C#, VB.NET, etc.  All Managed code compiles initially to MSIL.  MSIL is a low-level language like assembly.  It is often described as "Object Oriented Assembly" and rather than using registers you act upon memory in a highly optimized stack called the Evaluation Stack.  The beauty of IL is that is is low-level enough to be a very fast JIT (Just In Time) compile to native code, but the optimizations on a per-CPU basis can still be achieved in this final compilation step.  CPU optimizations like SSE2, SSE3, etc. that can greatly speed up code execution can be "given" to the users of managed code for free.  This could also include other optimizations such as GPU optimizations. 

When you run a piece of managed code it will normally take the first bit of execution to covert the MSIL to Native code specifically optimized for your platform.  This is known as JIT compilation usually happens very quickly and most people don't even realize that is what is happening every time they launch their application.  Of course, if your code is large, complex and has many dependencies you may be able to shave valuable milliseconds or in some cases even seconds by using ngen on your code.  Ngen is a tool that ships with the .NET framework that does the same compilation from IL to Native that the JIT compiler does but also keeps a copy of the native image that is generated.  This way a managed application is loaded much the same way a native application is loaded -- without JIT'ing. 

 

Code Execution

Figure 1 :: Managed Code Execution Lifecycle

So when you hear that .NET code is run in a virtual machine what they refer to is that you do not have to program to the specifics of a CPU; In that way the code is virtualized.  There are also services provided to you such as Memory Management, Thread Management, Exception Handling, Garbage Collection, and Security.  However, in my opinion the term virtual machine is a pretty poor fit.  These services all run side-by-side under the same process and even the same App Domain.  Therefore these services can be better thought of as a standard code template that gets compiled into every application rather than anything that is virtualized. 

On the Left - Managed Code

Managed code has some advantages!  Not the least of which is productivity.  Lets review some areas where managed code actually has an advantage! 

CPU Optimizations

As mentioned earlier, Managed Code is JIT compiled to target the specific platform in which you are running.  CPU optimizations can have a big advantage in performance!  While Native code can also take advantage of CPU optimizations you have to make a trade-off.  You have to either ship your code without such optimizations enabled for fear that someone without one of these will try to run your code OR you have to build some code and logic around working with or without each optimization you plan to target. 

Little is known, however, about the optimizations performed during JIT compilation.  Also, most modern CPU's are likely to have a base set of the most common CPU Optimizations so this argument gets somewhat weaker if there are few differences between chips. 

Managed Memory

Managed code has an awesome memory manager!  Suppose we have an array of integers like int[].  This is a data structure of contiguous integers.   Some of the advantages of using such a structure is that it is very quick to access.  You can use a syntax like MyInts[5] for access that is nearly as quick as referencing a local variable.  Internally pointer arithmetic is taking place where sizeof(int) * index + MyInts& = MyInts[5]; or Θ(1) which makes this the fastest method of accessing dynamically allocated memory.  The down-side to this stricture is adding elements to this array is extremely expensive!  Unlike dynamically-linked structures in order to add an element to this array usually requires the allocation of a completely new, bigger array, and the copying of all of the elements from the old array to the new one.  This has the complexity of Θ(n). This is extremely expensive for a single add operation! 

This is where managed code has another advantage!  Because there are no pointers, only system-managed references, it is possible for the memory manager to simply expand the array construct and anything in the way can be safely moved without harm to the execution of the application.  The worst case scenario for this operation is now a Θ(1) or that there is a constant cost for such an operation regardless of the size of the current structure.  So even in a worst case scenario we have the best possible performance.  This is good news for immutable data structures! 

Another memory-related advantage managed code has over native code is that it takes advantage of the memory vs. time tradeoff.  Basically all .NET applications consume more OS memory than they need.  There is some complex memory algorithm at the heart of this that tries to minimize calls to the OS for more memory and that such calls will gain larger blocks of memory.  Calls to the OS for more memory are very expensive whereas calls to the Memory Manager in Managed code is extremely fast.  Therefore, in theory, we should be able to dynamically create objects substantially faster in a managed language than a native one. 

Some of the other advantages to managed memory are the virtual non-existence of memory leaks, and automatic memory defragmentation.  And while the last of this list is somewhat controversial, I list it here as an advantage.  That is the garbage collector.  The garbage collector is the nebulous cloud that hangs over most C/C++ developers looking to write managed code.  They have been taught to manage memory themselves and do not like the idea of giving that control up to a complicated set of algorithms.  They all ask the same thing -- what happens if garbage collection is triggered at an inopportune time?  The answer to that question is a little complicated. But basically the garbage collection is very fast -- usually pausing execution for less than a few milliseconds.  Also, because the GC operates on many objects at once it can be a more efficient and less error-prone than self-styled memory management. Basically, the GC is not going to cause you any grief unless you do something stupid like leave your streams open (the only thing I can think of in Managed code that will cause a 'leak') and will mostly be a great burden off of your shoulders!

Security

One of the major reasons for the push for Managed Code was security!  Managed code can make certain guarantees about it's vulnerabilities to attack.  Even if an exploit is found (which very few, if any have been) then the CLR can usually be quickly patched to provide protection to ALL .net assemblies.  I believe this is one of the reasons Microsoft prefers you to allow the JIT to happen for each execution.  Part of these guarantees is type safety and bounds checking which are very common exploits for overflow attacks.

If you choose to sign your assemblies you are no longer venerable to "dll replacement" attacks as the CLR will verify the signature of the called assembly.  Additionally, with the GAC (Global Assembly Cache) different versions of the same assembly (dll) can live harmoniously side-by-side thus eliminating the infamous "dll hell".

On the Right - Unmanaged Code

Native code enjoys the legacy and reputation of performance.  This style of programming is considered "metal to metal" indicating you have complete control over the hardware of the system.  These applications are lean and efficient and have no "magic" services which is going to make this section very lean!  They use, in general, very little memory compared to their managed counterparts and have the ability to use memory in ways the managed code doesn't like or won't allow. 

Native code allows "unsafe" type casting which can result in better performance especially in cases where .net would employ boxing / un-boxing techniques which are very costly.  Because nothing is going to happen without you making it happen you should end up with faster code.  In smaller applications the memory management could be overkill.  You also have much more control over the lifetime of an object.  Rather than waiting for a GC to collect the memory at some unknown time you explicitly delete objects and the memory is reclaimed immediately.

There isn't much to say about Native code and I think that's why people are more comfortable with it's performance. 

 

The Matchup

I hope to be able to write the following tests in C# for managed code and C++ and/or Delphi for unmanaged code.  I will try to post the code for each "round" and am very open to criticism on fairness.  In some respects this is a little bit like comparing apples to oranges but that doesn't mean they can't compete!

Rounds:

  1. Round 1 : Theoretical (this round)
  2. Round 2 : Computational (bit manipulation, looping, adding, subtracting, searching, sorting, etc.)
  3. Round 3 : Dynamic Memory (object creation, array resizing, memory allocation, memory de-allocation, etc.)
  4. Round 4 : Windows Forms & Messages (dynamic creation of windows, buttons, etc.)
  5. Round 5 : IO (File System Access, Network Streams, etc.)

The Prediction

Based on the theory I would say that after a managed assembly has been loaded it should execute faster than it's native counterpart.  I base this idea mostly on the memory management provided to managed code. Though I would be a little surprised if Managed Code were to win a head-to-head challenge.  I believe that the end result will be native code will perform faster than managed code but by a statistically insignificant amount. 

I officially call this round: Winner - Managed Code (by decision).

Round By Round Predictions:

  1. Round 1 : Won by Decision: Managed Code
  2. Round 2 : Win by Native Code
  3. Round 3 : Win by Managed Code
  4. Round 4 : Tie (1 point each)
  5. Round 5 : Win by Native Code

 

References:

Comments

# Microsoft, Please Do Not Release IE8!

Friday, November 14, 2008 9:39 AM by Nates Stuff

For the last few months I have been testing out IE8 and for the last few months I have been testing out

# re: Managed vs. Unmanaged :: Round 1 - Theoretical

Friday, November 14, 2008 1:37 PM by Phil Gilmore

# JIT Optimizations Lacking! | keyongtech

Sunday, January 18, 2009 10:26 AM by JIT Optimizations Lacking! | keyongtech

Pingback from  JIT Optimizations Lacking! | keyongtech

# Bmw 850ci Club, Ln52a850 Sanus Mount

Friday, May 21, 2010 12:47 AM by Bmw 850ci Club, Ln52a850 Sanus Mount

Pingback from  Bmw 850ci Club, Ln52a850 Sanus Mount

# 1994 G30 Van 4l60e, Motors G30 Van Parts Delco Remy

Saturday, May 22, 2010 1:37 PM by 1994 G30 Van 4l60e, Motors G30 Van Parts Delco Remy

Pingback from  1994 G30 Van 4l60e, Motors G30 Van Parts Delco Remy

# 09 Corvette Convertible Sale, 1500 Corvette C3

Saturday, May 22, 2010 7:18 PM by 09 Corvette Convertible Sale, 1500 Corvette C3

Pingback from  09 Corvette Convertible Sale, 1500 Corvette C3

# Schl?sselnummer Bmw 320i E36, Lss Pokemon Wiki - 89.dlmreza.net

Pingback from  Schl?sselnummer Bmw 320i E36, Lss Pokemon Wiki - 89.dlmreza.net

# Cougar Parts Fifth Wheels Rv Dealer, 295 Fifth Avenue New York - 302.animejin.com

Pingback from  Cougar Parts Fifth Wheels Rv Dealer, 295 Fifth Avenue New York - 302.animejin.com

# Lexus Menlo Park, Locator Sc400 Lexus Salvage Yards - 390.codebluehacks.org

Pingback from  Lexus Menlo Park, Locator Sc400 Lexus Salvage Yards - 390.codebluehacks.org

# 1989 Voyager Parts Plymouth Acclaim, Voyager Scorpion Part Ii Rapidshare Dvdrip - 361.myipgirl.com

Pingback from  1989 Voyager Parts Plymouth Acclaim, Voyager Scorpion Part Ii Rapidshare Dvdrip - 361.myipgirl.com

# Chevrolet Brookwood Manual Replacement, Brookwood Hud - 417.an74.com

Pingback from  Chevrolet Brookwood Manual Replacement, Brookwood Hud - 417.an74.com

# Brookwood Old Chevrolet Biscayne, 1966 Biscayne Weight - 360.renters.ws

Pingback from  Brookwood Old Chevrolet Biscayne, 1966 Biscayne Weight - 360.renters.ws

# 1999 Dodge Avenger Se Chrysler Sebring, 1996 Chrysler Sebring Problems - 218.eumreborn.com

Pingback from  1999 Dodge Avenger Se Chrysler Sebring, 1996 Chrysler Sebring Problems - 218.eumreborn.com

# Used Alero 2001 Names, Oldsmobile Alero Bulb Types Halo Projector Headlights - 303.akemet.com

Pingback from  Used Alero 2001 Names, Oldsmobile Alero Bulb Types Halo Projector Headlights - 303.akemet.com

# Q100 Headlight Plug Management, Hyatt Vacation Club - 427.tgrconversions.com

Pingback from  Q100 Headlight Plug Management, Hyatt Vacation Club - 427.tgrconversions.com

# E 150 Econoline Club Wagon Bumper V8, Ford E 100 Econoline Club Wagon Bulb Replacement Tail Light - 446.codebluehacks.org

Pingback from  E 150 Econoline Club Wagon Bumper V8, Ford E 100 Econoline Club Wagon Bulb Replacement Tail Light - 446.codebluehacks.org

# R63 Amg Cooling System Family, Cooling System Mercedes Benz R63 Amg V8 - 149.unlockiphone30.net

Pingback from  R63 Amg Cooling System Family, Cooling System Mercedes Benz R63 Amg V8 - 149.unlockiphone30.net