From 83c189d0c4417497ab5d68690ae99315a31a168b Mon Sep 17 00:00:00 2001 From: Lee Campbell Date: Fri, 2 Sep 2016 11:31:26 +0800 Subject: [PATCH] Adding HistogramFactory. Updated RecorderExample --- .../Recording/Recording32BitBenchmark.cs | 47 ++- .../HdrHistogram.Examples.csproj | 1 + src/HdrHistogram.Examples/Program.cs | 5 +- src/HdrHistogram.Examples/RecorderExample.cs | 99 +++--- src/HdrHistogram.Examples/SocketTester.cs | 39 ++ .../HdrHistogram.UnitTests.csproj | 3 +- .../HistogramFactoryTests.cs | 222 ++++++++++++ .../HistogramTestBase.cs | 107 +++--- .../IntConcurrentHistogramTests.cs | 15 +- .../IntHistogramEncodingTests.cs | 6 +- .../IntHistogramTests.cs | 13 +- .../LongConcurrentHistogramTests.cs | 15 +- .../LongHistogramEncodingTests.cs | 6 +- .../LongHistogramTests.cs | 13 +- .../RecorderTestWithIntConcurrentHistogram.cs | 21 +- .../Recording/RecorderTestWithIntHistogram.cs | 19 +- .../RecorderTestWithLShortHistogram.cs | 13 - ...RecorderTestWithLongConcurrentHistogram.cs | 20 +- .../RecorderTestWithLongHistogram.cs | 19 +- .../RecorderTestWithShortHistogram.cs | 28 ++ .../Recording/RecorderTestsBase.cs | 41 ++- .../ShortHistogramEncodingTests.cs | 8 +- .../ShortHistogramTests.cs | 13 +- src/HdrHistogram/HdrHistogram.csproj | 2 + src/HdrHistogram/Histogram.cs | 332 ++++++++++++++++++ src/HdrHistogram/HistogramFactoryDelegate.cs | 20 ++ src/HdrHistogram/HistogramLogWriter.cs | 7 +- src/HdrHistogram/Recorder.cs | 23 +- 28 files changed, 969 insertions(+), 188 deletions(-) create mode 100644 src/HdrHistogram.Examples/SocketTester.cs create mode 100644 src/HdrHistogram.UnitTests/HistogramFactoryTests.cs delete mode 100644 src/HdrHistogram.UnitTests/Recording/RecorderTestWithLShortHistogram.cs create mode 100644 src/HdrHistogram.UnitTests/Recording/RecorderTestWithShortHistogram.cs create mode 100644 src/HdrHistogram/Histogram.cs create mode 100644 src/HdrHistogram/HistogramFactoryDelegate.cs diff --git a/src/HdrHistogram.Benchmarking/Recording/Recording32BitBenchmark.cs b/src/HdrHistogram.Benchmarking/Recording/Recording32BitBenchmark.cs index 5d26ef2..fdf65fa 100644 --- a/src/HdrHistogram.Benchmarking/Recording/Recording32BitBenchmark.cs +++ b/src/HdrHistogram.Benchmarking/Recording/Recording32BitBenchmark.cs @@ -20,9 +20,39 @@ public class Recording32BitBenchmark public Recording32BitBenchmark() { - //Create array of +ve numbers in the 'maxBit' bit range (i.e. 32 bit or 64bit) + const int lowestTrackableValue = 1; var highestTrackableValue = TimeStamp.Minutes(10); - _testValues = Enumerable.Range(0, 32) + const int numberOfSignificantValueDigits = 3; + + _testValues = TestValues(highestTrackableValue); + + _longHistogram = new LongHistogram(highestTrackableValue, numberOfSignificantValueDigits); + _intHistogram = new IntHistogram(highestTrackableValue, numberOfSignificantValueDigits); + _shortHistogram = new ShortHistogram(highestTrackableValue, numberOfSignificantValueDigits); + + _longConcurrentHistogram = new LongConcurrentHistogram(lowestTrackableValue, highestTrackableValue, numberOfSignificantValueDigits); + _intConcurrentHistogram = new IntConcurrentHistogram(lowestTrackableValue, highestTrackableValue, numberOfSignificantValueDigits); + + _longRecorder = new Recorder(lowestTrackableValue, highestTrackableValue, numberOfSignificantValueDigits, (id, low, hi, sf) => new LongHistogram(id, low, hi, sf)); + _longConcurrentRecorder = new Recorder(lowestTrackableValue, highestTrackableValue, numberOfSignificantValueDigits, (id, low, hi, sf) => new LongConcurrentHistogram(id, low, hi, sf)); + _intRecorder = new Recorder(lowestTrackableValue, highestTrackableValue, numberOfSignificantValueDigits, (id, low, hi, sf) => new IntHistogram(id, low, hi, sf)); + _intConcurrentRecorder = new Recorder(lowestTrackableValue, highestTrackableValue, numberOfSignificantValueDigits, (id, low, hi, sf) => new IntConcurrentHistogram(id, low, hi, sf)); + _shortRecorder = new Recorder(lowestTrackableValue, highestTrackableValue, numberOfSignificantValueDigits, (id, low, hi, sf) => new ShortHistogram(id, low, hi, sf)); + } + + private static long[] TestValues(long highestTrackableValue) + { + //Create array of +ve numbers in the 'maxBit' bit range (i.e. 32 bit or 64bit) + // 32 bit values are the 89 values + // 1,2,3,4,5,7,8,9,15,16,17,31,32,33,63,64,65,127,128,129,255,256,257,511,512,513,1023,1024,1025,2047,2048,2049,4095,4096,4097,8191,8192,8193, + // 16383,16384,16385,32767,32768,32769,65535,65536,65537,131071,131072,131073,262143,262144,262145,524287,524288,524289,1048575,1048576,1048577, + // 2097151,2097152,2097153,4194303,4194304,4194305,8388607,8388608,8388609,16777215,16777216,16777217,33554431,33554432,33554433, + // 67108863,67108864,67108865,134217727,134217728,134217729,268435455,268435456,268435457,536870911,536870912,536870913,1073741823,1073741824,1073741825 + //These value are choosen as they are the edge case values of where our bucket boundaries lie. i.e. + // a power of 2 + // 1 less than a power of 2 + // 1 more than a power of 2 + return Enumerable.Range(0, 32) .Select(exp => new { Value = 1L << exp, LZC = 63 - exp }) .SelectMany(x => new[] { @@ -34,19 +64,6 @@ public Recording32BitBenchmark() .Where(x => x < highestTrackableValue) .Distinct() .ToArray(); - - _longHistogram = new LongHistogram(highestTrackableValue, 3); - _intHistogram = new IntHistogram(highestTrackableValue, 3); - _shortHistogram = new ShortHistogram(highestTrackableValue, 3); - - _longConcurrentHistogram = new LongConcurrentHistogram(1, highestTrackableValue, 3); - _intConcurrentHistogram = new IntConcurrentHistogram(1, highestTrackableValue, 3); - - _longRecorder = new Recorder(1, highestTrackableValue, 3, (id, low, hi, sf) => new LongHistogram(id, low, hi, sf)); - _longConcurrentRecorder = new Recorder(1, highestTrackableValue, 3, (id, low, hi, sf) => new LongConcurrentHistogram(id, low, hi, sf)); - _intRecorder = new Recorder(1, highestTrackableValue, 3, (id, low, hi, sf) => new IntHistogram(id, low, hi, sf)); - _intConcurrentRecorder = new Recorder(1, highestTrackableValue, 3, (id, low, hi, sf) => new IntConcurrentHistogram(id, low, hi, sf)); - _shortRecorder = new Recorder(1, highestTrackableValue, 3, (id, low, hi, sf) => new ShortHistogram(id, low, hi, sf)); } [Benchmark(Baseline = true)] diff --git a/src/HdrHistogram.Examples/HdrHistogram.Examples.csproj b/src/HdrHistogram.Examples/HdrHistogram.Examples.csproj index be9f193..630b296 100644 --- a/src/HdrHistogram.Examples/HdrHistogram.Examples.csproj +++ b/src/HdrHistogram.Examples/HdrHistogram.Examples.csproj @@ -45,6 +45,7 @@ + diff --git a/src/HdrHistogram.Examples/Program.cs b/src/HdrHistogram.Examples/Program.cs index 142fb83..fd2eb11 100644 --- a/src/HdrHistogram.Examples/Program.cs +++ b/src/HdrHistogram.Examples/Program.cs @@ -5,7 +5,10 @@ class Program static void Main(string[] args) { //SimpleHistogramExample.Run(); - RecorderExample.Run(); + using (var example = new RecorderExample()) + { + example.Run(); + } } } } diff --git a/src/HdrHistogram.Examples/RecorderExample.cs b/src/HdrHistogram.Examples/RecorderExample.cs index 826971f..ca501ef 100644 --- a/src/HdrHistogram.Examples/RecorderExample.cs +++ b/src/HdrHistogram.Examples/RecorderExample.cs @@ -1,8 +1,6 @@ using System; using System.Diagnostics; using System.IO; -using System.Net; -using System.Net.Sockets; using System.Threading; namespace HdrHistogram.Examples @@ -12,82 +10,103 @@ namespace HdrHistogram.Examples /// time it takes to perform a simple Datagram Socket create/close operation, /// and report a histogram of the times at the end. /// - static class RecorderExample + internal sealed class RecorderExample : IDisposable { - private static readonly Recorder Recorder = new Recorder(1, TimeStamp.Hours(1), 3, (id, low, high, sf) => new LongHistogram(id, low, high, sf)); - private static readonly Lazy AddressFamily = new Lazy(() => GetAddressFamily("google.com")); + private const string LogPath = "DatagramSocket.histogram.log"; private static readonly TimeSpan RunPeriod = TimeSpan.FromSeconds(10); - private static readonly LongHistogram AccumulatingHistogram = new LongHistogram(TimeStamp.Hours(1), 3); - private const string LogPath = "DatagramSocket.histogram.log"; - private static HistogramLogWriter _logWriter; - private static FileStream _outputStream; - private static int _isCompleted = 0; - public static void Run() - { - Console.WriteLine($"Running for {RunPeriod.TotalSeconds}sec."); + private readonly HistogramLogWriter _logWriter; + private readonly FileStream _outputStream; + private int _isCompleted = -1; + + public RecorderExample() + { _outputStream = File.Create(LogPath); _logWriter = new HistogramLogWriter(_outputStream); + } + + public void Run() + { + if (HasRunBeenCalled()) + throw new InvalidOperationException("Can only call run once."); + + Console.WriteLine($"Running for {RunPeriod.TotalSeconds}sec."); + //Write the headers, but no histograms (as we don't have any yet). _logWriter.Write(DateTime.Now); - var outputThread = new Thread(ts => WriteToDisk()); - outputThread.Start(); - RecordMeasurements(); + //ThreadSafe-writes require a Concurrent implementation of a Histogram + //ThreadSafe-reads require a recorder + var recorder = HistogramFactory + .With64BitBucketSize() //LongHistogram + .WithValuesFrom(1) //Default value + .WithValuesUpTo(TimeStamp.Minutes(10)) //Default value + .WithPrecisionOf(3) //Default value + .WithThreadSafeWrites() //Switches internal imp to concurrent version i.e. LongConcurrentHistogram + .WithThreadSafeReads() //returns a Recorder that wraps the LongConcurrentHistogram + .Create(); + + var outputThread = new Thread(ts => WriteToDisk((Recorder)ts)); + outputThread.Start(recorder); + + RecordMeasurements(recorder); + + //Wait for the output thread to complete writing. + outputThread.Join(); + } + + private bool HasRunBeenCalled() + { + var currentValue = Interlocked.CompareExchange(ref _isCompleted, 0, -1); + return currentValue != -1; } - private static void WriteToDisk() + private void WriteToDisk(Recorder recorder) { //Sample every second until flagged as completed. + var accumulatingHistogram = new LongHistogram(TimeStamp.Hours(1), 3); while (_isCompleted == 0) { Thread.Sleep(1000); - var histogram = Recorder.GetIntervalHistogram(); - AccumulatingHistogram.Add(histogram); + var histogram = recorder.GetIntervalHistogram(); + accumulatingHistogram.Add(histogram); _logWriter.Append(histogram); - Console.WriteLine($"{DateTime.Now:o} Interval.TotalCount = {histogram.TotalCount,10:G}. Accumulated.TotalCount = {AccumulatingHistogram.TotalCount,10:G}."); + Console.WriteLine($"{DateTime.Now:o} Interval.TotalCount = {histogram.TotalCount,10:G}. Accumulated.TotalCount = {accumulatingHistogram.TotalCount,10:G}."); } _logWriter.Dispose(); _outputStream.Dispose(); + + Console.WriteLine("Log contents"); Console.WriteLine(File.ReadAllText(LogPath)); + Console.WriteLine(); + Console.WriteLine("Percentile distribution (values reported in milliseconds)"); + accumulatingHistogram.OutputPercentileDistribution(Console.Out, outputValueUnitScalingRatio: OutputScalingFactor.TimeStampToMilliseconds); + Console.WriteLine("Output thread finishing."); } /// /// Shows a sample loop where an action is executed, and the latency of each execution is recorded. /// - private static void RecordMeasurements() + private void RecordMeasurements(IRecorder recorder) { + var sut = new SocketTester("google.com"); + Action actionToMeasure = sut.CreateAndCloseDatagramSocket; var timer = Stopwatch.StartNew(); - Action actionToMeasure = CreateAndCloseDatagramSocket; do { - Recorder.Record(actionToMeasure); + recorder.Record(actionToMeasure); } while (timer.Elapsed < RunPeriod); Interlocked.Increment(ref _isCompleted); } - private static void CreateAndCloseDatagramSocket() - { - try - { - using (var socket = new Socket(AddressFamily.Value, SocketType.Stream, ProtocolType.Tcp)) - { - } - } - catch (SocketException) - { - } - } - - private static AddressFamily GetAddressFamily(string url) + public void Dispose() { - var hostIpAddress = Dns.GetHostEntry(url).AddressList[0]; - var hostIpEndPoint = new IPEndPoint(hostIpAddress, 80); - return hostIpEndPoint.AddressFamily; + _logWriter.Dispose(); + _outputStream.Dispose(); } } } \ No newline at end of file diff --git a/src/HdrHistogram.Examples/SocketTester.cs b/src/HdrHistogram.Examples/SocketTester.cs new file mode 100644 index 0000000..30e7340 --- /dev/null +++ b/src/HdrHistogram.Examples/SocketTester.cs @@ -0,0 +1,39 @@ +using System; +using System.Net; +using System.Net.Sockets; + +namespace HdrHistogram.Examples +{ + /// + /// A class used to test opening and closing a TCP socket. + /// + public class SocketTester + { + private readonly Lazy _addressFamily; + public SocketTester(string url) + { + _addressFamily = new Lazy(() => GetAddressFamily(url)); + } + + public void CreateAndCloseDatagramSocket() + { + try + { + using (var socket = new Socket(_addressFamily.Value, SocketType.Stream, ProtocolType.Tcp)) + { + } + } + catch (SocketException) + { + } + } + + + private static AddressFamily GetAddressFamily(string url) + { + var hostIpAddress = Dns.GetHostEntry(url).AddressList[0]; + var hostIpEndPoint = new IPEndPoint(hostIpAddress, 80); + return hostIpEndPoint.AddressFamily; + } + } +} \ No newline at end of file diff --git a/src/HdrHistogram.UnitTests/HdrHistogram.UnitTests.csproj b/src/HdrHistogram.UnitTests/HdrHistogram.UnitTests.csproj index c36941b..265442c 100644 --- a/src/HdrHistogram.UnitTests/HdrHistogram.UnitTests.csproj +++ b/src/HdrHistogram.UnitTests/HdrHistogram.UnitTests.csproj @@ -51,6 +51,7 @@ + @@ -76,7 +77,7 @@ - + diff --git a/src/HdrHistogram.UnitTests/HistogramFactoryTests.cs b/src/HdrHistogram.UnitTests/HistogramFactoryTests.cs new file mode 100644 index 0000000..5bdd461 --- /dev/null +++ b/src/HdrHistogram.UnitTests/HistogramFactoryTests.cs @@ -0,0 +1,222 @@ +using NUnit.Framework; + +namespace HdrHistogram.UnitTests +{ + [TestFixture] + public class HistogramFactoryTests + { + #region 16bit recording factory tests + + [Test] + public void CanCreateShortHistogram() + { + var actual = HistogramFactory.With16BitBucketSize() + .Create(); + Assert.IsInstanceOf(actual); + } + + [TestCase(1, 5000, 3)] + [TestCase(1000, 100000, 5)] + public void CanCreateShortHistogramWithSpecifiedRangeValues(long min, long max, int sf) + { + var actual = HistogramFactory.With16BitBucketSize() + .WithValuesFrom(min) + .WithValuesUpTo(max) + .WithPrecisionOf(sf) + .Create(); + Assert.IsInstanceOf(actual); + Assert.AreEqual(min, actual.LowestTrackableValue); + Assert.AreEqual(max, actual.HighestTrackableValue); + Assert.AreEqual(sf, actual.NumberOfSignificantValueDigits); + } + + [TestCase(1, 5000, 3)] + [TestCase(1000, 100000, 5)] + public void CanCreateShortHistogramRecorder(long min, long max, int sf) + { + var actual = HistogramFactory.With16BitBucketSize() + .WithValuesFrom(min) + .WithValuesUpTo(max) + .WithPrecisionOf(sf) + .WithThreadSafeReads() + .Create(); + var histogram = actual.GetIntervalHistogram(); + Assert.IsInstanceOf(histogram); + Assert.AreEqual(min, histogram.LowestTrackableValue); + Assert.AreEqual(max, histogram.HighestTrackableValue); + Assert.AreEqual(sf, histogram.NumberOfSignificantValueDigits); + } + + #endregion + + #region 32bit recording factory tests + + [Test] + public void CanCreateIntHistogram() + { + var actual = HistogramFactory.With32BitBucketSize() + .Create(); + Assert.IsInstanceOf(actual); + } + [Test] + public void CanCreateIntConcurrentHistogram() + { + var actual = HistogramFactory.With32BitBucketSize() + .WithThreadSafeWrites() + .Create(); + Assert.IsInstanceOf(actual); + } + + [TestCase(1, 5000, 3)] + [TestCase(1000, 100000, 5)] + public void CanCreateIntHistogramWithSpecifiedRangeValues(long min, long max, int sf) + { + var actual = HistogramFactory.With32BitBucketSize() + .WithValuesFrom(min) + .WithValuesUpTo(max) + .WithPrecisionOf(sf) + .Create(); + Assert.IsInstanceOf(actual); + Assert.AreEqual(min, actual.LowestTrackableValue); + Assert.AreEqual(max, actual.HighestTrackableValue); + Assert.AreEqual(sf, actual.NumberOfSignificantValueDigits); + } + [TestCase(1, 5000, 3)] + [TestCase(1000, 100000, 5)] + public void IntConcurrentHistogramWithSpecifiedRangeValues(long min, long max, int sf) + { + var actual = HistogramFactory.With32BitBucketSize() + .WithValuesFrom(min) + .WithValuesUpTo(max) + .WithPrecisionOf(sf) + .WithThreadSafeWrites() + .Create(); + Assert.IsInstanceOf(actual); + Assert.AreEqual(min, actual.LowestTrackableValue); + Assert.AreEqual(max, actual.HighestTrackableValue); + Assert.AreEqual(sf, actual.NumberOfSignificantValueDigits); + } + + [TestCase(1, 5000, 3)] + [TestCase(1000, 100000, 5)] + public void CanCreateIntHistogramRecorder(long min, long max, int sf) + { + var actual = HistogramFactory.With32BitBucketSize() + .WithValuesFrom(min) + .WithValuesUpTo(max) + .WithPrecisionOf(sf) + .WithThreadSafeReads() + .Create(); + var histogram = actual.GetIntervalHistogram(); + Assert.IsInstanceOf(histogram); + Assert.AreEqual(min, histogram.LowestTrackableValue); + Assert.AreEqual(max, histogram.HighestTrackableValue); + Assert.AreEqual(sf, histogram.NumberOfSignificantValueDigits); + } + + [TestCase(1, 5000, 3)] + [TestCase(1000, 100000, 5)] + public void CanCreateIntConcurrentHistogramRecorder(long min, long max, int sf) + { + var actual = HistogramFactory.With32BitBucketSize() + .WithValuesFrom(min) + .WithValuesUpTo(max) + .WithPrecisionOf(sf) + .WithThreadSafeWrites() + .WithThreadSafeReads() + .Create(); + var histogram = actual.GetIntervalHistogram(); + Assert.IsInstanceOf(histogram); + Assert.AreEqual(min, histogram.LowestTrackableValue); + Assert.AreEqual(max, histogram.HighestTrackableValue); + Assert.AreEqual(sf, histogram.NumberOfSignificantValueDigits); + } + + #endregion + + #region 64bit recording factory tests + + [Test] + public void CanCreateLongHistogram() + { + var actual = HistogramFactory.With64BitBucketSize() + .Create(); + Assert.IsInstanceOf(actual); + } + [Test] + public void CanCreateLongConcurrentHistogram() + { + var actual = HistogramFactory.With64BitBucketSize() + .WithThreadSafeWrites() + .Create(); + Assert.IsInstanceOf(actual); + } + + [TestCase(1, 5000, 3)] + [TestCase(1000, 100000, 5)] + public void CanCreateLongHistogramWithSpecifiedRangeValues(long min, long max, int sf) + { + var actual = HistogramFactory.With64BitBucketSize() + .WithValuesFrom(min) + .WithValuesUpTo(max) + .WithPrecisionOf(sf) + .Create(); + Assert.IsInstanceOf(actual); + Assert.AreEqual(min, actual.LowestTrackableValue); + Assert.AreEqual(max, actual.HighestTrackableValue); + Assert.AreEqual(sf, actual.NumberOfSignificantValueDigits); + } + [TestCase(1, 5000, 3)] + [TestCase(1000, 100000, 5)] + public void LongConcurrentHistogramWithSpecifiedRangeValues(long min, long max, int sf) + { + var actual = HistogramFactory.With64BitBucketSize() + .WithValuesFrom(min) + .WithValuesUpTo(max) + .WithPrecisionOf(sf) + .WithThreadSafeWrites() + .Create(); + Assert.IsInstanceOf(actual); + Assert.AreEqual(min, actual.LowestTrackableValue); + Assert.AreEqual(max, actual.HighestTrackableValue); + Assert.AreEqual(sf, actual.NumberOfSignificantValueDigits); + } + + [TestCase(1, 5000, 3)] + [TestCase(1000, 100000, 5)] + public void CanCreateLongHistogramRecorder(long min, long max, int sf) + { + var actual = HistogramFactory.With64BitBucketSize() + .WithValuesFrom(min) + .WithValuesUpTo(max) + .WithPrecisionOf(sf) + .WithThreadSafeReads() + .Create(); + var histogram = actual.GetIntervalHistogram(); + Assert.IsInstanceOf(histogram); + Assert.AreEqual(min, histogram.LowestTrackableValue); + Assert.AreEqual(max, histogram.HighestTrackableValue); + Assert.AreEqual(sf, histogram.NumberOfSignificantValueDigits); + } + + [TestCase(1, 5000, 3)] + [TestCase(1000, 100000, 5)] + public void CanCreateLongConcurrentHistogramRecorder(long min, long max, int sf) + { + var actual = HistogramFactory.With64BitBucketSize() + .WithValuesFrom(min) + .WithValuesUpTo(max) + .WithPrecisionOf(sf) + .WithThreadSafeWrites() + .WithThreadSafeReads() + .Create(); + var histogram = actual.GetIntervalHistogram(); + Assert.IsInstanceOf(histogram); + Assert.AreEqual(min, histogram.LowestTrackableValue); + Assert.AreEqual(max, histogram.HighestTrackableValue); + Assert.AreEqual(sf, histogram.NumberOfSignificantValueDigits); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/HdrHistogram.UnitTests/HistogramTestBase.cs b/src/HdrHistogram.UnitTests/HistogramTestBase.cs index ae0055f..b3f8d41 100644 --- a/src/HdrHistogram.UnitTests/HistogramTestBase.cs +++ b/src/HdrHistogram.UnitTests/HistogramTestBase.cs @@ -22,6 +22,10 @@ public abstract class HistogramTestBase { 8, (low,high,sf) => new LongHistogram(low, high, sf) } }; + protected abstract int WordSize { get; } + protected abstract HistogramBase Create(long highestTrackableValue, int numberOfSignificantValueDigits); + protected abstract HistogramBase Create(long lowestTrackableValue, long highestTrackableValue, int numberOfSignificantValueDigits); + [TestCase(0, 1, DefaultSignificantFigures, "lowestTrackableValue", "lowestTrackableValue must be >= 1")] [TestCase(DefautltLowestDiscernibleValue, 1, DefaultSignificantFigures, "highestTrackableValue", "highestTrackableValue must be >= 2 * lowestTrackableValue")] [TestCase(DefautltLowestDiscernibleValue, DefaultHighestTrackableValue, 6, "numberOfSignificantValueDigits", "numberOfSignificantValueDigits must be between 0 and 5")] @@ -40,26 +44,26 @@ public void ConstructorShouldRejectInvalidParameters( [TestCase(DefaultHighestTrackableValue, DefaultSignificantFigures)] public void TestConstructionArgumentGets(long highestTrackableValue, int numberOfSignificantValueDigits) { - var longHistogram = Create(highestTrackableValue, numberOfSignificantValueDigits); - Assert.AreEqual(1, longHistogram.LowestTrackableValue); - Assert.AreEqual(highestTrackableValue, longHistogram.HighestTrackableValue); - Assert.AreEqual(numberOfSignificantValueDigits, longHistogram.NumberOfSignificantValueDigits); + var histogram = Create(highestTrackableValue, numberOfSignificantValueDigits); + Assert.AreEqual(1, histogram.LowestTrackableValue); + Assert.AreEqual(highestTrackableValue, histogram.HighestTrackableValue); + Assert.AreEqual(numberOfSignificantValueDigits, histogram.NumberOfSignificantValueDigits); } [TestCase(1, 2, 2)] [TestCase(10, DefaultHighestTrackableValue, DefaultSignificantFigures)] public void TestConstructionArgumentGets(long lowestTrackableValue, long highestTrackableValue, int numberOfSignificantValueDigits) { - var longHistogram = Create(lowestTrackableValue, highestTrackableValue, numberOfSignificantValueDigits); - Assert.AreEqual(lowestTrackableValue, longHistogram.LowestTrackableValue); - Assert.AreEqual(highestTrackableValue, longHistogram.HighestTrackableValue); - Assert.AreEqual(numberOfSignificantValueDigits, longHistogram.NumberOfSignificantValueDigits); + var histogram = Create(lowestTrackableValue, highestTrackableValue, numberOfSignificantValueDigits); + Assert.AreEqual(lowestTrackableValue, histogram.LowestTrackableValue); + Assert.AreEqual(highestTrackableValue, histogram.HighestTrackableValue); + Assert.AreEqual(numberOfSignificantValueDigits, histogram.NumberOfSignificantValueDigits); } [Test] public void TestGetEstimatedFootprintInBytes2() { - var longHistogram = Create(DefaultHighestTrackableValue, DefaultSignificantFigures); + var histogram = Create(DefaultHighestTrackableValue, DefaultSignificantFigures); var largestValueWithSingleUnitResolution = 2 * (long)Math.Pow(10, DefaultSignificantFigures); var subBucketCountMagnitude = (int)Math.Ceiling(Math.Log(largestValueWithSingleUnitResolution) / Math.Log(2)); var subBucketSize = (int)Math.Pow(2, (subBucketCountMagnitude)); @@ -70,37 +74,37 @@ public void TestGetEstimatedFootprintInBytes2() var length = (bucketCount + 1) * (subBucketSize / 2); var expectedSize = header + (width * length); - Assert.AreEqual(expectedSize, longHistogram.GetEstimatedFootprintInBytes()); + Assert.AreEqual(expectedSize, histogram.GetEstimatedFootprintInBytes()); } [Test] public void RecordValue_increments_TotalCount() { - var longHistogram = Create(DefaultHighestTrackableValue, DefaultSignificantFigures); + var histogram = Create(DefaultHighestTrackableValue, DefaultSignificantFigures); for (int i = 1; i < 5; i++) { - longHistogram.RecordValue(i); - Assert.AreEqual(i, longHistogram.TotalCount); + histogram.RecordValue(i); + Assert.AreEqual(i, histogram.TotalCount); } } [Test] public void RecordValue_increments_CountAtValue() { - var longHistogram = Create(DefaultHighestTrackableValue, DefaultSignificantFigures); + var histogram = Create(DefaultHighestTrackableValue, DefaultSignificantFigures); for (int i = 1; i < 5; i++) { - longHistogram.RecordValue(TestValueLevel); - Assert.AreEqual(i, longHistogram.GetCountAtValue(TestValueLevel)); + histogram.RecordValue(TestValueLevel); + Assert.AreEqual(i, histogram.GetCountAtValue(TestValueLevel)); } } [Test] public void RecordValue_Overflow_ShouldThrowException() { - var longHistogram = Create(DefaultHighestTrackableValue, DefaultSignificantFigures); - Assert.Throws(() => longHistogram.RecordValue(DefaultHighestTrackableValue * 3)); + var histogram = Create(DefaultHighestTrackableValue, DefaultSignificantFigures); + Assert.Throws(() => histogram.RecordValue(DefaultHighestTrackableValue * 3)); } [TestCase(5)] @@ -161,22 +165,22 @@ public void RecordValueWithExpectedInterval() [Test] public void RecordAction_increments_TotalCount() { - var longHistogram = Create(DefaultHighestTrackableValue, DefaultSignificantFigures); + var histogram = Create(DefaultHighestTrackableValue, DefaultSignificantFigures); - longHistogram.Record(() => { }); - Assert.AreEqual(1, longHistogram.TotalCount); + histogram.Record(() => { }); + Assert.AreEqual(1, histogram.TotalCount); } [Test] public void RecordAction_records_in_correct_units() { var pause = TimeSpan.FromSeconds(1); - var longHistogram = Create(DefaultHighestTrackableValue, DefaultSignificantFigures); + var histogram = Create(DefaultHighestTrackableValue, DefaultSignificantFigures); - longHistogram.Record(() => Thread.Sleep(pause)); + histogram.Record(() => Thread.Sleep(pause)); var stringWriter = new StringWriter(); - longHistogram.OutputPercentileDistribution(stringWriter, + histogram.OutputPercentileDistribution(stringWriter, percentileTicksPerHalfDistance: 5, outputValueUnitScalingRatio: OutputScalingFactor.TimeStampToMilliseconds, useCsvFormat: true); @@ -192,47 +196,47 @@ public void RecordAction_records_in_correct_units() [Test] public void Reset_sets_counts_to_zero() { - var longHistogram = Create(DefaultHighestTrackableValue, DefaultSignificantFigures); - longHistogram.RecordValue(TestValueLevel); + var histogram = Create(DefaultHighestTrackableValue, DefaultSignificantFigures); + histogram.RecordValue(TestValueLevel); - longHistogram.Reset(); + histogram.Reset(); - Assert.AreEqual(0L, longHistogram.GetCountAtValue(TestValueLevel)); - Assert.AreEqual(0L, longHistogram.TotalCount); + Assert.AreEqual(0L, histogram.GetCountAtValue(TestValueLevel)); + Assert.AreEqual(0L, histogram.TotalCount); } [Test] public void Add_should_sum_the_counts_from_two_histograms() { - var longHistogram = Create(DefaultHighestTrackableValue, DefaultSignificantFigures); + var histogram = Create(DefaultHighestTrackableValue, DefaultSignificantFigures); var other = Create(DefaultHighestTrackableValue, DefaultSignificantFigures); - longHistogram.RecordValue(TestValueLevel); - longHistogram.RecordValue(TestValueLevel * 1000); + histogram.RecordValue(TestValueLevel); + histogram.RecordValue(TestValueLevel * 1000); other.RecordValue(TestValueLevel); other.RecordValue(TestValueLevel * 1000); - longHistogram.Add(other); + histogram.Add(other); - Assert.AreEqual(2L, longHistogram.GetCountAtValue(TestValueLevel)); - Assert.AreEqual(2L, longHistogram.GetCountAtValue(TestValueLevel * 1000)); - Assert.AreEqual(4L, longHistogram.TotalCount); + Assert.AreEqual(2L, histogram.GetCountAtValue(TestValueLevel)); + Assert.AreEqual(2L, histogram.GetCountAtValue(TestValueLevel * 1000)); + Assert.AreEqual(4L, histogram.TotalCount); } [Test] public void Add_should_allow_small_range_hsitograms_to_be_added() { - var longHistogram = Create(DefaultHighestTrackableValue, DefaultSignificantFigures); + var histogram = Create(DefaultHighestTrackableValue, DefaultSignificantFigures); - longHistogram.RecordValue(TestValueLevel); - longHistogram.RecordValue(TestValueLevel * 1000); + histogram.RecordValue(TestValueLevel); + histogram.RecordValue(TestValueLevel * 1000); var biggerOther = Create(DefaultHighestTrackableValue * 2, DefaultSignificantFigures); biggerOther.RecordValue(TestValueLevel); biggerOther.RecordValue(TestValueLevel * 1000); // Adding the smaller histogram to the bigger one should work: - biggerOther.Add(longHistogram); + biggerOther.Add(histogram); Assert.AreEqual(2L, biggerOther.GetCountAtValue(TestValueLevel)); Assert.AreEqual(2L, biggerOther.GetCountAtValue(TestValueLevel * 1000)); Assert.AreEqual(4L, biggerOther.TotalCount); @@ -241,10 +245,10 @@ public void Add_should_allow_small_range_hsitograms_to_be_added() [Test] public void Add_throws_if_other_has_a_larger_range() { - var longHistogram = Create(DefaultHighestTrackableValue, DefaultSignificantFigures); + var histogram = Create(DefaultHighestTrackableValue, DefaultSignificantFigures); var biggerOther = Create(DefaultHighestTrackableValue * 2, DefaultSignificantFigures); - Assert.Throws(() => { longHistogram.Add(biggerOther); }); + Assert.Throws(() => { histogram.Add(biggerOther); }); } [TestCase(1, 1)] @@ -254,8 +258,8 @@ public void Add_throws_if_other_has_a_larger_range() [TestCase(8, 10000)] public void SizeOfEquivalentValueRangeForValue(int expected, int value) { - var longHistogram = Create(DefaultHighestTrackableValue, DefaultSignificantFigures); - Assert.AreEqual(expected, longHistogram.SizeOfEquivalentValueRange(value)); + var histogram = Create(DefaultHighestTrackableValue, DefaultSignificantFigures); + Assert.AreEqual(expected, histogram.SizeOfEquivalentValueRange(value)); //Validate the scaling too. var scaledHistogram = Create(1024, DefaultHighestTrackableValue, DefaultSignificantFigures); @@ -266,8 +270,8 @@ public void SizeOfEquivalentValueRangeForValue(int expected, int value) [TestCase(10008, 10009)] public void LowestEquivalentValue_returns_the_smallest_value_that_would_be_assigned_to_the_same_count(int expected, int value) { - var longHistogram = Create(DefaultHighestTrackableValue, DefaultSignificantFigures); - Assert.AreEqual(expected, longHistogram.LowestEquivalentValue(value)); + var histogram = Create(DefaultHighestTrackableValue, DefaultSignificantFigures); + Assert.AreEqual(expected, histogram.LowestEquivalentValue(value)); //Validate the scaling too var scaledHistogram = Create(1024, DefaultHighestTrackableValue, DefaultSignificantFigures); Assert.AreEqual(expected * 1024, scaledHistogram.LowestEquivalentValue(value * 1024)); @@ -281,8 +285,8 @@ public void LowestEquivalentValue_returns_the_smallest_value_that_would_be_assig [TestCase(10015, 10008)] public void HighestEquivalentValue_returns_the_smallest_value_that_would_be_assigned_to_the_same_count(int expected, int value) { - var longHistogram = Create(DefaultHighestTrackableValue, DefaultSignificantFigures); - Assert.AreEqual(expected, longHistogram.HighestEquivalentValue(value)); + var histogram = Create(DefaultHighestTrackableValue, DefaultSignificantFigures); + Assert.AreEqual(expected, histogram.HighestEquivalentValue(value)); //Validate the scaling too var scaledHistogram = Create(1024, DefaultHighestTrackableValue, DefaultSignificantFigures); Assert.AreEqual(expected * 1024 + 1023, scaledHistogram.HighestEquivalentValue(value * 1024)); @@ -295,8 +299,8 @@ public void HighestEquivalentValue_returns_the_smallest_value_that_would_be_assi [TestCase(10004, 10007, 0)] public void TestMedianEquivalentValue(int expected, int value, int scaledHeader) { - var longHistogram = Create(DefaultHighestTrackableValue, DefaultSignificantFigures); - Assert.AreEqual(expected, longHistogram.MedianEquivalentValue(value)); + var histogram = Create(DefaultHighestTrackableValue, DefaultSignificantFigures); + Assert.AreEqual(expected, histogram.MedianEquivalentValue(value)); //Validate the scaling too var scaledHistogram = Create(1024, DefaultHighestTrackableValue, DefaultSignificantFigures); Assert.AreEqual(expected * 1024 + scaledHeader, scaledHistogram.MedianEquivalentValue(value * 1024)); @@ -355,9 +359,6 @@ private static int GetBucketsNeededToCoverValue(int subBucketSize, long value) } return bucketsNeeded; } - protected abstract int WordSize { get; } - protected abstract HistogramBase Create(long highestTrackableValue, int numberOfSignificantValueDigits); - protected abstract HistogramBase Create(long lowestTrackableValue, long highestTrackableValue, int numberOfSignificantValueDigits); private static string GetCellValue(string csvData, int col, int row) { diff --git a/src/HdrHistogram.UnitTests/IntConcurrentHistogramTests.cs b/src/HdrHistogram.UnitTests/IntConcurrentHistogramTests.cs index 10c7874..e06fd40 100644 --- a/src/HdrHistogram.UnitTests/IntConcurrentHistogramTests.cs +++ b/src/HdrHistogram.UnitTests/IntConcurrentHistogramTests.cs @@ -9,12 +9,23 @@ public class IntConcurrentHistogramTests : ConcurrentHistogramTestBase protected override HistogramBase Create(long highestTrackableValue, int numberOfSignificantValueDigits) { - return new IntConcurrentHistogram(1, highestTrackableValue, numberOfSignificantValueDigits); + //return new IntConcurrentHistogram(1, highestTrackableValue, numberOfSignificantValueDigits); + return HistogramFactory.With32BitBucketSize() + .WithValuesUpTo(highestTrackableValue) + .WithPrecisionOf(numberOfSignificantValueDigits) + .WithThreadSafeWrites() + .Create(); } protected override HistogramBase Create(long lowestTrackableValue, long highestTrackableValue, int numberOfSignificantValueDigits) { - return new IntConcurrentHistogram(lowestTrackableValue, highestTrackableValue, numberOfSignificantValueDigits); + //return new IntConcurrentHistogram(lowestTrackableValue, highestTrackableValue, numberOfSignificantValueDigits); + return HistogramFactory.With32BitBucketSize() + .WithValuesFrom(lowestTrackableValue) + .WithValuesUpTo(highestTrackableValue) + .WithPrecisionOf(numberOfSignificantValueDigits) + .WithThreadSafeWrites() + .Create(); } } } \ No newline at end of file diff --git a/src/HdrHistogram.UnitTests/IntHistogramEncodingTests.cs b/src/HdrHistogram.UnitTests/IntHistogramEncodingTests.cs index bb1181f..9df7cac 100644 --- a/src/HdrHistogram.UnitTests/IntHistogramEncodingTests.cs +++ b/src/HdrHistogram.UnitTests/IntHistogramEncodingTests.cs @@ -7,7 +7,11 @@ public sealed class IntHistogramEncodingTests : HistogramEncodingTestBase { protected override HistogramBase Create(long highestTrackableValue, int numberOfSignificantDigits) { - return new IntHistogram(highestTrackableValue, numberOfSignificantDigits); + //return new IntHistogram(highestTrackableValue, numberOfSignificantDigits); + return HistogramFactory.With32BitBucketSize() + .WithValuesUpTo(highestTrackableValue) + .WithPrecisionOf(numberOfSignificantDigits) + .Create(); } } } \ No newline at end of file diff --git a/src/HdrHistogram.UnitTests/IntHistogramTests.cs b/src/HdrHistogram.UnitTests/IntHistogramTests.cs index 1cc07eb..5ebce48 100644 --- a/src/HdrHistogram.UnitTests/IntHistogramTests.cs +++ b/src/HdrHistogram.UnitTests/IntHistogramTests.cs @@ -9,12 +9,21 @@ public class IntHistogramTests : HistogramTestBase protected override HistogramBase Create(long highestTrackableValue, int numberOfSignificantValueDigits) { - return new IntHistogram(highestTrackableValue, numberOfSignificantValueDigits); + //return new IntHistogram(highestTrackableValue, numberOfSignificantValueDigits); + return HistogramFactory.With32BitBucketSize() + .WithValuesUpTo(highestTrackableValue) + .WithPrecisionOf(numberOfSignificantValueDigits) + .Create(); } protected override HistogramBase Create(long lowestTrackableValue, long highestTrackableValue, int numberOfSignificantValueDigits) { - return new IntHistogram(lowestTrackableValue, highestTrackableValue, numberOfSignificantValueDigits); + //return new IntHistogram(lowestTrackableValue, highestTrackableValue, numberOfSignificantValueDigits); + return HistogramFactory.With32BitBucketSize() + .WithValuesFrom(lowestTrackableValue) + .WithValuesUpTo(highestTrackableValue) + .WithPrecisionOf(numberOfSignificantValueDigits) + .Create(); } } } \ No newline at end of file diff --git a/src/HdrHistogram.UnitTests/LongConcurrentHistogramTests.cs b/src/HdrHistogram.UnitTests/LongConcurrentHistogramTests.cs index 13544c4..d43cbf4 100644 --- a/src/HdrHistogram.UnitTests/LongConcurrentHistogramTests.cs +++ b/src/HdrHistogram.UnitTests/LongConcurrentHistogramTests.cs @@ -9,12 +9,23 @@ public class LongConcurrentHistogramTests : ConcurrentHistogramTestBase protected override HistogramBase Create(long highestTrackableValue, int numberOfSignificantValueDigits) { - return new LongConcurrentHistogram(1, highestTrackableValue, numberOfSignificantValueDigits); + //return new LongConcurrentHistogram(1, highestTrackableValue, numberOfSignificantValueDigits); + return HistogramFactory.With64BitBucketSize() + .WithValuesUpTo(highestTrackableValue) + .WithPrecisionOf(numberOfSignificantValueDigits) + .WithThreadSafeWrites() + .Create(); } protected override HistogramBase Create(long lowestTrackableValue, long highestTrackableValue, int numberOfSignificantValueDigits) { - return new LongConcurrentHistogram(lowestTrackableValue, highestTrackableValue, numberOfSignificantValueDigits); + //return new LongConcurrentHistogram(lowestTrackableValue, highestTrackableValue, numberOfSignificantValueDigits); + return HistogramFactory.With64BitBucketSize() + .WithValuesFrom(lowestTrackableValue) + .WithValuesUpTo(highestTrackableValue) + .WithPrecisionOf(numberOfSignificantValueDigits) + .WithThreadSafeWrites() + .Create(); } } } \ No newline at end of file diff --git a/src/HdrHistogram.UnitTests/LongHistogramEncodingTests.cs b/src/HdrHistogram.UnitTests/LongHistogramEncodingTests.cs index ab5a420..fd801cd 100644 --- a/src/HdrHistogram.UnitTests/LongHistogramEncodingTests.cs +++ b/src/HdrHistogram.UnitTests/LongHistogramEncodingTests.cs @@ -7,7 +7,11 @@ public sealed class LongHistogramEncodingTests : HistogramEncodingTestBase { protected override HistogramBase Create(long highestTrackableValue, int numberOfSignificantDigits) { - return new LongHistogram(highestTrackableValue, numberOfSignificantDigits); + //return new LongHistogram(highestTrackableValue, numberOfSignificantDigits); + return HistogramFactory.With64BitBucketSize() + .WithValuesUpTo(highestTrackableValue) + .WithPrecisionOf(numberOfSignificantDigits) + .Create(); } } } diff --git a/src/HdrHistogram.UnitTests/LongHistogramTests.cs b/src/HdrHistogram.UnitTests/LongHistogramTests.cs index 0db6517..bdd5937 100644 --- a/src/HdrHistogram.UnitTests/LongHistogramTests.cs +++ b/src/HdrHistogram.UnitTests/LongHistogramTests.cs @@ -8,11 +8,20 @@ public class LongHistogramTests : HistogramTestBase protected override int WordSize => sizeof(long); protected override HistogramBase Create(long highestTrackableValue, int numberOfSignificantValueDigits) { - return new LongHistogram(highestTrackableValue, numberOfSignificantValueDigits); + //return new LongHistogram(highestTrackableValue, numberOfSignificantValueDigits); + return HistogramFactory.With64BitBucketSize() + .WithValuesUpTo(highestTrackableValue) + .WithPrecisionOf(numberOfSignificantValueDigits) + .Create(); } protected override HistogramBase Create(long lowestTrackableValue, long highestTrackableValue, int numberOfSignificantValueDigits) { - return new LongHistogram(lowestTrackableValue, highestTrackableValue, numberOfSignificantValueDigits); + //return new LongHistogram(lowestTrackableValue, highestTrackableValue, numberOfSignificantValueDigits); + return HistogramFactory.With64BitBucketSize() + .WithValuesFrom(lowestTrackableValue) + .WithValuesUpTo(highestTrackableValue) + .WithPrecisionOf(numberOfSignificantValueDigits) + .Create(); } } } diff --git a/src/HdrHistogram.UnitTests/Recording/RecorderTestWithIntConcurrentHistogram.cs b/src/HdrHistogram.UnitTests/Recording/RecorderTestWithIntConcurrentHistogram.cs index 9906b58..c4e12ad 100644 --- a/src/HdrHistogram.UnitTests/Recording/RecorderTestWithIntConcurrentHistogram.cs +++ b/src/HdrHistogram.UnitTests/Recording/RecorderTestWithIntConcurrentHistogram.cs @@ -5,9 +5,26 @@ namespace HdrHistogram.UnitTests.Recording [TestFixture] public sealed class RecorderTestWithIntConcurrentHistogram : RecorderTestsBase { - protected override HistogramBase Create(long id, long min, long max, int sf) + protected override HistogramBase CreateHistogram(long id, long min, long max, int sf) { - return new IntConcurrentHistogram(id, min, max, sf); + //return new IntConcurrentHistogram(id, min, max, sf); + return HistogramFactory.With32BitBucketSize() + .WithValuesFrom(min) + .WithValuesUpTo(max) + .WithPrecisionOf(sf) + .WithThreadSafeWrites() + .Create(); + } + + protected override Recorder Create(long min, long max, int sf) + { + return HistogramFactory.With32BitBucketSize() + .WithValuesFrom(min) + .WithValuesUpTo(max) + .WithPrecisionOf(sf) + .WithThreadSafeWrites() + .WithThreadSafeReads() + .Create(); } } } \ No newline at end of file diff --git a/src/HdrHistogram.UnitTests/Recording/RecorderTestWithIntHistogram.cs b/src/HdrHistogram.UnitTests/Recording/RecorderTestWithIntHistogram.cs index bf76b3a..8971a0f 100644 --- a/src/HdrHistogram.UnitTests/Recording/RecorderTestWithIntHistogram.cs +++ b/src/HdrHistogram.UnitTests/Recording/RecorderTestWithIntHistogram.cs @@ -5,9 +5,24 @@ namespace HdrHistogram.UnitTests.Recording [TestFixture] public sealed class RecorderTestWithIntHistogram : RecorderTestsBase { - protected override HistogramBase Create(long id, long min, long max, int sf) + protected override HistogramBase CreateHistogram(long id, long min, long max, int sf) { - return new IntHistogram(id, min, max, sf); + //return new IntHistogram(id, min, max, sf); + return HistogramFactory.With32BitBucketSize() + .WithValuesFrom(min) + .WithValuesUpTo(max) + .WithPrecisionOf(sf) + .Create(); + } + + protected override Recorder Create(long min, long max, int sf) + { + return HistogramFactory.With32BitBucketSize() + .WithValuesFrom(min) + .WithValuesUpTo(max) + .WithPrecisionOf(sf) + .WithThreadSafeReads() + .Create(); } } } \ No newline at end of file diff --git a/src/HdrHistogram.UnitTests/Recording/RecorderTestWithLShortHistogram.cs b/src/HdrHistogram.UnitTests/Recording/RecorderTestWithLShortHistogram.cs deleted file mode 100644 index 56e2964..0000000 --- a/src/HdrHistogram.UnitTests/Recording/RecorderTestWithLShortHistogram.cs +++ /dev/null @@ -1,13 +0,0 @@ -using NUnit.Framework; - -namespace HdrHistogram.UnitTests.Recording -{ - [TestFixture] - public sealed class RecorderTestWithLShortHistogram : RecorderTestsBase - { - protected override HistogramBase Create(long id, long min, long max, int sf) - { - return new ShortHistogram(id, min, max, sf); - } - } -} \ No newline at end of file diff --git a/src/HdrHistogram.UnitTests/Recording/RecorderTestWithLongConcurrentHistogram.cs b/src/HdrHistogram.UnitTests/Recording/RecorderTestWithLongConcurrentHistogram.cs index b0e5b89..4b23a28 100644 --- a/src/HdrHistogram.UnitTests/Recording/RecorderTestWithLongConcurrentHistogram.cs +++ b/src/HdrHistogram.UnitTests/Recording/RecorderTestWithLongConcurrentHistogram.cs @@ -5,9 +5,25 @@ namespace HdrHistogram.UnitTests.Recording [TestFixture] public sealed class RecorderTestWithLongConcurrentHistogram : RecorderTestsBase { - protected override HistogramBase Create(long id, long min, long max, int sf) + protected override HistogramBase CreateHistogram(long id, long min, long max, int sf) { - return new LongConcurrentHistogram(id, min, max, sf); + return HistogramFactory.With64BitBucketSize() + .WithValuesFrom(min) + .WithValuesUpTo(max) + .WithPrecisionOf(sf) + .WithThreadSafeWrites() + .Create(); + } + + protected override Recorder Create(long min, long max, int sf) + { + return HistogramFactory.With64BitBucketSize() + .WithValuesFrom(min) + .WithValuesUpTo(max) + .WithPrecisionOf(sf) + .WithThreadSafeWrites() + .WithThreadSafeReads() + .Create(); } } } \ No newline at end of file diff --git a/src/HdrHistogram.UnitTests/Recording/RecorderTestWithLongHistogram.cs b/src/HdrHistogram.UnitTests/Recording/RecorderTestWithLongHistogram.cs index 3928fdb..57a26e4 100644 --- a/src/HdrHistogram.UnitTests/Recording/RecorderTestWithLongHistogram.cs +++ b/src/HdrHistogram.UnitTests/Recording/RecorderTestWithLongHistogram.cs @@ -5,9 +5,24 @@ namespace HdrHistogram.UnitTests.Recording [TestFixture] public sealed class RecorderTestWithLongHistogram : RecorderTestsBase { - protected override HistogramBase Create(long id, long min, long max, int sf) + protected override HistogramBase CreateHistogram(long id, long min, long max, int sf) { - return new LongHistogram(id, min, max, sf); + //return new LongHistogram(id, min, max, sf); + return HistogramFactory.With64BitBucketSize() + .WithValuesFrom(min) + .WithValuesUpTo(max) + .WithPrecisionOf(sf) + .Create(); + } + + protected override Recorder Create(long min, long max, int sf) + { + return HistogramFactory.With64BitBucketSize() + .WithValuesFrom(min) + .WithValuesUpTo(max) + .WithPrecisionOf(sf) + .WithThreadSafeReads() + .Create(); } } } \ No newline at end of file diff --git a/src/HdrHistogram.UnitTests/Recording/RecorderTestWithShortHistogram.cs b/src/HdrHistogram.UnitTests/Recording/RecorderTestWithShortHistogram.cs new file mode 100644 index 0000000..ae83a28 --- /dev/null +++ b/src/HdrHistogram.UnitTests/Recording/RecorderTestWithShortHistogram.cs @@ -0,0 +1,28 @@ +using NUnit.Framework; + +namespace HdrHistogram.UnitTests.Recording +{ + [TestFixture] + public sealed class RecorderTestWithShortHistogram : RecorderTestsBase + { + protected override HistogramBase CreateHistogram(long id, long min, long max, int sf) + { + //return new ShortHistogram(id, min, max, sf); + return HistogramFactory.With16BitBucketSize() + .WithValuesFrom(min) + .WithValuesUpTo(max) + .WithPrecisionOf(sf) + .Create(); + } + + protected override Recorder Create(long min, long max, int sf) + { + return HistogramFactory.With16BitBucketSize() + .WithValuesFrom(min) + .WithValuesUpTo(max) + .WithPrecisionOf(sf) + .WithThreadSafeReads() + .Create(); + } + } +} \ No newline at end of file diff --git a/src/HdrHistogram.UnitTests/Recording/RecorderTestsBase.cs b/src/HdrHistogram.UnitTests/Recording/RecorderTestsBase.cs index 660f56c..17e6c97 100644 --- a/src/HdrHistogram.UnitTests/Recording/RecorderTestsBase.cs +++ b/src/HdrHistogram.UnitTests/Recording/RecorderTestsBase.cs @@ -10,7 +10,8 @@ public abstract class RecorderTestsBase private const long DefaultHighestTrackableValue = 7716549600;//TimeStamp.Hours(1); // e.g. for 1 hr in system clock ticks (StopWatch.Frequency) private const int DefaultSignificantFigures = 3; - protected abstract HistogramBase Create(long id, long min, long max, int sf); + protected abstract HistogramBase CreateHistogram(long id, long min, long max, int sf); + protected abstract Recorder Create(long min, long max, int sf); [TestCase(0, 1, DefaultSignificantFigures, "lowestTrackableValue", "lowestTrackableValue must be >= 1")] [TestCase(1, 1, DefaultSignificantFigures, "highestTrackableValue", "highestTrackableValue must be >= 2 * lowestTrackableValue")] @@ -21,11 +22,9 @@ public void ConstructorShouldRejectInvalidParameters( string errorParamName, string errorMessage) { var ex = Assert.Throws(() => - new Recorder( - lowestTrackableValue, + Create(lowestTrackableValue, highestTrackableValue, - numberOfSignificantValueDigits, - Create)); + numberOfSignificantValueDigits)); Assert.AreEqual(errorParamName, ex.ParamName); StringAssert.StartsWith(errorMessage, ex.Message); } @@ -33,7 +32,7 @@ public void ConstructorShouldRejectInvalidParameters( [Test] public void GetIntervalHistogram_returns_alternating_instances_from_factory() { - var recorder = new Recorder(DefautltLowestDiscernibleValue, DefaultHighestTrackableValue, DefaultSignificantFigures, Create); + var recorder = Create(DefautltLowestDiscernibleValue, DefaultHighestTrackableValue, DefaultSignificantFigures); var a = recorder.GetIntervalHistogram(); var b = recorder.GetIntervalHistogram(a); var c = recorder.GetIntervalHistogram(b); @@ -48,7 +47,7 @@ public void GetIntervalHistogram_returns_alternating_instances_from_factory() [Test] public void GetIntervalHistogram_returns_current_histogram_values() { - var recorder = new Recorder(DefautltLowestDiscernibleValue, DefaultHighestTrackableValue, DefaultSignificantFigures, Create); + var recorder = Create(DefautltLowestDiscernibleValue, DefaultHighestTrackableValue, DefaultSignificantFigures); recorder.RecordValue(1); recorder.RecordValue(10); recorder.RecordValue(100); @@ -61,7 +60,7 @@ public void GetIntervalHistogram_returns_current_histogram_values() [Test] public void GetIntervalHistogram_causes_recording_to_happen_on_new_histogram() { - var recorder = new Recorder(DefautltLowestDiscernibleValue, DefaultHighestTrackableValue, DefaultSignificantFigures, Create); + var recorder = Create(DefautltLowestDiscernibleValue, DefaultHighestTrackableValue, DefaultSignificantFigures); recorder.RecordValue(1); var histogramPrimary = recorder.GetIntervalHistogram(); Assert.AreEqual(1, histogramPrimary.GetCountAtValue(1)); @@ -78,7 +77,7 @@ public void GetIntervalHistogram_causes_recording_to_happen_on_new_histogram() [Test] public void GetIntervalHistogram_resets_recycled_histogram() { - var recorder = new Recorder(DefautltLowestDiscernibleValue, DefaultHighestTrackableValue, DefaultSignificantFigures, Create); + var recorder = Create(DefautltLowestDiscernibleValue, DefaultHighestTrackableValue, DefaultSignificantFigures); recorder.RecordValue(1); recorder.RecordValue(10); recorder.RecordValue(100); @@ -100,7 +99,7 @@ public void GetIntervalHistogram_resets_recycled_histogram() [Test] public void RecordValue_increments_TotalCount() { - var recorder = new Recorder(DefautltLowestDiscernibleValue, DefaultHighestTrackableValue, DefaultSignificantFigures, Create); + var recorder = Create(DefautltLowestDiscernibleValue, DefaultHighestTrackableValue, DefaultSignificantFigures); recorder.RecordValue(1000); var histogram = recorder.GetIntervalHistogram(); Assert.AreEqual(1, histogram.TotalCount); @@ -109,7 +108,7 @@ public void RecordValue_increments_TotalCount() [Test] public void RecordValue_increments_CountAtValue() { - var recorder = new Recorder(DefautltLowestDiscernibleValue, DefaultHighestTrackableValue, DefaultSignificantFigures, Create); + var recorder = Create(DefautltLowestDiscernibleValue, DefaultHighestTrackableValue, DefaultSignificantFigures); recorder.RecordValue(1000); recorder.RecordValue(1000); recorder.RecordValue(1000); @@ -121,14 +120,14 @@ public void RecordValue_increments_CountAtValue() public void RecordValue_Overflow_ShouldThrowException() { var highestTrackableValue = DefaultHighestTrackableValue; - var recorder = new Recorder(DefautltLowestDiscernibleValue, highestTrackableValue, DefaultSignificantFigures, Create); + var recorder = Create(DefautltLowestDiscernibleValue, highestTrackableValue, DefaultSignificantFigures); Assert.Throws(() => recorder.RecordValue(highestTrackableValue * 3)); } [Test] public void RecordValueWithCount_increments_TotalCount() { - var recorder = new Recorder(DefautltLowestDiscernibleValue, DefaultHighestTrackableValue, DefaultSignificantFigures, Create); + var recorder = Create(DefautltLowestDiscernibleValue, DefaultHighestTrackableValue, DefaultSignificantFigures); recorder.RecordValueWithCount(1000, 10); var histogram = recorder.GetIntervalHistogram(); Assert.AreEqual(10, histogram.TotalCount); @@ -137,7 +136,7 @@ public void RecordValueWithCount_increments_TotalCount() [Test] public void RecordValueWithCount_increments_CountAtValue() { - var recorder = new Recorder(DefautltLowestDiscernibleValue, DefaultHighestTrackableValue, DefaultSignificantFigures, Create); + var recorder = Create(DefautltLowestDiscernibleValue, DefaultHighestTrackableValue, DefaultSignificantFigures); recorder.RecordValueWithCount(1000, 10); recorder.RecordValueWithCount(1000, 10); recorder.RecordValueWithCount(5000, 20); @@ -150,7 +149,7 @@ public void RecordValueWithCount_increments_CountAtValue() public void RecordValueWithCount_Overflow_ShouldThrowException() { var highestTrackableValue = DefaultHighestTrackableValue; - var recorder = new Recorder(DefautltLowestDiscernibleValue, highestTrackableValue, DefaultSignificantFigures, Create); + var recorder = Create(DefautltLowestDiscernibleValue, highestTrackableValue, DefaultSignificantFigures); Assert.Throws(() => recorder.RecordValueWithCount(highestTrackableValue * 3, 100)); } @@ -158,7 +157,7 @@ public void RecordValueWithCount_Overflow_ShouldThrowException() public void RecordValueWithExpectedInterval() { var TestValueLevel = 4L; - var recorder = new Recorder(DefautltLowestDiscernibleValue, DefaultHighestTrackableValue, DefaultSignificantFigures, Create); + var recorder = Create(DefautltLowestDiscernibleValue, DefaultHighestTrackableValue, DefaultSignificantFigures); var valueHistogram = new LongHistogram(DefaultHighestTrackableValue, DefaultSignificantFigures); recorder.RecordValueWithExpectedInterval(TestValueLevel, TestValueLevel / 4); @@ -182,7 +181,7 @@ public void RecordValueWithExpectedInterval() [Test] public void RecordAction_increments_TotalCount() { - var recorder = new Recorder(DefautltLowestDiscernibleValue, DefaultHighestTrackableValue, DefaultSignificantFigures, Create); + var recorder = Create(DefautltLowestDiscernibleValue, DefaultHighestTrackableValue, DefaultSignificantFigures); recorder.Record(() => { }); @@ -193,7 +192,7 @@ public void RecordAction_increments_TotalCount() [Test] public void Reset_clears_counts_for_instances() { - var recorder = new Recorder(DefautltLowestDiscernibleValue, DefaultHighestTrackableValue, DefaultSignificantFigures, Create); + var recorder = Create(DefautltLowestDiscernibleValue, DefaultHighestTrackableValue, DefaultSignificantFigures); recorder.RecordValue(1); recorder.RecordValue(10); recorder.RecordValue(100); @@ -229,7 +228,7 @@ public void GetIntervalHistogramInto_copies_data_over_provided_Histogram() Assert.AreEqual(1, targetHistogram.GetCountAtValue(10)); Assert.AreEqual(1, targetHistogram.GetCountAtValue(100)); - var recorder = new Recorder(DefautltLowestDiscernibleValue, DefaultHighestTrackableValue, DefaultSignificantFigures, Create); + var recorder = Create(DefautltLowestDiscernibleValue, DefaultHighestTrackableValue, DefaultSignificantFigures); recorder.RecordValue(1000); recorder.RecordValue(10000); recorder.RecordValue(100000); @@ -251,8 +250,8 @@ public void GetIntervalHistogramInto_copies_data_over_provided_Histogram() public void Using_external_histogram_for_recycling_throws() { const int id = -1000; - var externallyCreatedHistogram = Create(id, DefautltLowestDiscernibleValue, DefaultHighestTrackableValue, DefaultSignificantFigures); - var recorder = new Recorder(1, DefaultHighestTrackableValue, DefaultSignificantFigures, Create); + var externallyCreatedHistogram = CreateHistogram(id, DefautltLowestDiscernibleValue, DefaultHighestTrackableValue, DefaultSignificantFigures); + var recorder = Create(1, DefaultHighestTrackableValue, DefaultSignificantFigures); recorder.RecordValue(1000); Assert.Throws(() => recorder.GetIntervalHistogram(externallyCreatedHistogram)); diff --git a/src/HdrHistogram.UnitTests/ShortHistogramEncodingTests.cs b/src/HdrHistogram.UnitTests/ShortHistogramEncodingTests.cs index e0b067d..d7f5c4a 100644 --- a/src/HdrHistogram.UnitTests/ShortHistogramEncodingTests.cs +++ b/src/HdrHistogram.UnitTests/ShortHistogramEncodingTests.cs @@ -7,7 +7,11 @@ public sealed class ShortHistogramEncodingTests : HistogramEncodingTestBase { protected override HistogramBase Create(long highestTrackableValue, int numberOfSignificantDigits) { - return new ShortHistogram(highestTrackableValue, numberOfSignificantDigits); + //return new ShortHistogram(highestTrackableValue, numberOfSignificantDigits); + return HistogramFactory.With16BitBucketSize() + .WithValuesUpTo(highestTrackableValue) + .WithPrecisionOf(numberOfSignificantDigits) + .Create(); } protected override void LoadFullRange(IRecorder source) @@ -19,4 +23,4 @@ protected override void LoadFullRange(IRecorder source) source.RecordValue(DefaultHighestTrackableValue); } } -} \ No newline at end of file +} \ No newline at end of file diff --git a/src/HdrHistogram.UnitTests/ShortHistogramTests.cs b/src/HdrHistogram.UnitTests/ShortHistogramTests.cs index 9f75eb5..b1e63c4 100644 --- a/src/HdrHistogram.UnitTests/ShortHistogramTests.cs +++ b/src/HdrHistogram.UnitTests/ShortHistogramTests.cs @@ -9,12 +9,21 @@ public class ShortHistogramTests : HistogramTestBase protected override HistogramBase Create(long highestTrackableValue, int numberOfSignificantValueDigits) { - return new ShortHistogram(highestTrackableValue, numberOfSignificantValueDigits); + //return new ShortHistogram(highestTrackableValue, numberOfSignificantValueDigits); + return HistogramFactory.With16BitBucketSize() + .WithValuesUpTo(highestTrackableValue) + .WithPrecisionOf(numberOfSignificantValueDigits) + .Create(); } protected override HistogramBase Create(long lowestTrackableValue, long highestTrackableValue, int numberOfSignificantValueDigits) { - return new ShortHistogram(lowestTrackableValue, highestTrackableValue, numberOfSignificantValueDigits); + //return new ShortHistogram(lowestTrackableValue, highestTrackableValue, numberOfSignificantValueDigits); + return HistogramFactory.With16BitBucketSize() + .WithValuesFrom(lowestTrackableValue) + .WithValuesUpTo(highestTrackableValue) + .WithPrecisionOf(numberOfSignificantValueDigits) + .Create(); } } } \ No newline at end of file diff --git a/src/HdrHistogram/HdrHistogram.csproj b/src/HdrHistogram/HdrHistogram.csproj index ce9b333..f795f42 100644 --- a/src/HdrHistogram/HdrHistogram.csproj +++ b/src/HdrHistogram/HdrHistogram.csproj @@ -46,6 +46,8 @@ + + diff --git a/src/HdrHistogram/Histogram.cs b/src/HdrHistogram/Histogram.cs new file mode 100644 index 0000000..47cc538 --- /dev/null +++ b/src/HdrHistogram/Histogram.cs @@ -0,0 +1,332 @@ +using System; + +namespace HdrHistogram +{ + /// + /// Provides factory methods to define the features of your histogram. + /// + public abstract class HistogramFactory + { + /// + /// Private constructor to force usage via the Static starter methods. + /// + private HistogramFactory() + { + } + + /// + /// The lowest value that can be tracked (distinguished from 0) by the histogram. + /// Must be a positive integer that is >= 1. + /// May be internally rounded down to nearest power of 2. + /// + protected long LowestTrackableValue { get; set; } = 1; + + /// + /// The highest value to be tracked by the histogram. Must be a positive integer that is >= (2 * ). + /// + protected long HighestTrackableValue { get; set; } = TimeStamp.Minutes(10); + + /// + /// The number of significant decimal digits to which the histogram will maintain value resolution and separation. + /// Must be a non-negative integer between 0 and 5. + /// + protected int NumberOfSignificantValueDigits { get; set; } = 3; + + + + /// + /// Specifies that the Histogram to be created should be thread safe when written to from multiple threads. + /// + /// Returns a that is set to return a threadsafe writer. + public abstract HistogramFactory WithThreadSafeWrites(); + + /// + /// Specifies that the consumer will need to be able to read Histogram values in a thread safe manner. + /// This will mean will be used to wrap the Histogram, allowing thread safe reads. + /// + /// Returns a which can create recorders. Recorders allow for threadsafe reads. + public abstract RecorderFactory WithThreadSafeReads(); + + /// + /// A factory-method to create the Histogram. + /// + /// + /// The lowest value that can be tracked (distinguished from 0) by the histogram. + /// Must be a positive integer that is >= 1. + /// May be internally rounded down to nearest power of 2. + /// + /// The highest value to be tracked by the histogram. Must be a positive integer that is >= (2 * ). + /// The number of significant decimal digits to which the histogram will maintain value resolution and separation. + /// Must be a non-negative integer between 0 and 5. + /// + /// Returns a newly created instance defined by the settings of the current instance of . + public abstract HistogramBase Create( + long lowestDiscernibleValue, + long highestTrackableValue, + int numberOfSignificantValueDigits); + + /// + /// A factory-method to create the Histogram. + /// + /// An identifier for this instance. + /// + /// The lowest value that can be tracked (distinguished from 0) by the histogram. + /// Must be a positive integer that is >= 1. + /// May be internally rounded down to nearest power of 2. + /// + /// The highest value to be tracked by the histogram. Must be a positive integer that is >= (2 * ). + /// The number of significant decimal digits to which the histogram will maintain value resolution and separation. + /// Must be a non-negative integer between 0 and 5. + /// + /// Returns a newly created instance defined by the settings of the current instance of . + public abstract HistogramBase Create( + long instanceId, + long lowestDiscernibleValue, + long highestTrackableValue, + int numberOfSignificantValueDigits); + + /// + /// Specifies the lowest value the Histogram should be configured to record. + /// + /// + /// The lowest value that can be tracked (distinguished from 0) by the histogram. + /// Must be a positive integer that is >= 1. + /// May be internally rounded down to nearest power of 2. + /// + /// The configured with the specified minimum allowed value. + public HistogramFactory WithValuesFrom(long lowestDiscernibleValue) + { + LowestTrackableValue = lowestDiscernibleValue; + return this; + } + + /// + /// Specifies the highest value the Histogram should be configured to record. + /// + /// + /// The highest value to be tracked by the histogram. Must be a positive integer that is >= (2 * ). + /// + /// The configured with the specified maximum allowed value. + public HistogramFactory WithValuesUpTo(long highestTrackableValue) + { + HighestTrackableValue = highestTrackableValue; + return this; + } + + /// + /// Specifies the number of significant figures that the Histogram should record. + /// + /// + /// The number of significant decimal digits to which the histogram will maintain value resolution and separation. + /// Must be a non-negative integer between 0 and 5. + /// + /// The configured with the specified maximum allowed value. + public HistogramFactory WithPrecisionOf(int numberOfSignificantValueDigits) + { + NumberOfSignificantValueDigits = numberOfSignificantValueDigits; + return this; + } + + /// + /// Creates the histogram as configured by this factory instance. + /// + /// A newly created instance of . + public HistogramBase Create() + { + return Create(LowestTrackableValue, HighestTrackableValue, NumberOfSignificantValueDigits); + } + + + + + /// + /// Specify that the Histogram should be able to record count values in the 64bit range. + /// + /// The configured for 64bit bucket sizes. + public static HistogramFactory With64BitBucketSize() + { + return new LongHistogramFactory(); + } + + /// + /// Specify that the Histogram should be able to record count values in the 32bit range. + /// + /// The configured for 64bit bucket sizes. + public static HistogramFactory With32BitBucketSize() + { + return new IntHistogramFactory(); + } + + /// + /// Specify that the Histogram should be able to record count values in the 32bit range. + /// + /// The configured for 64bit bucket sizes. + public static HistogramFactory With16BitBucketSize() + { + return new ShortHistogramFactory(); + } + + + + private sealed class LongHistogramFactory : HistogramFactory + { + public override HistogramBase Create(long lowestDiscernibleValue, long highestTrackableValue, int numberOfSignificantValueDigits) + { + return new LongHistogram(lowestDiscernibleValue, highestTrackableValue, numberOfSignificantValueDigits); + } + + public override HistogramBase Create(long instanceId, long lowestDiscernibleValue, long highestTrackableValue, + int numberOfSignificantValueDigits) + { + return new LongHistogram(instanceId, lowestDiscernibleValue, highestTrackableValue, numberOfSignificantValueDigits); + } + + public override HistogramFactory WithThreadSafeWrites() + { + return new LongConcurrentHistogramFactory(this); + } + + public override RecorderFactory WithThreadSafeReads() + { + return new RecorderFactory(this); + } + } + + private sealed class IntHistogramFactory : HistogramFactory + { + public override HistogramBase Create(long lowestDiscernibleValue, long highestTrackableValue, int numberOfSignificantValueDigits) + { + return new IntHistogram(lowestDiscernibleValue, highestTrackableValue, numberOfSignificantValueDigits); + } + + public override HistogramBase Create(long instanceId, long lowestDiscernibleValue, long highestTrackableValue, + int numberOfSignificantValueDigits) + { + return new IntHistogram(instanceId, lowestDiscernibleValue, highestTrackableValue, numberOfSignificantValueDigits); + } + + public override HistogramFactory WithThreadSafeWrites() + { + return new IntConcurrentHistogramFactory(this); + } + + public override RecorderFactory WithThreadSafeReads() + { + return new RecorderFactory(this); + } + } + + private sealed class ShortHistogramFactory : HistogramFactory + { + public override HistogramBase Create(long lowestDiscernibleValue, long highestTrackableValue, int numberOfSignificantValueDigits) + { + return new ShortHistogram(lowestDiscernibleValue, highestTrackableValue, numberOfSignificantValueDigits); + } + + public override HistogramBase Create(long instanceId, long lowestDiscernibleValue, long highestTrackableValue, + int numberOfSignificantValueDigits) + { + return new ShortHistogram(instanceId, lowestDiscernibleValue, highestTrackableValue, numberOfSignificantValueDigits); + } + + public override HistogramFactory WithThreadSafeWrites() + { + throw new NotSupportedException("Short(16bit) Histograms do not support thread safe writes."); + } + + public override RecorderFactory WithThreadSafeReads() + { + return new RecorderFactory(this); + } + } + + private sealed class LongConcurrentHistogramFactory : HistogramFactory + { + public LongConcurrentHistogramFactory(HistogramFactory histogramFactory) + { + LowestTrackableValue = histogramFactory.LowestTrackableValue; + HighestTrackableValue = histogramFactory.HighestTrackableValue; + NumberOfSignificantValueDigits = histogramFactory.NumberOfSignificantValueDigits; + } + + public override HistogramFactory WithThreadSafeWrites() + { + return this; + } + + public override RecorderFactory WithThreadSafeReads() + { + return new RecorderFactory(this); + } + + public override HistogramBase Create(long lowestDiscernibleValue, long highestTrackableValue, int numberOfSignificantValueDigits) + { + return new LongConcurrentHistogram(lowestDiscernibleValue, highestTrackableValue, numberOfSignificantValueDigits); + } + + public override HistogramBase Create(long instanceId, long lowestDiscernibleValue, long highestTrackableValue, + int numberOfSignificantValueDigits) + { + return new LongConcurrentHistogram(instanceId, lowestDiscernibleValue, highestTrackableValue, numberOfSignificantValueDigits); + } + } + + private sealed class IntConcurrentHistogramFactory : HistogramFactory + { + public IntConcurrentHistogramFactory(HistogramFactory histogramFactory) + { + LowestTrackableValue = histogramFactory.LowestTrackableValue; + HighestTrackableValue = histogramFactory.HighestTrackableValue; + NumberOfSignificantValueDigits = histogramFactory.NumberOfSignificantValueDigits; + } + + public override HistogramFactory WithThreadSafeWrites() + { + return this; + } + + public override RecorderFactory WithThreadSafeReads() + { + return new RecorderFactory(this); + } + + public override HistogramBase Create(long lowestDiscernibleValue, long highestTrackableValue, int numberOfSignificantValueDigits) + { + return new IntConcurrentHistogram(lowestDiscernibleValue, highestTrackableValue, numberOfSignificantValueDigits); + } + + public override HistogramBase Create(long instanceId, long lowestDiscernibleValue, long highestTrackableValue, + int numberOfSignificantValueDigits) + { + return new IntConcurrentHistogram(instanceId, lowestDiscernibleValue, highestTrackableValue, numberOfSignificantValueDigits); + } + } + + + /// + /// Factory for creating Recorders for thread safe reading of histograms. + /// + public sealed class RecorderFactory + { + private readonly HistogramFactory _histogramBuilder; + + internal RecorderFactory(HistogramFactory histogramBuilder) + { + _histogramBuilder = histogramBuilder; + } + + /// + /// Creates the recorder as configured by this factory instance. + /// + /// A newly created instance of . + public Recorder Create() + { + return new Recorder( + _histogramBuilder.LowestTrackableValue, + _histogramBuilder.HighestTrackableValue, + _histogramBuilder.NumberOfSignificantValueDigits, + _histogramBuilder.Create); + } + } + } +} \ No newline at end of file diff --git a/src/HdrHistogram/HistogramFactoryDelegate.cs b/src/HdrHistogram/HistogramFactoryDelegate.cs new file mode 100644 index 0000000..a026d16 --- /dev/null +++ b/src/HdrHistogram/HistogramFactoryDelegate.cs @@ -0,0 +1,20 @@ +namespace HdrHistogram +{ + /// + /// The method definition for a histogram factory. + /// + /// The instance id the histogram should be created with. + /// The lowest value that can be tracked (distinguished from 0) by the histogram. + /// Must be a positive integer that is >= 1. + /// May be internally rounded down to nearest power of 2. + /// + /// The highest value to be tracked by the histogram. + /// Must be a positive integer that is >= (2 * lowestTrackableValue). + /// + /// + /// The number of significant decimal digits to which the histogram will maintain value resolution and separation. + /// Must be a non-negative integer between 0 and 5. + /// + public delegate HistogramBase HistogramFactoryDelegate( + long instanceId, long lowestDiscernibleValue, long highestTrackableValue, int numberOfSignificantValueDigits); +} \ No newline at end of file diff --git a/src/HdrHistogram/HistogramLogWriter.cs b/src/HdrHistogram/HistogramLogWriter.cs index 59726d2..815f00a 100644 --- a/src/HdrHistogram/HistogramLogWriter.cs +++ b/src/HdrHistogram/HistogramLogWriter.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Threading; using HdrHistogram.Utilities; namespace HdrHistogram @@ -14,6 +15,7 @@ public sealed class HistogramLogWriter : IDisposable private readonly TextWriter _log; private bool _hasHeaderWritten = false; + private int _isDisposed = 0; /// /// Writes the provided histograms to the underlying with a given overall start time. @@ -127,7 +129,10 @@ private void WriteHistogram(HistogramBase histogram) /// public void Dispose() { - using (_log) { } + if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0) + { + using (_log) { } + } } } } \ No newline at end of file diff --git a/src/HdrHistogram/Recorder.cs b/src/HdrHistogram/Recorder.cs index eb12224..14ce366 100644 --- a/src/HdrHistogram/Recorder.cs +++ b/src/HdrHistogram/Recorder.cs @@ -12,24 +12,6 @@ namespace HdrHistogram { - /// - /// The method definition for a histogram factory. - /// - /// The instance id the histogram should be created with. - /// The lowest value that can be tracked (distinguished from 0) by the histogram. - /// Must be a positive integer that is >= 1. - /// May be internally rounded down to nearest power of 2. - /// - /// The highest value to be tracked by the histogram. - /// Must be a positive integer that is >= (2 * lowestTrackableValue). - /// - /// - /// The number of significant decimal digits to which the histogram will maintain value resolution and separation. - /// Must be a non-negative integer between 0 and 5. - /// - public delegate HistogramBase HistogramFactory( - long instanceId, long lowestDiscernibleValue, long highestTrackableValue, int numberOfSignificantValueDigits); - /// /// Records integer values, and provides stable interval samples from live recorded data without interrupting or stalling active recording of values. /// Each interval histogram provided contains all value counts accumulated since the previous interval histogram was taken. @@ -45,7 +27,7 @@ public class Recorder : IRecorder private readonly object _gate = new object(); private readonly long _instanceId = Interlocked.Increment(ref _instanceIdSequencer); private readonly WriterReaderPhaser _recordingPhaser = new WriterReaderPhaser(); - private readonly HistogramFactory _histogramFactory; + private readonly HistogramFactoryDelegate _histogramFactory; private HistogramBase _activeHistogram; private HistogramBase _inactiveHistogram; @@ -69,7 +51,7 @@ public Recorder( long lowestDiscernibleValue, long highestTrackableValue, int numberOfSignificantValueDigits, - HistogramFactory histogramFactory) + HistogramFactoryDelegate histogramFactory) { _histogramFactory = histogramFactory; _activeHistogram = histogramFactory(_instanceId, lowestDiscernibleValue, highestTrackableValue, numberOfSignificantValueDigits); @@ -253,6 +235,5 @@ private void ValidateFitAsReplacementHistogram(HistogramBase replacementHistogra $"Replacement histogram must have been obtained via a previous getIntervalHistogram() call from this {GetType().Name} instance"); } } - } } \ No newline at end of file