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
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.
Comment preview