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 <DataType: BinaryInteger>
* change RecorderHandler::record to take Int64 and Double instead of <DataType: BinaryInteger> and <DataType: BinaryFloatingPoint>
* adjust example and test implementation
* adjust docs
This commit is contained in:
tomer doron 2019-04-05 10:30:46 -07:00 committed by GitHub
parent 187653d466
commit 5c1c739969
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 61 additions and 63 deletions

View File

@ -104,8 +104,8 @@ public protocol TimerHandler: AnyObject {
```swift ```swift
public protocol RecorderHandler: AnyObject { public protocol RecorderHandler: AnyObject {
func record<DataType: BinaryInteger>(_ value: DataType) func record(_ value: Int64)
func record<DataType: BinaryFloatingPoint>(_ value: DataType) func record(_ value: Double)
} }
``` ```
@ -120,7 +120,7 @@ class SimpleMetricsLibrary: MetricsFactory {
} }
func makeRecorder(label: String, dimensions: [(String, String)], aggregate: Bool) -> RecorderHandler { 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) return maker(label, dimensions)
} }
@ -133,9 +133,15 @@ class SimpleMetricsLibrary: MetricsFactory {
let lock = NSLock() let lock = NSLock()
var value: Int64 = 0 var value: Int64 = 0
func increment<DataType: BinaryInteger>(_ value: DataType) { func increment(_ value: Int64) {
self.lock.withLock { 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() private let lock = NSLock()
var values = [(Int64, Double)]() var values = [(Int64, Double)]()
func record<DataType: BinaryInteger>(_ value: DataType) { func record(_ value: Int64) {
self.record(Double(value)) self.record(Double(value))
} }
func record<DataType: BinaryFloatingPoint>(_ value: DataType) { func record(_ value: Double) {
// this may loose precision, but good enough as an example
let v = Double(value)
// TODO: sliding window // TODO: sliding window
lock.withLock { lock.withLock {
values.append((Date().nanoSince1970, v)) values.append((Date().nanoSince1970, value))
self._count += 1 self._count += 1
self._sum += v self._sum += value
if 0 == self._min || v < self._min { self._min = v } self._min = Swift.min(self._min, value)
if 0 == self._max || v > self._max { self._max = v } self._max = Swift.max(self._max, value)
} }
} }
@ -188,13 +192,12 @@ class SimpleMetricsLibrary: MetricsFactory {
let lock = NSLock() let lock = NSLock()
var _value: Double = 0 var _value: Double = 0
func record<DataType: BinaryInteger>(_ value: DataType) { func record(_ value: Int64) {
self.record(Double(value)) self.record(Double(value))
} }
func record<DataType: BinaryFloatingPoint>(_ value: DataType) { func record(_ value: Double) {
// this may loose precision but good enough as an example self.lock.withLock { _value = value }
self.lock.withLock { _value = Double(value) }
} }
} }

View File

@ -14,7 +14,7 @@
/// This is the Counter protocol a metrics library implements. It must have reference semantics /// This is the Counter protocol a metrics library implements. It must have reference semantics
public protocol CounterHandler: AnyObject { public protocol CounterHandler: AnyObject {
func increment<DataType: BinaryInteger>(_ value: DataType) func increment(_ value: Int64)
func reset() func reset()
} }
@ -35,7 +35,7 @@ public class Counter {
@inlinable @inlinable
public func increment<DataType: BinaryInteger>(_ value: DataType) { public func increment<DataType: BinaryInteger>(_ value: DataType) {
self.handler.increment(value) self.handler.increment(Int64(value))
} }
@inlinable @inlinable
@ -58,8 +58,8 @@ public extension Counter {
/// This is the Recorder protocol a metrics library implements. It must have reference semantics /// This is the Recorder protocol a metrics library implements. It must have reference semantics
public protocol RecorderHandler: AnyObject { public protocol RecorderHandler: AnyObject {
func record<DataType: BinaryInteger>(_ value: DataType) func record(_ value: Int64)
func record<DataType: BinaryFloatingPoint>(_ value: DataType) func record(_ value: Double)
} }
// This is the user facing Recorder API. Its behavior depends on the `RecorderHandler` implementation // This is the user facing Recorder API. Its behavior depends on the `RecorderHandler` implementation
@ -81,12 +81,12 @@ public class Recorder {
@inlinable @inlinable
public func record<DataType: BinaryInteger>(_ value: DataType) { public func record<DataType: BinaryInteger>(_ value: DataType) {
self.handler.record(value) self.handler.record(Int64(value))
} }
@inlinable @inlinable
public func record<DataType: BinaryFloatingPoint>(_ value: DataType) { public func record<DataType: BinaryFloatingPoint>(_ 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) } self.counters = factories.map { $0.makeCounter(label: label, dimensions: dimensions) }
} }
func increment<DataType: BinaryInteger>(_ value: DataType) { func increment(_ value: Int64) {
self.counters.forEach { $0.increment(value) } 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) } self.recorders = factories.map { $0.makeRecorder(label: label, dimensions: dimensions, aggregate: aggregate) }
} }
func record<DataType: BinaryInteger>(_ value: DataType) { func record(_ value: Int64) {
self.recorders.forEach { $0.record(value) } self.recorders.forEach { $0.record(value) }
} }
func record<DataType: BinaryFloatingPoint>(_ value: DataType) { func record(_ value: Double) {
self.recorders.forEach { $0.record(value) } self.recorders.forEach { $0.record(value) }
} }
} }
@ -278,9 +278,9 @@ public final class NOOPMetricsHandler: MetricsFactory, CounterHandler, RecorderH
return self return self
} }
public func increment<DataType: BinaryInteger>(_: DataType) {} public func increment(_: Int64) {}
public func reset() {} public func reset() {}
public func record<DataType: BinaryInteger>(_: DataType) {} public func record(_: Int64) {}
public func record<DataType: BinaryFloatingPoint>(_: DataType) {} public func record(_: Double) {}
public func recordNanoseconds(_: Int64) {} public func recordNanoseconds(_: Int64) {}
} }

View File

@ -138,27 +138,25 @@ class ExampleRecorder: RecorderHandler, CustomStringConvertible {
private let lock = NSLock() private let lock = NSLock()
var values = [(Int64, Double)]() var values = [(Int64, Double)]()
func record<DataType: BinaryInteger>(_ value: DataType) { func record(_ value: Int64) {
self.record(Double(value)) self.record(Double(value))
} }
func record<DataType: BinaryFloatingPoint>(_ value: DataType) { func record(_ value: Double) {
// this may loose precision, but good enough as an example
let v = Double(value)
// TODO: sliding window // TODO: sliding window
lock.withLock { self.lock.withLock {
values.append((Date().nanoSince1970, v)) values.append((Date().nanoSince1970, value))
} }
options.forEach { option in self.options.forEach { option in
switch option { switch option {
case .count: case .count:
self.count += 1 self.count += 1
case .sum: case .sum:
self.sum += v self.sum += value
case .min: case .min:
if 0 == self.min || v < self.min { self.min = v } self.min = Swift.min(self.min, value)
case .max: case .max:
if 0 == self.max || v > self.max { self.max = v } self.max = Swift.max(self.max, value)
case .quantiles(let items): case .quantiles(let items):
self.computeQuantiles(items) self.computeQuantiles(items)
} }
@ -224,7 +222,7 @@ class ExampleRecorder: RecorderHandler, CustomStringConvertible {
self.lock.withLock { self.lock.withLock {
self._quantiels.removeAll() self._quantiels.removeAll()
items.forEach { item in 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 self._quantiels[item] = result
} }
} }
@ -242,13 +240,12 @@ class ExampleGauge: RecorderHandler, CustomStringConvertible {
let lock = NSLock() let lock = NSLock()
var _value: Double = 0 var _value: Double = 0
func record<DataType: BinaryInteger>(_ value: DataType) { func record(_ value: Int64) {
self.record(Double(value)) self.record(Double(value))
} }
func record<DataType: BinaryFloatingPoint>(_ value: DataType) { func record(_ value: Double) {
// this may loose precision but good enough as an example self.lock.withLock { _value = value }
self.lock.withLock { _value = Double(value) }
} }
var description: String { var description: String {

View File

@ -38,9 +38,9 @@ class SimpleMetricsLibrary: MetricsFactory {
let lock = NSLock() let lock = NSLock()
var value: Int64 = 0 var value: Int64 = 0
func increment<DataType: BinaryInteger>(_ value: DataType) { func increment(_ value: Int64) {
self.lock.withLock { self.lock.withLock {
self.value += Int64(value) self.value += value
} }
} }
@ -56,20 +56,18 @@ class SimpleMetricsLibrary: MetricsFactory {
private let lock = NSLock() private let lock = NSLock()
var values = [(Int64, Double)]() var values = [(Int64, Double)]()
func record<DataType: BinaryInteger>(_ value: DataType) { func record(_ value: Int64) {
self.record(Double(value)) self.record(Double(value))
} }
func record<DataType: BinaryFloatingPoint>(_ value: DataType) { func record(_ value: Double) {
// this may loose precision, but good enough as an example
let v = Double(value)
// TODO: sliding window // TODO: sliding window
lock.withLock { self.lock.withLock {
values.append((Date().nanoSince1970, v)) values.append((Date().nanoSince1970, value))
self._count += 1 self._count += 1
self._sum += v self._sum += value
self._min = Swift.min(self._min, v) self._min = Swift.min(self._min, value)
self._max = Swift.max(self._max, v) self._max = Swift.max(self._max, value)
} }
} }
@ -99,13 +97,12 @@ class SimpleMetricsLibrary: MetricsFactory {
let lock = NSLock() let lock = NSLock()
var _value: Double = 0 var _value: Double = 0
func record<DataType: BinaryInteger>(_ value: DataType) { func record(_ value: Int64) {
self.record(Double(value)) self.record(Double(value))
} }
func record<DataType: BinaryFloatingPoint>(_ value: DataType) { func record(_ value: Double) {
// this may loose precision but good enough as an example self.lock.withLock { _value = value }
self.lock.withLock { _value = Double(value) }
} }
} }

View File

@ -60,9 +60,9 @@ internal class TestCounter: CounterHandler, Equatable {
self.dimensions = dimensions self.dimensions = dimensions
} }
func increment<DataType: BinaryInteger>(_ value: DataType) { func increment(_ value: Int64) {
self.lock.withLock { self.lock.withLock {
self.values.append((Date(), Int64(value))) self.values.append((Date(), value))
} }
print("adding \(value) to \(self.label)") print("adding \(value) to \(self.label)")
} }
@ -71,6 +71,7 @@ internal class TestCounter: CounterHandler, Equatable {
self.lock.withLock { self.lock.withLock {
self.values = [] self.values = []
} }
print("reseting \(self.label)")
} }
public static func == (lhs: TestCounter, rhs: TestCounter) -> Bool { public static func == (lhs: TestCounter, rhs: TestCounter) -> Bool {
@ -94,11 +95,11 @@ internal class TestRecorder: RecorderHandler, Equatable {
self.aggregate = aggregate self.aggregate = aggregate
} }
func record<DataType: BinaryInteger>(_ value: DataType) { func record(_ value: Int64) {
self.record(Double(value)) self.record(Double(value))
} }
func record<DataType: BinaryFloatingPoint>(_ value: DataType) { func record(_ value: Double) {
self.lock.withLock { self.lock.withLock {
// this may loose precision but good enough as an example // this may loose precision but good enough as an example
values.append((Date(), Double(value))) values.append((Date(), Double(value)))