Ayende @ Rahien

Refunds available at head office

Creating objects - round 2

It was pointed out that I had a skew in my test, I was also calling i.ToString() in a tight loop, which probably kills the numbers.

Here is the exact same benchmark, but using a constant string value, instead of calling i.ToString() all the time.

  • Using new - 00.0177508 seconds (down from 00.3648117 seconds)
  • Using Activator.CreateInstance - 06.3033382 seconds (down from 06.8242636 seconds)
  • Using GetUninitializedObject - 02.8209057 seconds (down from 03.2422335 seconds)
  • Using specialized dynamic method - 00.0417958 seconds (down from 00.4314517 seconds)
  • Using generic dynamic method -  00.1189762 seconds (down from 00.5018288 seconds).

Here are some numbers to keep you entertained.

Difference from previous approach Time to create single instance Iterations Time Method
0% 0.0000000177508 1000000
0.01775
New
235.46% 0.0000000417958 1000000
0.0418
Specialized dynamic method
284.66% 0.0000001189762 1000000
0.11898
Generic dynamic method
2370.98% 0.0000028209057 1000000
2.82091
GetUnitializedObject
223.45% 0.0000063033382 1000000
6.30334
Activator

It doesn't change the overall conclusion, by the way. There is just no way I can make myself work about 0.0000063 second.

I was also asked what the cost of creating dynamic method is, it comes to the tune of 00.0012487 seconds, for the generic version that I have shown. I would strongly encourage caching that, since (relative to creating the object itself) is seems expensive.

It is important to remember that dynamic method are also garbage collected, however, so we don't really have to worry about blowing the AppDomain if we use them.

Comments

Sasha Goldshtein
02/29/2008 03:38 PM by
Sasha Goldshtein

Oren, you are probably right that these numbers would not matter in a typical business environment.

However, part of my job is developing a soft-realtime .NET application framework with a spec of 300,000 messages passed per second. When you're talking about numbers such as these, the difference between "new"-ing up an object and using Activator.CreateInstance is not something I would neglect (and in fact, there is a specific point in our framework where we use up-front code-generation to prevent using Activator.CreateInstance for types we do not recognize during compile-time).

Another thing to remember is that these numbers add up across your application. There's one place where you used Activator.CreateInstance because you were lazy to optimize or generate code up-front. Another place where you have unintentional boxing/unboxing. Another object with a finalizer going to Gen2 and carrying lots of memory to Gen2 with it. Another structure with some unaligned fields which produces cache collisions. And the result is a big surprise when you run the code under a profiler and don't understand how come the cost is distributed EVERYWHERE in the codebase.

I totally agree that this might not be relevant for everyone. But it is for me, and it's important for me to convey the message that there are applications where you must care about details as minute as these.

Comments have been closed on this topic.