Updated 28/01/2025
“Use Case: Simplest way to measure how long a method on your code takes to execute, this is helpful for logging, profiling and general performance measurement. Often there is degradation over time and its useful to compare how long execution takes.”
The code used in this post can be found at https://github.com/carlpaton/BenchmarkDemo
Stopwatch (classic timer way)
This was how I learnt how to measure things using a Stopwatch
1 | public async Task MyMethod() |
As an example this could show MyMethod took 91ms (00:00:00.0916101)ts
which is useful but the code now has noise. It really needs to just UnitOfWorkAsync
, there are ways to make this smarter with wrappers (see dapper wrapper example) or MediatR but MethodTimer.Fody is a simpler implementation.
MethodTimer.Fody
These examples are based on Nick Chapsas’s video The Easiest Way to Measure Your Method’s Performance in C# and the Fody MethodTimer docs.
The library MethodTimer.Fody
does some assembly weaving magic, this just takes the compiled code and injects some new behavior, like the stopwatch timer above.
- Install
Fody
andMethodTimer.Fody
- My debug did not immediately start working, I replaced the content of FodyWeavers.xml per the docs with the default, rebuilt and then it worked.
1 | <Weavers> |
- Add the attribute
[Time]
to the top of the method, this is just a marker that the weaving magic will look for and change the code
1 | using MethodTimer; |
If you look at the IL (intermediate language) it should have simliar code to the stop watch code above, I couldnt get this to work when trying in VS 2022.
- This will write to the
trace
logger, so you can view the logs in debug, this is useful if you just want to eyeball some performance times.
1 | FodyExample.MyMethod 96ms |
Interceptor MethodTimeLogger
You can hook into Fody
by writing some extensions that the weaver will look for and use when logging
- Define the method
1 | public static class MethodTimeLogger |
- Call as normal but pass the message param
1 | [ ] |
- This now logs the same as before but then you have access to the logging details which you can use with what ever metrics persistance your code base uses
1 | ZZZZ MethodTimeLogger |
The message is useful as you can then have some context of the load, here 42 could be creater than 1 so you would take that into account with your metrics.
MethodTimer.Fody Example
I often struggle to understand the value of something if I have never needed it, although the below is a contrived example, selecting the correct data structure is something most developers will come across in their daily code.
- Consider these methods
DemoUseDict(..)
andDemoUseLinq()
, here I pass in the data structures I want to query as I dont want to measure the creation, only the access. I dont do anything else like console writeline as that takes time, I just want to access the data. The variables
will be immutable, so we know a new string is created everytime but that shouldnt affect the results too much.
Spoiler Alert: Front of class nerds will know using a hash like Dictionary
will be Big O of 1 ie: O(1)
which is constant time, see Big O Notation. Lets ignore that for a second and just test the code
1 | public void DemoUseDict(Dictionary<int, DemoClass> dict) |
- Add the
[Time]
attribute as before
1 | public void DemoUseDict(Dictionary<int, DemoClass> dict) |
- Create the data upfront, again we are interested in access speed not creation and call the methods
1 | var dict = new Dictionary<int, DemoClass>(); |
- Running the results a few times and running them individually consistantly produces results as per the below, its clear the front of class kids were right about the hash but we now have some data to back it up.
1 | FodyActualExample.DemoUseDict 0ms |
Obviously the results would change based on the machines power, usage at the time and this is not an accurate average - but its at least a data driven decision based on a small sample set.
BenchmarkDotNet
This alternate library is more advanced and shows cool outputs as shown below but the setup is harder and I have not seen a real use case.
1 | | Method | Mean | Error | |