{"id":5723,"date":"2020-01-28T15:00:25","date_gmt":"2020-01-28T15:00:25","guid":{"rendered":"http:\/\/writeasync.net\/?p=5723"},"modified":"2020-01-27T03:44:30","modified_gmt":"2020-01-27T03:44:30","slug":"low-latency-latency-measurement","status":"publish","type":"post","link":"https:\/\/writeasync.net\/?p=5723","title":{"rendered":"Low-latency latency measurement"},"content":{"rendered":"<p>Measuring latency is easy with .NET. Just use a <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.diagnostics.stopwatch?view=netcore-3.1\">Stopwatch<\/a>!<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\nStopwatch stopwatch = Stopwatch.StartNew();\r\n\/\/ . . . do The Operation(s) here\r\nTimeSpan elapsed = stopwatch.Elapsed;\r\n\/\/ elapsed == how long it took to do The Operation(s)\r\n<\/pre>\n<p>This is certainly the <a href=\"https:\/\/www.dotnetperls.com\/stopwatch\">most commonly recommended way<\/a> to <a href=\"https:\/\/stackoverflow.com\/questions\/14019510\/calculate-the-execution-time-of-a-method\">measure operational timings<\/a>. But is it the fastest way?<\/p>\n<p>It seems almost absurd to ask for a low-latency latency measurement, but <a href=\"https:\/\/isocpp.org\/blog\/2012\/12\/three-optimization-tips-alexandrescu\">good performance discipline requires us to measure<\/a>. As they saying goes, &#8220;<a href=\"https:\/\/www.barrypopik.com\/index.php\/new_york_city\/entry\/one_test_is_worth_a_thousand_expert_opinions\">one test is worth a thousand expert opinions<\/a>.&#8221; So let&#8217;s come up with a benchmark:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n&#x5B;SimpleJob(RuntimeMoniker.NetCoreApp31)]\r\n&#x5B;MemoryDiagnoser]\r\npublic class Benchmarks\r\n{\r\n    &#x5B;Benchmark]\r\n    public long Watch()\r\n    {\r\n        Stopwatch stopwatch = Stopwatch.StartNew();\r\n        return stopwatch.Elapsed.Ticks;\r\n    }\r\n}\r\n<\/pre>\n<p>The result:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n| Method |     Mean |    Error |   StdDev |  Gen 0 | Gen 1 | Gen 2 | Allocated |\r\n|------- |---------:|---------:|---------:|-------:|------:|------:|----------:|\r\n|  Watch | 55.01 ns | 1.702 ns | 1.421 ns | 0.0051 |     - |     - |      40 B |\r\n<\/pre>\n<p>It&#8217;s pretty fast! There is one problem, though &#8212; what are we even supposed to compare this with? Is there another way to measure time that could actually be faster? I see only one realistic optimization opportunity here which is to get rid of the memory allocation penalty. Yes, Stopwatch is <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/csharp\/language-reference\/keywords\/reference-types\">reference type<\/a> (it is stateful after all) and thus requires heap allocation.<\/p>\n<p>The key realization is that calculating elapsed time merely requires subtracting a start time and an end time &#8212; two 64-bit integers if we are using <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.datetime.ticks?view=netcore-3.1#remarks\">ticks<\/a>. We should in principle be able to do this without allocating any additional memory. It just so happens that Stopwatch is still our friend in this journey, as it provides a <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.diagnostics.stopwatch.gettimestamp?view=netcore-3.1\">GetTimestamp static method<\/a>. The only problem is that it returns a value in units of <em>timer<\/em> ticks. Ah, but look, it also has a <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.diagnostics.stopwatch.frequency?view=netcore-3.1\">Frequency field<\/a> which gives the conversion between timer ticks and seconds. These insights give us the blueprint for a new struct type which marks points in time:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\npublic readonly struct TimePoint\r\n{\r\n    private static readonly double TicksPerTimerTick = 10000000.0 \/ Stopwatch.Frequency;\r\n\r\n    private readonly long timerTicks;\r\n\r\n    private TimePoint(long timerTicks)\r\n    {\r\n        this.timerTicks = timerTicks;\r\n    }\r\n\r\n    public static TimeSpan operator -(TimePoint x, TimePoint y)\r\n    {\r\n        long deltaTimerTicks = x.timerTicks - y.timerTicks;\r\n        double deltaTicks = TicksPerTimerTick * deltaTimerTicks;\r\n        return new TimeSpan((long)deltaTicks);\r\n    }\r\n\r\n    public static TimePoint Now() =&gt; new TimePoint(Stopwatch.GetTimestamp());\r\n\r\n    public TimeSpan Elapsed() =&gt; Now() - this;\r\n}\r\n<\/pre>\n<p>At last, we have a comparison benchmark for a maybe-faster latency tracker:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n&#x5B;Benchmark]\r\npublic long Point()\r\n{\r\n    TimePoint start = TimePoint.Now();\r\n    return start.Elapsed().Ticks;\r\n}\r\n<\/pre>\n<p>The results:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n| Method |     Mean |    Error |   StdDev |  Gen 0 | Gen 1 | Gen 2 | Allocated |\r\n|------- |---------:|---------:|---------:|-------:|------:|------:|----------:|\r\n|  Watch | 54.18 ns | 1.512 ns | 1.263 ns | 0.0051 |     - |     - |      40 B |\r\n|  Point | 40.20 ns | 0.417 ns | 0.325 ns |      - |     - |     - |         - |\r\n<\/pre>\n<p>We managed to get a 25% performance improvement! For now, I&#8217;m comfortable calling <code>TimePoint<\/code> the lowest latency low-latency latency measurement approach. Can you come up with a better way in .NET?<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Measuring latency is easy with .NET. Just use a Stopwatch! Stopwatch stopwatch = Stopwatch.StartNew(); \/\/ . . . do The Operation(s) here TimeSpan elapsed = stopwatch.Elapsed; \/\/ elapsed == how long it took to do The Operation(s) This is certainly&hellip; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[71,104],"tags":[],"class_list":["post-5723","post","type-post","status-publish","format-standard","hentry","category-diagnostics","category-performance"],"_links":{"self":[{"href":"https:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts\/5723","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=5723"}],"version-history":[{"count":1,"href":"https:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts\/5723\/revisions"}],"predecessor-version":[{"id":5724,"href":"https:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts\/5723\/revisions\/5724"}],"wp:attachment":[{"href":"https:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=5723"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=5723"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=5723"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}