From 5c1c7399692c583d7eaeca4faad3cbab20f265ae Mon Sep 17 00:00:00 2001 From: tomer doron Date: Fri, 5 Apr 2019 10:30:46 -0700 Subject: [PATCH] make protocol functions data type specific motivation: it's very beneficial for the protocols to use functions with concrete types because then there's no need for specialisation. At compile time, the compiler can often not know what concrete type will be used so it needs to create a 'generic at runtime' version of the function. If however all functions on the protocol do not use generics, there's no need for a 'generic at runtime' version of the function changes: * change CounterHandler::increment to take Int64 instead of * change RecorderHandler::record to take Int64 and Double instead of and * adjust example and test implementation * adjust docs --- README.md | 37 +++++++++++--------- Sources/CoreMetrics/Metrics.swift | 24 ++++++------- Sources/Examples/ExampleMetricsLibrary.swift | 27 +++++++------- Sources/Examples/SimpleMetricsLibrary.swift | 27 +++++++------- Tests/MetricsTests/TestMetrics.swift | 9 ++--- 5 files changed, 61 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index fd1ce39..e5f9478 100644 --- a/README.md +++ b/README.md @@ -104,8 +104,8 @@ public protocol TimerHandler: AnyObject { ```swift public protocol RecorderHandler: AnyObject { - func record(_ value: DataType) - func record(_ value: DataType) + func record(_ value: Int64) + func record(_ value: Double) } ``` @@ -120,7 +120,7 @@ class SimpleMetricsLibrary: MetricsFactory { } func makeRecorder(label: String, dimensions: [(String, String)], aggregate: Bool) -> RecorderHandler { - let maker:(String, [(String, String)]) -> Recorder = aggregate ? ExampleRecorder.init : ExampleGauge.init + let maker: (String, [(String, String)]) -> RecorderHandler = aggregate ? ExampleRecorder.init : ExampleGauge.init return maker(label, dimensions) } @@ -133,9 +133,15 @@ class SimpleMetricsLibrary: MetricsFactory { let lock = NSLock() var value: Int64 = 0 - func increment(_ value: DataType) { + func increment(_ value: Int64) { self.lock.withLock { - self.value += Int64(value) + self.value += value + } + } + + func reset() { + self.lock.withLock { + self.value = 0 } } } @@ -145,20 +151,18 @@ class SimpleMetricsLibrary: MetricsFactory { private let lock = NSLock() var values = [(Int64, Double)]() - func record(_ value: DataType) { + func record(_ value: Int64) { self.record(Double(value)) } - func record(_ value: DataType) { - // this may loose precision, but good enough as an example - let v = Double(value) + func record(_ value: Double) { // TODO: sliding window lock.withLock { - values.append((Date().nanoSince1970, v)) + values.append((Date().nanoSince1970, value)) self._count += 1 - self._sum += v - if 0 == self._min || v < self._min { self._min = v } - if 0 == self._max || v > self._max { self._max = v } + self._sum += value + self._min = Swift.min(self._min, value) + self._max = Swift.max(self._max, value) } } @@ -188,13 +192,12 @@ class SimpleMetricsLibrary: MetricsFactory { let lock = NSLock() var _value: Double = 0 - func record(_ value: DataType) { + func record(_ value: Int64) { self.record(Double(value)) } - func record(_ value: DataType) { - // this may loose precision but good enough as an example - self.lock.withLock { _value = Double(value) } + func record(_ value: Double) { + self.lock.withLock { _value = value } } } diff --git a/Sources/CoreMetrics/Metrics.swift b/Sources/CoreMetrics/Metrics.swift index 2074685..ae0a9f6 100644 --- a/Sources/CoreMetrics/Metrics.swift +++ b/Sources/CoreMetrics/Metrics.swift @@ -14,7 +14,7 @@ /// This is the Counter protocol a metrics library implements. It must have reference semantics public protocol CounterHandler: AnyObject { - func increment(_ value: DataType) + func increment(_ value: Int64) func reset() } @@ -35,7 +35,7 @@ public class Counter { @inlinable public func increment(_ value: DataType) { - self.handler.increment(value) + self.handler.increment(Int64(value)) } @inlinable @@ -58,8 +58,8 @@ public extension Counter { /// This is the Recorder protocol a metrics library implements. It must have reference semantics public protocol RecorderHandler: AnyObject { - func record(_ value: DataType) - func record(_ value: DataType) + func record(_ value: Int64) + func record(_ value: Double) } // This is the user facing Recorder API. Its behavior depends on the `RecorderHandler` implementation @@ -81,12 +81,12 @@ public class Recorder { @inlinable public func record(_ value: DataType) { - self.handler.record(value) + self.handler.record(Int64(value)) } @inlinable public func record(_ value: DataType) { - self.handler.record(value) + self.handler.record(Double(value)) } } @@ -225,7 +225,7 @@ public final class MultiplexMetricsHandler: MetricsFactory { self.counters = factories.map { $0.makeCounter(label: label, dimensions: dimensions) } } - func increment(_ value: DataType) { + func increment(_ value: Int64) { self.counters.forEach { $0.increment(value) } } @@ -240,11 +240,11 @@ public final class MultiplexMetricsHandler: MetricsFactory { self.recorders = factories.map { $0.makeRecorder(label: label, dimensions: dimensions, aggregate: aggregate) } } - func record(_ value: DataType) { + func record(_ value: Int64) { self.recorders.forEach { $0.record(value) } } - func record(_ value: DataType) { + func record(_ value: Double) { self.recorders.forEach { $0.record(value) } } } @@ -278,9 +278,9 @@ public final class NOOPMetricsHandler: MetricsFactory, CounterHandler, RecorderH return self } - public func increment(_: DataType) {} + public func increment(_: Int64) {} public func reset() {} - public func record(_: DataType) {} - public func record(_: DataType) {} + public func record(_: Int64) {} + public func record(_: Double) {} public func recordNanoseconds(_: Int64) {} } diff --git a/Sources/Examples/ExampleMetricsLibrary.swift b/Sources/Examples/ExampleMetricsLibrary.swift index 66bb62d..421fb10 100644 --- a/Sources/Examples/ExampleMetricsLibrary.swift +++ b/Sources/Examples/ExampleMetricsLibrary.swift @@ -138,27 +138,25 @@ class ExampleRecorder: RecorderHandler, CustomStringConvertible { private let lock = NSLock() var values = [(Int64, Double)]() - func record(_ value: DataType) { + func record(_ value: Int64) { self.record(Double(value)) } - func record(_ value: DataType) { - // this may loose precision, but good enough as an example - let v = Double(value) + func record(_ value: Double) { // TODO: sliding window - lock.withLock { - values.append((Date().nanoSince1970, v)) + self.lock.withLock { + values.append((Date().nanoSince1970, value)) } - options.forEach { option in + self.options.forEach { option in switch option { case .count: self.count += 1 case .sum: - self.sum += v + self.sum += value case .min: - if 0 == self.min || v < self.min { self.min = v } + self.min = Swift.min(self.min, value) case .max: - if 0 == self.max || v > self.max { self.max = v } + self.max = Swift.max(self.max, value) case .quantiles(let items): self.computeQuantiles(items) } @@ -224,7 +222,7 @@ class ExampleRecorder: RecorderHandler, CustomStringConvertible { self.lock.withLock { self._quantiels.removeAll() items.forEach { item in - if let result = Sigma.quantiles.method1(self.values.map { Double($0.1) }, probability: Double(item)) { + if let result = Sigma.quantiles.method1(self.values.map { $0.1 }, probability: Double(item)) { self._quantiels[item] = result } } @@ -242,13 +240,12 @@ class ExampleGauge: RecorderHandler, CustomStringConvertible { let lock = NSLock() var _value: Double = 0 - func record(_ value: DataType) { + func record(_ value: Int64) { self.record(Double(value)) } - func record(_ value: DataType) { - // this may loose precision but good enough as an example - self.lock.withLock { _value = Double(value) } + func record(_ value: Double) { + self.lock.withLock { _value = value } } var description: String { diff --git a/Sources/Examples/SimpleMetricsLibrary.swift b/Sources/Examples/SimpleMetricsLibrary.swift index 0242773..1724e89 100644 --- a/Sources/Examples/SimpleMetricsLibrary.swift +++ b/Sources/Examples/SimpleMetricsLibrary.swift @@ -38,9 +38,9 @@ class SimpleMetricsLibrary: MetricsFactory { let lock = NSLock() var value: Int64 = 0 - func increment(_ value: DataType) { + func increment(_ value: Int64) { self.lock.withLock { - self.value += Int64(value) + self.value += value } } @@ -56,20 +56,18 @@ class SimpleMetricsLibrary: MetricsFactory { private let lock = NSLock() var values = [(Int64, Double)]() - func record(_ value: DataType) { + func record(_ value: Int64) { self.record(Double(value)) } - func record(_ value: DataType) { - // this may loose precision, but good enough as an example - let v = Double(value) + func record(_ value: Double) { // TODO: sliding window - lock.withLock { - values.append((Date().nanoSince1970, v)) + self.lock.withLock { + values.append((Date().nanoSince1970, value)) self._count += 1 - self._sum += v - self._min = Swift.min(self._min, v) - self._max = Swift.max(self._max, v) + self._sum += value + self._min = Swift.min(self._min, value) + self._max = Swift.max(self._max, value) } } @@ -99,13 +97,12 @@ class SimpleMetricsLibrary: MetricsFactory { let lock = NSLock() var _value: Double = 0 - func record(_ value: DataType) { + func record(_ value: Int64) { self.record(Double(value)) } - func record(_ value: DataType) { - // this may loose precision but good enough as an example - self.lock.withLock { _value = Double(value) } + func record(_ value: Double) { + self.lock.withLock { _value = value } } } diff --git a/Tests/MetricsTests/TestMetrics.swift b/Tests/MetricsTests/TestMetrics.swift index 8fabebd..b0c36db 100644 --- a/Tests/MetricsTests/TestMetrics.swift +++ b/Tests/MetricsTests/TestMetrics.swift @@ -60,9 +60,9 @@ internal class TestCounter: CounterHandler, Equatable { self.dimensions = dimensions } - func increment(_ value: DataType) { + func increment(_ value: Int64) { self.lock.withLock { - self.values.append((Date(), Int64(value))) + self.values.append((Date(), value)) } print("adding \(value) to \(self.label)") } @@ -71,6 +71,7 @@ internal class TestCounter: CounterHandler, Equatable { self.lock.withLock { self.values = [] } + print("reseting \(self.label)") } public static func == (lhs: TestCounter, rhs: TestCounter) -> Bool { @@ -94,11 +95,11 @@ internal class TestRecorder: RecorderHandler, Equatable { self.aggregate = aggregate } - func record(_ value: DataType) { + func record(_ value: Int64) { self.record(Double(value)) } - func record(_ value: DataType) { + func record(_ value: Double) { self.lock.withLock { // this may loose precision but good enough as an example values.append((Date(), Double(value)))