by skeet via Jon Skeet: Coding Blog on 1/26/2009 8:57:55 PM
While I was answering a Stack Overflow question on the performance implications of using a for loop instead of a foreach loop (or vice versa) I promised to blog about the results - particularly as I was getting different results to some other posters.
On Saturday I started writing the bigger benchmark (which I will post about in the fullness of time) and used a technique that I'd used when answering a different question: have a single timing method and pass it a delegate, expected input and expected output. You can ask a delegate for the associated method and thus find out its name (for normal methods, anyway - anonymous functions won't give you anything useful, of course) so that's all the information you really need to run the test.
I've often shied away from using delegates for benchmarking on the grounds of it interfering with the results - including the code inline with the iteration and timing obviously has a bit less overhead. However, the CLR is so fast at delegate invocation these days that it's really not an issue for benchmarks where each iteration does any real work at all.
It's still a pain to have to write that testing infrastructure each time, however. A very long time ago I wrote a small attribute-based framework. It worked well enough, but I've found myself ignoring it - I've barely used it despite writing many, many benchmarks (mostly for newsgroup, blog and Stack Overflow posts) over the course of the years. I'm hoping that the new framework will prove more practical.
There are a few core concepts and (as always) a few assumptions:
Now for the interesting bit (from my point of view, anyway): I decided that this would be a perfect situation to try playing with a functional style. As a result, everything in the framework is immutable. When you "add" a test to a test suite, it returns a new test suite with the extra test. Running the test suite returns the result suite; scaling the result suite returns a new result suite; scaling a result returns a new result etc.
The one downside of this (beyond a bit of inefficiency in list copying) is that C# collection initializers only work with mutable collections. They also only work with direct constructor calls, whereas generic type inference doesn't apply to constructors. In the end, the "static generic factory method" combined with simple Add method calls yields quite nice results, even though I can't use a collection initializer:
This is a pretty small amount of extra code to write, beyond the code we actually want to benchmark (the ArrayFor and ArrayForEach methods in particular). No looping by iteration count, no guessing at the number of iterations and rerunning it until it lasts a reasonable amount of time, etc.
My only regret is that I haven't written this in a test-driven way. There are currently no unit tests at all. Such is the way of projects that start off as "let's just knock something together" and end up being rather bigger than originally intended.
At some point I'll make it all downloadable from my main C# page, in normal source form, binary form, and also a "single source file" form so you can compile your benchmark with just csc /o+ /debug- Bench*.cs to avoid checking the assembly filename each time you use it. For the moment, here's a zip of the source code and a short sample program, should you find them useful. Obviously it's early days - there's a lot more that I could add. Feedback would help!
csc /o+ /debug- Bench*.cs
Next time (hopefully fairly soon) I'll post the for/foreach benchmark and results.
Original Post: Benchmarking made easy
The content of the postings is owned by the respective author. CSharpFeeds is not responsible for the contents of the postings. This site is automatically generated and cannot be reviewed for abusive content. If you find abusive content on CSharpFeeds, please contact us. Designated trademarks and brands are the property of their respective owners. All rights reserved.