-
Notifications
You must be signed in to change notification settings - Fork 57
Simple performance stopwatch
I often needed to know how long a process took. I do use BenchmarkDotNet, but often I want a simpler method, and My solution is TimeThings
. TimeThings
is very easy to use and very flexible (see next section), but its nowhere as good at BenchmarkDotNet
, but it only adds ~0.3 ms to the performance test. I use TimeThings
to get a "quick and dirty" performance while I'm building a new application, but if performance is important I use BenchmarkDotNet
to get accurate timing, for instance I used BenchmarkDotNet
in my Net.DistributedFileStoreCache.
I usually TimeThings
in my Xunit unit tests, and the code below shows a basic use, but you be aware most unit tests run in parallel, which affect the time a process takes. Therefore you need to run the Xunit's test that contains a TimeThings
on its own to get the correct performance timing (or if you have one unit test class that contains multiple TimeThings
s, then you can run all of the unit tests in the class because each unit test is run sequentially in a test class). The code below shows a Xunit with the a TimeThings
- the result performance time is sent to your Test Explorer's output panel.
public class PerfBookJsonContext
{
private readonly ITestOutputHelper _output;
public PerfBookJsonContext(ITestOutputHelper output) => _output = output;
[Fact]
public void TestWithTimeThings()
{
//SETUP
//your test setup
//ATTEMPT
using (new TimeThings(output)) //The performance time it sent to your
{
//your process you need to test and
}
//VERIFY
//your test checks
}
}
The TimeThings
has a version which takes an Action<TimeThingResult>
which is fills in the performance data when the TimeThings
is disposed.
[Fact]
public void TestTimeThingResultReturn()
{
//SETUP
TimeThingResult result = null;
//ATTEMPT
using (new TimeThings(x => result = x))
{
Thread.Sleep(10);
}
//VERIFY
//your test checks
}
-
new TimeThings(output)
: returns a string showing the time, e.g., "took 1.64 ms." -
new TimeThings(output, "Read book")
: This message took
The TimeThings
class has some optional parameters you can use, like message
and numRuns
- see below the method
/// <summary>
/// This will measure the time it took from this class being created to it being disposed and writes out to xUnit ITestOutputHelper
/// </summary>
/// <param name="output">On dispose it will write the result to the output</param>
/// <param name="message">Optional: a string to show in the result. Useful if you have multiple timing in one unit test.</param>
/// <param name="numRuns">Optional: if the timing covers multiple runs of something, then set numRuns to the number of runs and it will give you the average per run</param>
public TimeThings(ITestOutputHelper output, string message = "", int numRuns = 0)
{
// rest of code left out
So, if you used the TimeThings
class with new TimeThings(output, "read data", 500)
, you would get the string
500 x read data took 23.45 ms., ave. per run = 46.90 us.
The overall time is shown in ms., but if you set the numRuns
parameter, then the "ave. per run" time will be "scaled" , i.e. it will look at the time and give you the time that is easier to understand. The private TimeScaled
method does this.
private string TimeScaled(double timeMilliseconds)
{
if (timeMilliseconds > 5 * 1000)
return $"{timeMilliseconds / 1000:F3} sec."; //Seconds
if (timeMilliseconds > 5)
return $"{timeMilliseconds:#,###.00} ms."; //Milliseconds
if (timeMilliseconds > 5 / 1000.0)
return $"{timeMilliseconds * 1000:#,###.00} us."; //Microseconds
return $"{timeMilliseconds * 1000_000:#,###.0} ns."; //Nanoseconds
}
There is a version of the TimeThings
which return a TimeThingResult
which contains
- Testing against a PostgreSQL db
- Changes in EfCore.TestSupport 5
- Testing with production data
- Using an in-memory database (old)