Reducing the cost of getting a stack trace
I am trying to find ways to reduce the cost of the stack trace used in NH Prof. The access to the stack trace is extremely valuable, but there is a significant cost of using it, so we need a better way of handling this. I decided to run a couple of experiment running this.
All experiments were run 5,000 times, on a stack trace of 7 levels.
- new StackTrace(true) - ~600ms
- new StackTrace(false) - ~150ms
So right there, we have a huge cost saving, but let us continue a bit.
- throwing exception - ~400ms
That is not so good, I have to say.
Well, when in doubt, cheat!
Using reflector and some _really_ nasty stuff, I came up with this:
var stackFrameHelperType = typeof(object).Assembly.GetType("System.Diagnostics.StackFrameHelper");var GetStackFramesInternal = Type.GetType("System.Diagnostics.StackTrace, mscorlib").GetMethod("GetStackFramesInternal",BindingFlags.Static|BindingFlags.NonPublic);var method = new DynamicMethod("GetStackTraceFast",typeof(object),new Type[0],typeof(StackTrace),true);var generator = method.GetILGenerator();generator.DeclareLocal(stackFrameHelperType);generator.Emit(OpCodes.Ldc_I4_0);generator.Emit(OpCodes.Ldnull);generator.Emit(OpCodes.Newobj, stackFrameHelperType.GetConstructor(new[] { typeof(bool), typeof(Thread) }));generator.Emit(OpCodes.Stloc_0);generator.Emit(OpCodes.Ldloc_0);generator.Emit(OpCodes.Ldc_I4_0);generator.Emit(OpCodes.Ldnull);generator.Emit(OpCodes.Call, GetStackFramesInternal);generator.Emit(OpCodes.Ldloc_0);generator.Emit(OpCodes.Ret);getTheStackTrace = (Func<object>)method.CreateDelegate(typeof(Func<object>));
Calling getTheStackTrace 5000 times with depth of 7 frames is… 54ms. And now that is a horse of a different color indeed.
And the best part is, I can use the StackFrameHelper as a key into cached stack traces.
And yes, I am aware that if anyone from the CLR team is reading this, a ninja team will be dispatched to… discuss with me the notion of supported operations vs. unsupported operation.
Comments
I hate internal types and method that clearly would help when extending the .net enviroment!
I almost created my own CheckedListBox in pure frustration.
What do you do with the StackFrameHelper you receive?
We have cows all over downtown calgary that are painted up like that horse. I had almost forgotten the atroscities until this post.
Whats the trust level to invoke that method? only a lot of web hosting is medium trust.
I can use that to get RuntimeMethodHandle, which is good enough to create a cache key.
Stephen,
I am assuming FullTrust, but I haven't considered this.
I don't think its a huge problem, you can probably use the old style in the event you can't use the cheat right?
Ayende,
you can probably form a compound cache key from the rgMethodHandle field inside of the StackFrameHelper (but beware of generic methods, where the handles are not unique per specialization!). Apart from that, the names of the methods on the stack are only available via StackFrameHelper.GetMethodBase.
And once you iterate over the frames and call GetMethodBase, you're basically back to what the "official" StackTrace class does. (Minus a few ms, maybe, when you optimize it for your needs, but never a two-third reduction...) And this at the cost of using some extremely brittle code, which is bound to break sooner or later, especially if you have a heterogeneous user base. (Which, I guess, you would have with NH Prof.)
Maybe in your current use case the advantages outweigh the disadvantages, but in most cases, I think it wouldn't be worth the trouble.
Fabian,
I am using rgMethodHandle and rgiILOffset as compound cache key, which will work just fine for my needs, since it match a location in a the source file.
Generic methods are actually not a problem for me, since I am only interested in the source line.
And since I only care about the uniqueness of the stack trace, I don't need to get GetMethodBase(), and can use the RuntimeMethodHandle
Thanks for posting this kind of ideas and tips, it's an additional knowledge from what I know before, keep up the good work
Comment preview