-
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 add ~2ms 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 TimeThingss, 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 outSo, 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)