From 0ee6b141d296de350938186d3889996e3ac3156a Mon Sep 17 00:00:00 2001 From: Lee Campbell Date: Mon, 30 May 2016 16:43:32 +0800 Subject: [PATCH] Correcting usage of Stopwatch TimeStamp Ticks vs DateTime Ticks --- README.md | 124 ++++++++++-------- .../SimpleHistogramExample.cs | 10 +- .../HgrmPercentileDistrubutionOutputTests.cs | 10 +- .../HistogramTestBase.cs | 2 +- src/HdrHistogram/HistogramBase.cs | 6 +- src/HdrHistogram/HistogramExtensions.cs | 20 +-- src/HdrHistogram/IntHistogram.cs | 4 +- src/HdrHistogram/LongHistogram.cs | 4 +- src/HdrHistogram/OutputScalingFactor.cs | 35 +++-- src/HdrHistogram/ShortHistogram.cs | 4 +- src/HdrHistogram/Utilities/Bitwise.cs | 3 +- 11 files changed, 127 insertions(+), 95 deletions(-) diff --git a/README.md b/README.md index c9b3aa5..43781c6 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,19 @@ - + #HdrHistogram **A High Dynamic Range (HDR) Histogram** [![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/HdrHistogram/HdrHistogram?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build status](https://ci.appveyor.com/api/projects/status/q0o5faahigq6u4qe/branch/master?svg=true)](https://ci.appveyor.com/project/LeeCampbell/hdrhistogram-net/branch/master) [![Build status](https://ci.appveyor.com/api/projects/status/q0o5faahigq6u4qe?svg=true)](https://ci.appveyor.com/project/LeeCampbell/hdrhistogram-net) ##What is it -HdrHistogram.NET is the official port of the Java HdrHistogram library. +HdrHistogram.NET is the official port of the Java HdrHistogram library. All official implementations of HdrHistogram can be found at https://github.com/HdrHistogram ##Why would I use it? You would use it to efficiently capture large number of response times measurements. -Often when measuring response times, one could make the common mistake of reporting on the mean value or the 90th percentile. -Gil Tene (the original author of the Java HdrHistogram) illustrates in numerous presentations (such as [here](http://www.infoq.com/presentations/latency-pitfalls) and [here](https://www.youtube.com/watch?v=9MKY4KypBzg)) on why this is a mistake. -Instead you want to collect all of the data and then be able to report your measurements across the range of measurements. +Often when measuring response times, one could make the common mistake of reporting on the mean value or the 90th percentile. +Gil Tene (the original author of the Java HdrHistogram) illustrates in numerous presentations (such as [here](http://www.infoq.com/presentations/latency-pitfalls) and [here](https://www.youtube.com/watch?v=9MKY4KypBzg)) on why this is a mistake. +Instead you want to collect all of the data and then be able to report your measurements across the range of measurements. ##How would I use it? The library is available as a package from Nuget as [HdrHistogram](https://www.nuget.org/packages/HdrHistogram/) @@ -24,24 +24,22 @@ To do this code might look something like this ### Declare the Histogram ``` csharp -//A single tick represents one hundred nanoseconds or one ten-millionth of a second. -//There are 10,000 ticks in a millisecond, or 10 million ticks in a second. -// https://msdn.microsoft.com/en-us/library/system.datetime.ticks(v=vs.110).aspx +// A Histogram covering the range from ~466 nanoseconds to 1 hour (3,600,000,000,000 ns) with a resolution of 3 significant figures: +var histogram = new LongHistogram(TimeStamp.Hours(1), 3); -// A Histogram covering the range from 100 nano-seconds to 1 hour (3,600,000,000,000 ns) with 3 decimal point resolution: -var histogram = new LongHistogram(TimeSpan.TicksPerHour, 3); - -``` +``` ### Record your measurements Next you would record your measurements. The `System.Diagnostics.Stopwatch.GetTimestamp()` method provides the most accurate way to record the elapsed time an action took to run. +By measuring the difference of the timestamp values before and after the action to measure, we can get the most accurate recording of elapsed +time available on the .NET platform. ``` csharp long startTimestamp = Stopwatch.GetTimestamp(); //Execute some action to be measured -long ticks = Stopwatch.GetTimestamp() - startTimestamp; -histogram.RecordValue(ticks); +long elapsed = Stopwatch.GetTimestamp() - startTimestamp; +histogram.RecordValue(elapsed); ``` @@ -54,9 +52,15 @@ Here we show an example of writing to the `Console`. ``` csharp var writer = new StringWriter(); -histogram.OutputPercentileDistribution(writer); +var scalingRatio = OutputScalingFactor.TimeStampToMicroseconds; +histogram.OutputPercentileDistribution( + writer, + outputValueUnitScalingRatio: scalingRatio); Console.WriteLine(writer.ToString()); -//Or just simply histogram.OutputPercentileDistribution(Console.Out); +//Or just simply write directly to the Console output stream +//histogram.OutputPercentileDistribution( +// Console.Out, +// outputValueUnitScalingRatio: scalingRatio); ``` Would produce output similar to: @@ -146,9 +150,14 @@ Would produce output similar to: #[Max = 2541.568, Total count = 35004] #[Buckets = 21, SubBuckets = 2048] ``` +Note that in the example above a value for the optional parameter `outputValueUnitScalingRatio` is provided. +If you record elapsed time using the suggested method with `Stopwatch.GetTimestamp()`, then you will have recorded values in a non-standard unit of time. +Instead of paying to cost of converting recorded values at the time of recording, record raw values. +Use the helper methods to convert recorded values to standard units at output time, when performance is less critical. + ###Example of reporting results as a chart -You can also have HdrHistogram output the results in a file format that can be charted. +You can also have HdrHistogram output the results in a file format that can be charted. This is especially useful when comparing measurements. First you will need to create the file to be used as an input for the chart. @@ -162,7 +171,7 @@ using (var writer = new StreamWriter("HistogramResults.hgrm")) The data can then be plotter to visualize the percentile distribution of your results. Multiple files can be plotted in the same chart allowing effective visual comparison of your results. -You can use either +You can use either * the online tool - http://hdrhistogram.github.io/HdrHistogram/plotFiles.html * the local tool - _.\GoogleChartsExample\plotFiles.html_ @@ -172,7 +181,7 @@ If you use the local tool, there are example result files in the _.\GoogleCharts The tool also allows you to export to png. ##So what is so special about this way of recording response times? - + * itself is low latency * tiny foot print due to just storing a dynamic range of buckets and counts * produces the reports you actually want @@ -182,8 +191,8 @@ This code sample show a recording of the time taken to execute a ping request. We execute and record this in a loop. ``` csharp -//Create a Histogram that records in ticks, values up to an hour, with accuracy to 3 significant digits. -var histogram = new LongHistogram(TimeSpan.TicksPerHour, 3); +// A Histogram covering the range from ~466 nanoseconds to 1 hour (3,600,000,000,000 ns) with a resolution of 3 significant figures: +var histogram = new LongHistogram(TimeStamp.Hours(1), 3); using (var ping = new System.Net.NetworkInformation.Ping()) { for (int i = 0; i < 100; i++) @@ -191,16 +200,15 @@ using (var ping = new System.Net.NetworkInformation.Ping()) long startTimestamp = Stopwatch.GetTimestamp(); //Execute our action we want to record. ping.Send("www.github.com"); - long ticks = Stopwatch.GetTimestamp() - startTimestamp; - //Record the elapsed ticks taken to execute the `Ping.Send` method - histogram.RecordValue(ticks); + long elapsed = Stopwatch.GetTimestamp() - startTimestamp; + histogram.RecordValue(elapsed); } } //Output the percentile distribution of our results to the Console with values presented in Milliseconds histogram.OutputPercentileDistribution( printStream: Console.Out, percentileTicksPerHalfDistance: 3, - outputValueUnitScalingRatio: OutputScalingFactor.TicksToMilliseconds); + outputValueUnitScalingRatio: OutputScalingFactor.TimeStampToMilliseconds); ``` @@ -249,33 +257,33 @@ Ideally if it is fixing an issue or a bug, there would be a Unit Test proving th HdrHistogram Details ---------------------------------------------- -An HdrHistogram supports the recording and analyzing of sampled data value counts across a configurable integer value range with configurable value precision within the range. +An HdrHistogram supports the recording and analyzing of sampled data value counts across a configurable integer value range with configurable value precision within the range. Value precision is expressed as the number of significant digits in the value recording, and provides control over value quantization behavior across the value range and the subsequent value resolution at any given level. -For example, a Histogram could be configured to track the counts of observed integer values between 0 and 3,600,000,000 while maintaining a value precision of 3 significant digits across that range. -Value quantization within the range will thus be no larger than 1/1,000th (or 0.1%) of any value. +For example, a Histogram could be configured to track the counts of observed integer values between 0 and 3,600,000,000 while maintaining a value precision of 3 significant digits across that range. +Value quantization within the range will thus be no larger than 1/1,000th (or 0.1%) of any value. This example Histogram could be used to track and analyze the counts of observed response times ranging between 1 microsecond and 1 hour in magnitude. -This Histogram would still maintain a value resolution of 1 microsecond up to 1 millisecond, a resolution of 1 millisecond (or better) up to one second, and a resolution of 1 second (or better) up to 1,000 seconds. +This Histogram would still maintain a value resolution of 1 microsecond up to 1 millisecond, a resolution of 1 millisecond (or better) up to one second, and a resolution of 1 second (or better) up to 1,000 seconds. At its maximum tracked value (1 hour), it would still maintain a resolution of 3.6 seconds (or better). -The HdrHistogram package includes the `LongHistogram` implementation, which tracks value counts in `long` fields, and is expected to be the commonly used Histogram form. +The HdrHistogram package includes the `LongHistogram` implementation, which tracks value counts in `long` fields, and is expected to be the commonly used Histogram form. `IntHistogram` and `ShortHistogram`, which track value counts in `int` and `short` fields respectively, are provided for use cases where smaller count ranges are practical and smaller overall storage is beneficial. Performance impacts should be measured prior to choosing one over the other in the name of optimization. -HdrHistogram is designed for recoding histograms of value measurements in latency and performance sensitive applications. +HdrHistogram is designed for recoding histograms of value measurements in latency and performance sensitive applications. Measurements show value recording times as low as 3-6 nanoseconds on modern (circa 2012) Intel CPUs. That is, 1,000,000,000 (1 billion) recordings can be made at a total cost of around 3 seconds on modern hardware. -A Histogram's memory footprint is constant, with no allocation operations involved in recording data values or in iterating through them. -The memory footprint is fixed regardless of the number of data value samples recorded, and depends solely on the dynamic range and precision chosen. +A Histogram's memory footprint is constant, with no allocation operations involved in recording data values or in iterating through them. +The memory footprint is fixed regardless of the number of data value samples recorded, and depends solely on the dynamic range and precision chosen. The amount of work involved in recording a sample is constant, and directly computes storage index locations such that no iteration or searching is ever involved in recording data values. -A combination of high dynamic range and precision is useful for collection and accurate post-recording analysis of sampled value data distribution in various forms. +A combination of high dynamic range and precision is useful for collection and accurate post-recording analysis of sampled value data distribution in various forms. Whether it's calculating or plotting arbitrary percentiles, iterating through and summarizing values in various ways, or deriving mean and standard deviation values, the fact that the recorded data information is kept in high resolution allows for accurate post-recording analysis with low [and ultimately configurable] loss in accuracy when compared to performing the same analysis directly on the potentially infinite series of sourced data values samples. -An common use example of HdrHistogram would be to record response times in units of microseconds across a dynamic range stretching from 1 usec to over an hour, with a good enough resolution to support later performing post-recording analysis on the collected data. +An common use example of HdrHistogram would be to record response times in units of microseconds across a dynamic range stretching from 1 usec to over an hour, with a good enough resolution to support later performing post-recording analysis on the collected data. Analysis can include computing, examining, and reporting of distribution by percentiles, linear or logarithmic value buckets, mean and standard deviation, or by any other means that can can be easily added by using the various iteration techniques supported by the Histogram. In order to facilitate the accuracy needed for various post-recording analysis techniques, this example can maintain a resolution of ~1 usec or better for times ranging to ~2 msec in magnitude, while at the same time maintaining a resolution of ~1 msec or better for times ranging to ~2 sec, and a resolution of ~1 second or better for values up to 2,000 seconds. -This sort of example resolution can be thought of as "always accurate to 3 decimal points." +This sort of example resolution can be thought of as "always accurate to 3 decimal points." Such an example Histogram would simply be created with a highestTrackableValue of 3,600,000,000, and a numberOfSignificantValueDigits of 3, and would occupy a fixed, unchanging memory footprint of around 185KB (see "Footprint estimation" below). @@ -289,21 +297,21 @@ The HdrHistogram package includes multiple implementations of the - `SynchronizedHistogram` (see 'Synchronization and concurrent access' below) Internally, data in HdrHistogram variants is maintained using a concept somewhat similar to that of floating point number representation. -Using an exponent a (non-normalized) mantissa to support a wide dynamic range at a high but varying (by exponent value) resolution. -Histograms use exponentially increasing bucket value ranges (the parallel of the exponent portion of a floating point number) with each bucket containing a fixed number (per bucket) set of linear sub-buckets (the parallel of a non-normalized mantissa portion of a floating point number). +Using an exponent a (non-normalized) mantissa to support a wide dynamic range at a high but varying (by exponent value) resolution. +Histograms use exponentially increasing bucket value ranges (the parallel of the exponent portion of a floating point number) with each bucket containing a fixed number (per bucket) set of linear sub-buckets (the parallel of a non-normalized mantissa portion of a floating point number). Both dynamic range and resolution are configurable, with `highestTrackableValue` controlling dynamic range, and `numberOfSignificantValueDigits` controlling resolution. Synchronization and concurrent access ---------------------------------------------- -In the interest of keeping value recording cost to a minimum, the commonly used `LongHistogram` class and its `IntHistogram` and `ShortHistogram` variants are NOT internally synchronized, and do NOT use atomic variables. -Callers wishing to make potentially concurrent, multi-threaded updates or queries against Histogram objects should either take care to externally synchronize and/or order their access, or use the `SynchronizedHistogram` variant. +In the interest of keeping value recording cost to a minimum, the commonly used `LongHistogram` class and its `IntHistogram` and `ShortHistogram` variants are NOT internally synchronized, and do NOT use atomic variables. +Callers wishing to make potentially concurrent, multi-threaded updates or queries against Histogram objects should either take care to externally synchronize and/or order their access, or use the `SynchronizedHistogram` variant. It is worth mentioning that since Histogram objects are additive, it is common practice to use per-thread, non-synchronized histograms for the recording fast path, and "flipping" the actively recorded-to histogram (usually with some non-locking variants on the fast path) and having a summary/reporting thread perform histogram aggregation math across time and/or threads. Iteration ---------------------------------------------- -Histograms supports multiple convenient forms of iterating through the histogram data set, including linear, logarithmic, and percentile iteration mechanisms, as well as means for iterating through each recorded value or each possible value level. +Histograms supports multiple convenient forms of iterating through the histogram data set, including linear, logarithmic, and percentile iteration mechanisms, as well as means for iterating through each recorded value or each possible value level. The iteration mechanisms are accessible through the HistogramData available through `getHistogramData()`. Iteration mechanisms all provide `HistogramIterationValue` data points along the histogram's iterated data set. @@ -318,12 +326,12 @@ All others are available for the default (corrected) histogram data set via the - `LinearBucketValues`: An `IEnumerable` through the histogram using a `LinearBucketEnumerable`/`LinearEnumerator` - `LogarithmicBucketValues`: An `IEnumerable` through the histogram using a `LogarithmicBucketEnumerable`/`LogarithmicEnumerator` - + Iteration is typically done with a for-each loop statement. E.g.: ``` csharp - foreach (var v in histogram.Percentiles(ticksPerHalfDistance)) + foreach (var v in histogram.Percentiles(ticksPerHalfDistance)) { ... } @@ -332,7 +340,7 @@ Iteration is typically done with a for-each loop statement. E.g.: or ``` csharp - for (var v in histogram.LinearBucketValues(unitsPerBucket)) + for (var v in histogram.LinearBucketValues(unitsPerBucket)) { ... } @@ -344,28 +352,28 @@ They are low allocation and may reuse objects internally to keep allocations low Equivalent Values and value ranges ---------------------------------------------- -Due to the finite (and configurable) resolution of the histogram, multiple adjacent integer data values can be "equivalent". -Two values are considered "equivalent" if samples recorded for both are always counted in a common total count due to the histogram's resolution level. -HdrHistogram provides methods for +Due to the finite (and configurable) resolution of the histogram, multiple adjacent integer data values can be "equivalent". +Two values are considered "equivalent" if samples recorded for both are always counted in a common total count due to the histogram's resolution level. +HdrHistogram provides methods for - * determining the lowest and highest equivalent values for any given value, - * determining whether two values are equivalent, + * determining the lowest and highest equivalent values for any given value, + * determining whether two values are equivalent, * finding the next non-equivalent value for a given value (useful when looping through values, in order to avoid a double-counting count). Corrected vs. Raw value recording calls ---------------------------------------------- -In order to support a common use case needed when histogram values are used to track response time distribution, Histogram provides for the recording of corrected histogram value by supporting a `RecordValueWithExpectedInterval(long, long)` variant is provided. +In order to support a common use case needed when histogram values are used to track response time distribution, Histogram provides for the recording of corrected histogram value by supporting a `RecordValueWithExpectedInterval(long, long)` variant is provided. This value recording form is useful in [common latency measurement] scenarios where response times may exceed the expected interval between issuing requests, leading to "dropped" response time measurements that would typically correlate with "bad" results. When a value recorded in the histogram exceeds the `expectedIntervalBetweenValueSamples` parameter, recorded histogram data will reflect an appropriate number of additional values, linearly decreasing in steps of `expectedIntervalBetweenValueSamples`, down to the last value that would still be higher than `expectedIntervalBetweenValueSamples`. -To illustrate why this corrective behavior is critically needed in order to accurately represent value distribution when large value measurements may lead to missed samples, imagine a system for which response times samples are taken once every 10 msec to characterize response time distribution. -The hypothetical system behaves "perfectly" for 100 seconds (10,000 recorded samples), with each sample showing a 1msec response time value. -At each sample for 100 seconds (10,000 logged samples at 1 msec each). +To illustrate why this corrective behavior is critically needed in order to accurately represent value distribution when large value measurements may lead to missed samples, imagine a system for which response times samples are taken once every 10 msec to characterize response time distribution. +The hypothetical system behaves "perfectly" for 100 seconds (10,000 recorded samples), with each sample showing a 1msec response time value. +At each sample for 100 seconds (10,000 logged samples at 1 msec each). The hypothetical system then encounters a 100 sec pause during which only a single sample is recorded (with a 100 second value). -The raw data histogram collected for such a hypothetical system (over the 200 second scenario above) would show ~99.99% of results at 1 msec or below, which is obviously "not right". -The same histogram, corrected with the knowledge of an `expectedIntervalBetweenValueSamples` of 10msec will correctly represent the response time distribution. +The raw data histogram collected for such a hypothetical system (over the 200 second scenario above) would show ~99.99% of results at 1 msec or below, which is obviously "not right". +The same histogram, corrected with the knowledge of an `expectedIntervalBetweenValueSamples` of 10msec will correctly represent the response time distribution. Only ~50% of results will be at 1 msec or below, with the remaining 50% coming from the auto-generated value records covering the missing increments spread between 10msec and 100 sec. Data sets recorded with and without an `expectedIntervalBetweenValueSamples` parameter will differ only if at least one value recorded with the `RecordValue(..)` method was greater than its associated `expectedIntervalBetweenValueSamples` parameter. @@ -377,8 +385,8 @@ Footprint estimation ---------------------------------------------- Due to it's dynamic range representation, Histogram is relatively efficient in memory space requirements given the accuracy and dynamic range it covers. -Still, it is useful to be able to estimate the memory footprint involved for a given `highestTrackableValue` and `numberOfSignificantValueDigits` combination. -Beyond a relatively small fixed-size footprint used for internal fields and stats (which can be estimated as "fixed at well less than 1KB"), the bulk of a Histogram's storage is taken up by it's data value recording counts array. +Still, it is useful to be able to estimate the memory footprint involved for a given `highestTrackableValue` and `numberOfSignificantValueDigits` combination. +Beyond a relatively small fixed-size footprint used for internal fields and stats (which can be estimated as "fixed at well less than 1KB"), the bulk of a Histogram's storage is taken up by it's data value recording counts array. The total footprint can be conservatively estimated by: ``` csharp @@ -395,7 +403,7 @@ A conservative (high) estimate of a Histogram's footprint in bytes is available ##Terminology - * **Latency** : The time that something is latent i.e. not being processed. + * **Latency** : The time that something is latent i.e. not being processed. This maybe due to being in a queue. * **Service Time** : The time taken to actually service a request. * **Response time** : The sum of the latency and the service time. e.g. the time your request was queued, plus the time it took to process. diff --git a/src/HdrHistogram.Examples/SimpleHistogramExample.cs b/src/HdrHistogram.Examples/SimpleHistogramExample.cs index 15482be..d6f8d46 100644 --- a/src/HdrHistogram.Examples/SimpleHistogramExample.cs +++ b/src/HdrHistogram.Examples/SimpleHistogramExample.cs @@ -20,7 +20,7 @@ namespace HdrHistogram.Examples /// static class SimpleHistogramExample { - private static readonly LongHistogram Histogram = new LongHistogram(TimeSpan.TicksPerHour, 3); + private static readonly LongHistogram Histogram = new LongHistogram(TimeStamp.Hours(1), 3); private static volatile Socket _socket; private static readonly Lazy AddressFamily = new Lazy(() => GetAddressFamily("google.com")); @@ -57,20 +57,20 @@ private static void OutputMeasurements() var size = Histogram.GetEstimatedFootprintInBytes(); Console.WriteLine("Histogram size = {0} bytes ({1:F2} MB)", size, size / 1024.0 / 1024.0); - Console.WriteLine("Recorded latencies [in ticks] for Create+Close of a DatagramSocket:"); + Console.WriteLine("Recorded latencies [in system clock ticks] for Create+Close of a DatagramSocket:"); Histogram.OutputPercentileDistribution(Console.Out, outputValueUnitScalingRatio: OutputScalingFactor.None); Console.WriteLine(); Console.WriteLine("Recorded latencies [in usec] for Create+Close of a DatagramSocket:"); - Histogram.OutputPercentileDistribution(Console.Out, outputValueUnitScalingRatio: OutputScalingFactor.TicksToMicroseconds); + Histogram.OutputPercentileDistribution(Console.Out, outputValueUnitScalingRatio: OutputScalingFactor.TimeStampToMicroseconds); Console.WriteLine(); Console.WriteLine("Recorded latencies [in msec] for Create+Close of a DatagramSocket:"); - Histogram.OutputPercentileDistribution(Console.Out, outputValueUnitScalingRatio: OutputScalingFactor.TicksToMilliseconds); + Histogram.OutputPercentileDistribution(Console.Out, outputValueUnitScalingRatio: OutputScalingFactor.TimeStampToMilliseconds); Console.WriteLine(); Console.WriteLine("Recorded latencies [in sec] for Create+Close of a DatagramSocket:"); - Histogram.OutputPercentileDistribution(Console.Out, outputValueUnitScalingRatio: OutputScalingFactor.TicksToSeconds); + Histogram.OutputPercentileDistribution(Console.Out, outputValueUnitScalingRatio: OutputScalingFactor.TimeStampToSeconds); } private static void CreateAndCloseDatagramSocket() diff --git a/src/HdrHistogram.UnitTests/HgrmPercentileDistrubutionOutputTests.cs b/src/HdrHistogram.UnitTests/HgrmPercentileDistrubutionOutputTests.cs index aa8daa0..cba53ef 100644 --- a/src/HdrHistogram.UnitTests/HgrmPercentileDistrubutionOutputTests.cs +++ b/src/HdrHistogram.UnitTests/HgrmPercentileDistrubutionOutputTests.cs @@ -1,16 +1,18 @@ -using System; -using System.Globalization; +using System.Globalization; using System.IO; using NUnit.Framework; namespace HdrHistogram.UnitTests { + /// + /// Validates against files generated by the official Java implementation. + /// [TestFixture] public sealed class HgrmPercentileDistrubutionOutputTests { [Test, Combinatorial] public void PercentileDistrubution_hgrm_output_is_in_correct_format( - [Values(TimeSpan.TicksPerHour)]long highestTrackableValue, + [Values(36000000000)]long highestTrackableValue, [Values(1, 2, 3, 4, 5)]int signigicantDigits, [Values(10000.0)]double scaling, [Values(5, 10, 20)]int percentileTicksPerHalfDistance) @@ -31,7 +33,7 @@ public void PercentileDistrubution_hgrm_output_is_in_correct_format( [Test, Combinatorial] public void PercentileDistrubution_csv_output_is_in_correct_format( - [Values(TimeSpan.TicksPerHour)]long highestTrackableValue, + [Values(36000000000)]long highestTrackableValue, [Values(1, 2, 3, 4, 5)]int signigicantDigits, [Values(10000.0)]double scaling, [Values(5, 10, 20)]int percentileTicksPerHalfDistance) diff --git a/src/HdrHistogram.UnitTests/HistogramTestBase.cs b/src/HdrHistogram.UnitTests/HistogramTestBase.cs index 710c744..0b6b599 100644 --- a/src/HdrHistogram.UnitTests/HistogramTestBase.cs +++ b/src/HdrHistogram.UnitTests/HistogramTestBase.cs @@ -5,7 +5,7 @@ namespace HdrHistogram.UnitTests { public abstract class HistogramTestBase { - private const long HighestTrackableValue = TimeSpan.TicksPerHour; // e.g. for 1 hr in ticks + private const long HighestTrackableValue = 7716549600;//TimeStamp.Hours(1); // e.g. for 1 hr in system clock ticks (StopWatch.Frequency) private const int NumberOfSignificantValueDigits = 3; private const long TestValueLevel = 4; diff --git a/src/HdrHistogram/HistogramBase.cs b/src/HdrHistogram/HistogramBase.cs index 156ac07..1b2eeb3 100644 --- a/src/HdrHistogram/HistogramBase.cs +++ b/src/HdrHistogram/HistogramBase.cs @@ -28,7 +28,7 @@ namespace HdrHistogram /// 36,000,000,000 while maintaining a value precision of 3 significant digits across that range. /// Value quantization within the range will thus be no larger than 1/1,000th (or 0.1%) of any value. /// This example Histogram could be used to track and analyze the counts of observed response times ranging between - /// 1 tick (100 nanoseconds) and 1 hour in magnitude, while maintaining a value resolution of 100 nanosecond up to + /// 100 nanoseconds and 1 hour in magnitude, while maintaining a value resolution of 100 nanosecond up to /// 100 microseconds, a resolution of 1 millisecond(or better) up to one second, and a resolution of 1 second /// (or better) up to 1,000 seconds. /// At it's maximum tracked value(1 hour), it would still maintain a resolution of 3.6 seconds (or better). @@ -122,8 +122,8 @@ public abstract class HistogramBase /// /// Providing a lowestTrackableValue is useful in situations where the units used for the histogram's values are much /// smaller that the minimal accuracy required. - /// For example when tracking time values stated in ticks (100 nanoseconds), where the minimal accuracy required is a - /// microsecond, the proper value for lowestTrackableValue would be 10. + /// For example when tracking time values stated in nanoseconds, where the minimal accuracy required is a + /// microsecond, the proper value for would be 1000. /// protected HistogramBase(long lowestTrackableValue, long highestTrackableValue, int numberOfSignificantValueDigits) { diff --git a/src/HdrHistogram/HistogramExtensions.cs b/src/HdrHistogram/HistogramExtensions.cs index c717ea6..168605f 100644 --- a/src/HdrHistogram/HistogramExtensions.cs +++ b/src/HdrHistogram/HistogramExtensions.cs @@ -111,7 +111,7 @@ public static IEnumerable Percentiles(this HistogramBas public static void OutputPercentileDistribution(this HistogramBase histogram, TextWriter writer, int percentileTicksPerHalfDistance = 5, - double outputValueUnitScalingRatio = OutputScalingFactor.TicksToMilliseconds, + double outputValueUnitScalingRatio = OutputScalingFactor.None, bool useCsvFormat = false) { var formatter = useCsvFormat @@ -144,7 +144,8 @@ public static void OutputPercentileDistribution(this HistogramBase histogram, /// /// Executes the action and records the time to complete the action. - /// The time is recorded in ticks. + /// The time is recorded in system clock ticks. + /// This time may vary between frameworks and platforms, but is equivalent to (1/Stopwatch.Frequency) seconds. /// Note this is a convenience method and can carry a cost. /// If the delegate is not cached, then it may incur an allocation cost for each invocation of /// @@ -152,9 +153,13 @@ public static void OutputPercentileDistribution(this HistogramBase histogram, /// The functionality to execute and measure /// /// - /// Ticks are used as the unit of recording here as they are the smallest unit that .NET can measure - /// and require no conversion at time of recording. Instead conversion (scaling) can be done at time - /// of output to microseconds, milliseconds, seconds or other appropriate unit. + /// The units returned from Stopwatch.GetTimestamp() are used as the unit of + /// recording here as they are the smallest unit that .NET can measure and require no + /// conversion at time of recording. + /// Instead conversion (scaling) can be done at time of output to microseconds, milliseconds, + /// seconds or other appropriate unit. + /// These units are sometimes refered to as ticks, but should not not to be confused with + /// ticks used in or . /// /// /// If you are able to cache the delegate, then doing so is encouraged. @@ -187,9 +192,8 @@ public static void Record(this HistogramBase histogram, Action action) { var start = Stopwatch.GetTimestamp(); action(); - var elapsedTicks = (Stopwatch.GetTimestamp() - start); - histogram.RecordValue(elapsedTicks); + var elapsed = Stopwatch.GetTimestamp() - start; + histogram.RecordValue(elapsed); } } - } \ No newline at end of file diff --git a/src/HdrHistogram/IntHistogram.cs b/src/HdrHistogram/IntHistogram.cs index a83ef91..4cfbc8b 100644 --- a/src/HdrHistogram/IntHistogram.cs +++ b/src/HdrHistogram/IntHistogram.cs @@ -23,7 +23,7 @@ namespace HdrHistogram /// 36,000,000,000 while maintaining a value precision of 3 significant digits across that range. /// Value quantization within the range will thus be no larger than 1/1,000th (or 0.1%) of any value. /// This example Histogram could be used to track and analyze the counts of observed response times ranging between - /// 1 tick (100 nanoseconds) and 1 hour in magnitude, while maintaining a value resolution of 100 nanosecond up to + /// 100 nanoseconds and 1 hour in magnitude, while maintaining a value resolution of 100 nanosecond up to /// 100 microseconds, a resolution of 1 millisecond(or better) up to one second, and a resolution of 1 second /// (or better) up to 1,000 seconds. /// At it's maximum tracked value(1 hour), it would still maintain a resolution of 3.6 seconds (or better). @@ -52,7 +52,7 @@ public IntHistogram(long highestTrackableValue, int numberOfSignificantValueDigi /// /// Construct a given the lowest and highest values to be tracked and a number of significant decimal digits. /// Providing a is useful is situations where the units used for the histogram's values are much smaller that the minimal accuracy required. - /// For example when tracking time values stated in ticks (100 nanosecond units), where the minimal accuracy required is a microsecond, the proper value for would be 10. + /// For example when tracking time values stated in nanoseconds, where the minimal accuracy required is a microsecond, the proper value for would be 1000. /// /// /// The lowest value that can be tracked (distinguished from 0) by the histogram. diff --git a/src/HdrHistogram/LongHistogram.cs b/src/HdrHistogram/LongHistogram.cs index c5979bd..5584a30 100644 --- a/src/HdrHistogram/LongHistogram.cs +++ b/src/HdrHistogram/LongHistogram.cs @@ -23,7 +23,7 @@ namespace HdrHistogram /// 36,000,000,000 while maintaining a value precision of 3 significant digits across that range. /// Value quantization within the range will thus be no larger than 1/1,000th (or 0.1%) of any value. /// This example Histogram could be used to track and analyze the counts of observed response times ranging between - /// 1 tick (100 nanoseconds) and 1 hour in magnitude, while maintaining a value resolution of 100 nanosecond up to + /// 100 nanoseconds and 1 hour in magnitude, while maintaining a value resolution of 100 nanosecond up to /// 100 microseconds, a resolution of 1 millisecond(or better) up to one second, and a resolution of 1 second /// (or better) up to 1,000 seconds. /// At it's maximum tracked value(1 hour), it would still maintain a resolution of 3.6 seconds (or better). @@ -51,7 +51,7 @@ public LongHistogram(long highestTrackableValue, int numberOfSignificantValueDig /// /// Construct a given the lowest and highest values to be tracked and a number of significant decimal digits. /// Providing a is useful is situations where the units used for the histogram's values are much smaller that the minimal accuracy required. - /// For example when tracking time values stated in tick (100 nanosecond units), where the minimal accuracy required is a microsecond, the proper value for would be 10. + /// For example when tracking time values stated in nanosecond units, where the minimal accuracy required is a microsecond, the proper value for would be 1000. /// /// /// The lowest value that can be tracked (distinguished from 0) by the histogram. diff --git a/src/HdrHistogram/OutputScalingFactor.cs b/src/HdrHistogram/OutputScalingFactor.cs index 5f6cd1f..f262027 100644 --- a/src/HdrHistogram/OutputScalingFactor.cs +++ b/src/HdrHistogram/OutputScalingFactor.cs @@ -6,7 +6,7 @@ * http://creativecommons.org/publicdomain/zero/1.0/ */ -using System; +using System.Diagnostics; namespace HdrHistogram { @@ -16,23 +16,40 @@ namespace HdrHistogram public static class OutputScalingFactor { /// - /// For use when values are recorded in ticks and output should be in ticks + /// For use when values are recorded and reported in the same unit of measurement. /// public const double None = 1.0; - + /// - /// For use when values are recorded in ticks and output should be measured in microseconds. + /// For use when values are recorded with and output should be reported in microseconds. /// - public const double TicksToMicroseconds = 10.0; + public static readonly double TimeStampToMicroseconds = Stopwatch.Frequency / (1000d * 1000d); /// - /// For use when values are recorded in ticks and output should be measured in milliseconds. + /// For use when values are recorded with and output should be reported in milliseconds. /// - public const double TicksToMilliseconds = TimeSpan.TicksPerMillisecond; + public static readonly double TimeStampToMilliseconds = Stopwatch.Frequency / 1000d; /// - /// For use when values are recorded in ticks and output should be measured in seconds. + /// For use when values are recorded with and output should be reported in seconds. /// - public const double TicksToSeconds = TimeSpan.TicksPerSecond; + public static readonly double TimeStampToSeconds = Stopwatch.Frequency; + } + + public static class TimeStamp + { + public static long Seconds(int seconds) + { + return Stopwatch.Frequency*seconds; + } + + public static long Minutes(int minutes) + { + return Stopwatch.Frequency * minutes * 60L; + } + public static long Hours(int hours) + { + return Stopwatch.Frequency * hours * 60L * 60L; + } } } \ No newline at end of file diff --git a/src/HdrHistogram/ShortHistogram.cs b/src/HdrHistogram/ShortHistogram.cs index bddad47..ea505ca 100644 --- a/src/HdrHistogram/ShortHistogram.cs +++ b/src/HdrHistogram/ShortHistogram.cs @@ -24,7 +24,7 @@ namespace HdrHistogram /// 36,000,000,000 while maintaining a value precision of 3 significant digits across that range. /// Value quantization within the range will thus be no larger than 1/1,000th (or 0.1%) of any value. /// This example Histogram could be used to track and analyze the counts of observed response times ranging between - /// 1 tick (100 nanoseconds) and 1 hour in magnitude, while maintaining a value resolution of 100 nanosecond up to + /// 100 nanoseconds and 1 hour in magnitude, while maintaining a value resolution of 100 nanosecond up to /// 100 microseconds, a resolution of 1 millisecond(or better) up to one second, and a resolution of 1 second /// (or better) up to 1,000 seconds. /// At it's maximum tracked value(1 hour), it would still maintain a resolution of 3.6 seconds (or better). @@ -53,7 +53,7 @@ public ShortHistogram(long highestTrackableValue, int numberOfSignificantValueDi /// /// Construct a given the lowest and highest values to be tracked and a number of significant decimal digits. /// Providing a is useful is situations where the units used for the histogram's values are much smaller that the minimal accuracy required. - /// For example when tracking time values stated in ticks (100 nanosecond units), where the minimal accuracy required is a microsecond, the proper value for would be 10. + /// For example when tracking time values stated in nanoseconds, where the minimal accuracy required is a microsecond, the proper value for would be 1000. /// /// /// The lowest value that can be tracked (distinguished from 0) by the histogram. diff --git a/src/HdrHistogram/Utilities/Bitwise.cs b/src/HdrHistogram/Utilities/Bitwise.cs index bcc0ef7..d81569c 100644 --- a/src/HdrHistogram/Utilities/Bitwise.cs +++ b/src/HdrHistogram/Utilities/Bitwise.cs @@ -31,7 +31,8 @@ static Bitwise() } public static int NumberOfLeadingZeros(long value) { - //Optimisation for 32 bit values. So for any value under 00:03:34.7 when measuring in ticks, we will hit a fast path. + //Optimisation for 32 bit values. So values under 00:16:41.0 when measuring with Stopwatch.GetTimestamp()*, we will hit a fast path. + // * as at writing on Win10 .NET 4.6 if (value < int.MaxValue) return 63 - Log2((int)value); return NumberOfLeadingZerosLong(value);