Skip to content

Commit

Permalink
Introduce PromSummary.capacity to improve performance (#57)
Browse files Browse the repository at this point in the history
* Introduce PromSummary.capacity to improve performance

* Add a test for summary with custom capacity

* Use Deque instead of Array to store summary values

* Revert "Use Deque instead of Array to store summary values"

This reverts commit 1dd8c32.

* Use CircularBuffer instead of Array to store summary values

* Slightly improve wording in comments
  • Loading branch information
wacumov authored Aug 5, 2021
1 parent daf102f commit e85499c
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 6 deletions.
3 changes: 3 additions & 0 deletions Sources/Prometheus/MetricTypes/PromMetric.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ public enum PromMetricType: String {
}

public enum Prometheus {
/// Default capacity of Summaries
public static let defaultSummaryCapacity = 500

/// Default quantiles used by Summaries
public static let defaultQuantiles = [0.01, 0.05, 0.5, 0.9, 0.95, 0.99, 0.999]
}
Expand Down
20 changes: 16 additions & 4 deletions Sources/Prometheus/MetricTypes/Summary.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import NIOConcurrencyHelpers
import NIO
import struct CoreMetrics.TimeUnit
import Dispatch

Expand Down Expand Up @@ -43,8 +44,11 @@ public class PromSummary<NumType: DoubleRepresentable, Labels: SummaryLabels>: P
private let count: PromCounter<NumType, EmptyLabels>

/// Values in this Summary
private var values: [NumType] = []

private var values: CircularBuffer<NumType>

/// Number of values to keep for calculating quantiles
internal let capacity: Int

/// Quantiles used by this Summary
internal let quantiles: [Double]

Expand All @@ -60,9 +64,10 @@ public class PromSummary<NumType: DoubleRepresentable, Labels: SummaryLabels>: P
/// - name: Name of the Summary
/// - help: Help text of the Summary
/// - labels: Labels for the Summary
/// - capacity: Number of values to keep for calculating quantiles
/// - quantiles: Quantiles to use for the Summary
/// - p: Prometheus instance creating this Summary
internal init(_ name: String, _ help: String? = nil, _ labels: Labels = Labels(), _ quantiles: [Double] = Prometheus.defaultQuantiles, _ p: PrometheusClient) {
internal init(_ name: String, _ help: String? = nil, _ labels: Labels = Labels(), _ capacity: Int = Prometheus.defaultSummaryCapacity, _ quantiles: [Double] = Prometheus.defaultQuantiles, _ p: PrometheusClient) {
self.name = name
self.help = help

Expand All @@ -74,6 +79,10 @@ public class PromSummary<NumType: DoubleRepresentable, Labels: SummaryLabels>: P

self.count = .init("\(self.name)_count", nil, 0, p)

self.values = CircularBuffer(initialCapacity: capacity)

self.capacity = capacity

self.quantiles = quantiles

self.labels = labels
Expand Down Expand Up @@ -159,6 +168,9 @@ public class PromSummary<NumType: DoubleRepresentable, Labels: SummaryLabels>: P
}
self.count.inc(1)
self.sum.inc(value)
if self.values.count == self.capacity {
_ = self.values.popFirst()
}
self.values.append(value)
}
}
Expand Down Expand Up @@ -190,7 +202,7 @@ extension PrometheusClient {
if let summary = summaries.first {
return summary
} else {
let newSummary = PromSummary<T, U>(summary.name, summary.help, labels, summary.quantiles, self)
let newSummary = PromSummary<T, U>(summary.name, summary.help, labels, summary.capacity, summary.quantiles, self)
summary.subSummaries.append(newSummary)
return newSummary
}
Expand Down
5 changes: 3 additions & 2 deletions Sources/Prometheus/Prometheus.swift
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ public class PrometheusClient {
/// - type: The type the summary will observe
/// - name: Name of the summary
/// - helpText: Help text for the summary. Usually a short description
/// - capacity: Number of observations to keep for calculating quantiles
/// - quantiles: Quantiles to calculate
/// - labels: Labels to give this summary. Can be left out to default to no labels
///
Expand All @@ -248,15 +249,15 @@ public class PrometheusClient {
forType type: T.Type,
named name: String,
helpText: String? = nil,
capacity: Int = Prometheus.defaultSummaryCapacity,
quantiles: [Double] = Prometheus.defaultQuantiles,
labels: U.Type) -> PromSummary<T, U>
{
return self.lock.withLock {
if let cachedSummary: PromSummary<T, U> = self._getMetricInstance(with: name, andType: .summary) {
return cachedSummary
}

let summary = PromSummary<T, U>(name, helpText, U(), quantiles, self)
let summary = PromSummary<T, U>(name, helpText, U(), capacity, quantiles, self)
let oldInstrument = self.metrics.updateValue(summary, forKey: name)
precondition(oldInstrument == nil, "Label \(oldInstrument!.name) is already associated with a \(oldInstrument!._type).")
return summary
Expand Down
17 changes: 17 additions & 0 deletions Tests/SwiftPrometheusTests/SummaryTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -144,4 +144,21 @@ final class SummaryTests: XCTestCase {
my_summary_sum{myValue=\"labels\"} 123.0
""")
}

func testStandaloneSummaryWithCustomCapacity() {
let capacity = 10
let summary = prom.createSummary(forType: Double.self, named: "my_summary", helpText: "Summary for testing", capacity: capacity, quantiles: [0.5, 0.99], labels: BaseSummaryLabels.self)

for i in 0 ..< capacity { summary.observe(Double(i * 1_000)) }
for i in 0 ..< capacity { summary.observe(Double(i)) }

XCTAssertEqual(summary.collect(), """
# HELP my_summary Summary for testing
# TYPE my_summary summary
my_summary{quantile="0.5", myValue="*"} 4.5
my_summary{quantile="0.99", myValue="*"} 9.0
my_summary_count{myValue="*"} 20.0
my_summary_sum{myValue="*"} 45045.0
""")
}
}

0 comments on commit e85499c

Please sign in to comment.