Skip to content
This repository has been archived by the owner on Dec 19, 2018. It is now read-only.

Higher accuracy in timings #543

Closed
wants to merge 1 commit into from

Conversation

benaadams
Copy link
Contributor

Use Stopwatch.GetTimestamp for sub millisecond timings

Resolves #544

@benaadams
Copy link
Contributor Author

Before with Environment.TickCount

info: Microsoft.AspNet.Hosting.Internal.WebApplication[1]
Request starting HTTP/1.1 GET http://localhost:5001/plaintext
info: Microsoft.AspNet.Hosting.Internal.WebApplication[2]
Request finished in 15ms 200
info: Microsoft.AspNet.Hosting.Internal.WebApplication[1]
Request starting HTTP/1.1 GET http://localhost:5001/favicon.ico
info: Microsoft.AspNet.Hosting.Internal.WebApplication[2]
Request finished in 0ms 200
info: Microsoft.AspNet.Hosting.Internal.WebApplication[1]
Request starting HTTP/1.1 GET http://localhost:5001/plaintext
info: Microsoft.AspNet.Hosting.Internal.WebApplication[2]
Request finished in 0ms 200
info: Microsoft.AspNet.Hosting.Internal.WebApplication[1]
Request starting HTTP/1.1 GET http://localhost:5001/favicon.ico
info: Microsoft.AspNet.Hosting.Internal.WebApplication[2]
Request finished in 0ms 200
info: Microsoft.AspNet.Hosting.Internal.WebApplication[1]
Request starting HTTP/1.1 GET http://localhost:5001/plaintext
info: Microsoft.AspNet.Hosting.Internal.WebApplication[2]
Request finished in 0ms 200
info: Microsoft.AspNet.Hosting.Internal.WebApplication[1]
Request starting HTTP/1.1 GET http://localhost:5001/favicon.ico
info: Microsoft.AspNet.Hosting.Internal.WebApplication[2]
Request finished in 0ms 200
info: Microsoft.AspNet.Hosting.Internal.WebApplication[1]
Request starting HTTP/1.1 GET http://localhost:5001/plaintext
info: Microsoft.AspNet.Hosting.Internal.WebApplication[2]
Request finished in 15ms 200
info: Microsoft.AspNet.Hosting.Internal.WebApplication[1]
Request starting HTTP/1.1 GET http://localhost:5001/favicon.ico
info: Microsoft.AspNet.Hosting.Internal.WebApplication[2]
Request finished in 0ms 200
info: Microsoft.AspNet.Hosting.Internal.WebApplication[1]
Request starting HTTP/1.1 GET http://localhost:5001/plaintext
info: Microsoft.AspNet.Hosting.Internal.WebApplication[2]
Request finished in 0ms 200
info: Microsoft.AspNet.Hosting.Internal.WebApplication[1]
Request starting HTTP/1.1 GET http://localhost:5001/favicon.ico
info: Microsoft.AspNet.Hosting.Internal.WebApplication[2]
Request finished in 0ms 200

After with Stopwatch.GetTimestamp()

Request starting HTTP/1.1 GET http://localhost:5001/plaintext
info: Microsoft.AspNet.Hosting.Internal.WebApplication[2]
Request finished in 0.2912ms 200
info: Microsoft.AspNet.Hosting.Internal.WebApplication[1]
Request starting HTTP/1.1 GET http://localhost:5001/favicon.ico
info: Microsoft.AspNet.Hosting.Internal.WebApplication[2]
Request finished in 0.2585ms 200
info: Microsoft.AspNet.Hosting.Internal.WebApplication[1]
Request starting HTTP/1.1 GET http://localhost:5001/plaintext
info: Microsoft.AspNet.Hosting.Internal.WebApplication[2]
Request finished in 0.2394ms 200
info: Microsoft.AspNet.Hosting.Internal.WebApplication[1]
Request starting HTTP/1.1 GET http://localhost:5001/favicon.ico
info: Microsoft.AspNet.Hosting.Internal.WebApplication[2]
Request finished in 0.2698ms 200
info: Microsoft.AspNet.Hosting.Internal.WebApplication[1]
Request starting HTTP/1.1 GET http://localhost:5001/plaintext
info: Microsoft.AspNet.Hosting.Internal.WebApplication[2]
Request finished in 0.3228ms 200
info: Microsoft.AspNet.Hosting.Internal.WebApplication[1]
Request starting HTTP/1.1 GET http://localhost:5001/favicon.ico
info: Microsoft.AspNet.Hosting.Internal.WebApplication[2]
Request finished in 0.3882ms 200
info: Microsoft.AspNet.Hosting.Internal.WebApplication[1]
Request starting HTTP/1.1 GET http://localhost:5001/plaintext
info: Microsoft.AspNet.Hosting.Internal.WebApplication[2]
Request finished in 0.3152ms 200
info: Microsoft.AspNet.Hosting.Internal.WebApplication[1]
Request starting HTTP/1.1 GET http://localhost:5001/favicon.ico
info: Microsoft.AspNet.Hosting.Internal.WebApplication[2]
Request finished in 0.2772ms 200
info: Microsoft.AspNet.Hosting.Internal.WebApplication[1]
Request starting HTTP/1.1 GET http://localhost:5001/plaintext
info: Microsoft.AspNet.Hosting.Internal.WebApplication[2]
Request finished in 0.3678ms 200
info: Microsoft.AspNet.Hosting.Internal.WebApplication[1]
Request starting HTTP/1.1 GET http://localhost:5001/favicon.ico
info: Microsoft.AspNet.Hosting.Internal.WebApplication[2]
Request finished in 0.2701ms 200
info: Microsoft.AspNet.Hosting.Internal.WebApplication[1]
Request starting HTTP/1.1 GET http://localhost:5001/plaintext
info: Microsoft.AspNet.Hosting.Internal.WebApplication[2]
Request finished in 0.3148ms 200

@benaadams
Copy link
Contributor Author

Benchmark: https://gist.github.com/benaadams/988b223a2433dcf773fc

Environment.TickCount is the lowest cost

BenchmarkDotNet-Dev=v0.8.0.0+
OS=Microsoft Windows NT 6.2.9200.0
Processor=Intel(R) Core(TM) i7-4720HQ CPU @ 2.60GHz, ProcessorCount=8
HostCLR=MS.NET 4.0.30319.42000, Arch=64-bit  [RyuJIT]
Type=Measurements  Mode=Throughput  Platform=X64  Jit=RyuJit  .NET=HostFramework  toolchain=Classic  Runtime=Clr  Warmup=5  Target=10
Method AvrTime StdDev op/s
DateTimeNowTicks 24.6842 ns 0.3468 ns 40,519,628.87
DateTimeUtcNowTicks 0.5665 ns 0.0098 ns 1,765,792,561.14
EnvironmentTickCount 0.1741 ns 0.0036 ns 5,745,520,332.73
StopwatchGetTimestamp 1.4230 ns 0.0231 ns 702,899,855.26

Will reduce the cost when not logging times

@benaadams
Copy link
Contributor Author

Now best of both worlds?

benaadams added a commit to benaadams/Mvc that referenced this pull request Jan 1, 2016
@omariom
Copy link

omariom commented Jan 1, 2016

Can the logger be enabled after the app has started?

@benaadams
Copy link
Contributor Author

@DamianEdwards suggested it can at a talk

@benaadams benaadams force-pushed the moar-accurate-timings branch from 8fcf38b to 22ca4a6 Compare January 2, 2016 00:44
@benaadams
Copy link
Contributor Author

@pranavkm pointed out in aspnet/Mvc#3850 (diff) that you still get the right results if it wraps without needing to do the additions and subtractions, so taken them out.

benaadams added a commit to benaadams/Mvc that referenced this pull request Jan 2, 2016
benaadams added a commit to benaadams/Mvc that referenced this pull request Jan 2, 2016
@DamianEdwards
Copy link
Member

Isn't DateTime.UtcNow math accurate enough for this without having to resort to Stopwatch?

@benaadams
Copy link
Contributor Author

While DateTime.UtcNow has a high precision down to 100ns; it only updates every 5ms - 15ms depending on current system timer; so it has a low accuracy.

It would mean you'd get the same effect as with Environment.TickCount of requests under 15ms output as either being 0ms or 15ms (and in 15ms increments for ones that took longer).

@benaadams
Copy link
Contributor Author

It would likely be 4ms resolution on linux dotnet/coreclr#2293

@benaadams
Copy link
Contributor Author

Also on OSX at sure its even precise either as it might be also rounded to the millisecond https://github.com/dotnet/coreclr/issues/1300

@mattwarren
Copy link

While DateTime.UtcNow has a high precision down to 100ns; it only updates every 5ms - 15ms depending on current system timer; so it has a low accuracy.

You can actually use BenchmarkDotNet to give you this information, just write a benchmark like so for each method (Stopwatch, DateTime.Now, DateTime.UtcNow and Environment.TickCount):

[Benchmark]
public long StopwatchGranularity()
{
    // Keep calling Stopwatch.GetTimestamp() till we get a different/new value
    long current, lastValue = Stopwatch.GetTimestamp();
    do
    {
        current = Stopwatch.GetTimestamp();
    } while (current == lastValue);
    lastValue = current;
    return current;
}

And this article is also a pretty good read, http://shipilev.net/blog/2014/nanotrusting-nanotime/

pranavkm pushed a commit to aspnet/Mvc that referenced this pull request Jan 5, 2016

if (exception == null)
{
if (_diagnosticSource.IsEnabled("Microsoft.AspNet.Hosting.EndRequest"))
{
_diagnosticSource.Write("Microsoft.AspNet.Hosting.EndRequest", new { httpContext = httpContext, tickCount = currentTick });
_diagnosticSource.Write("Microsoft.AspNet.Hosting.EndRequest", new { httpContext = httpContext, tickCount = currentTimestamp });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a TimeSpan? For starters tickCount is a bit of a misnomer with this change and doesn't convey the unit of measurement.

Also, changing this from int -> long \ TimeSpan, this would be a breaking change I know @avanderhoorn uses it in glimpse. Are there other folks we'd need to notify about this change?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A time stamp isn't a TimeSpan. It's closer to a DateTime.

Rename the parameter.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh. I thought it was subtracting the values. DateTimeOffset in that case?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On a tangent, why wouldn't the listener time this? Wouldn't they need to pair the start-stop events to make sense of this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to timestamp; also enabled the checking if either the diagnostics is enabled or the logging (rather than just logging)

@Tratcher Tratcher self-assigned this Jan 8, 2016
@Tratcher Tratcher added this to the 1.0.0-rc2 milestone Jan 8, 2016
@Tratcher
Copy link
Member

Tratcher commented Jan 8, 2016

@avanderhoorn, what do you think?

@avanderhoorn
Copy link
Member

+1 Looks good to me.

@benaadams benaadams force-pushed the moar-accurate-timings branch from bd7532a to 076b57a Compare January 9, 2016 09:49
@Tratcher Tratcher assigned JunTaoLuo and unassigned Tratcher Jan 13, 2016
@Tratcher
Copy link
Member

@JunTaoLuo can you merge this?

@JunTaoLuo
Copy link
Contributor

Merged to dev in fa72fde

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants