From 5430e86780ab33fb7d0c325723175c6f737482d6 Mon Sep 17 00:00:00 2001 From: BlindSpot <127803250+blindspotbounty@users.noreply.github.com> Date: Fri, 6 Jun 2025 14:39:53 +0200 Subject: [PATCH 1/2] use bin search + typed instead of tuple --- Sources/Prometheus/Histogram.swift | 40 ++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/Sources/Prometheus/Histogram.swift b/Sources/Prometheus/Histogram.swift index dabe2a0..a790917 100644 --- a/Sources/Prometheus/Histogram.swift +++ b/Sources/Prometheus/Histogram.swift @@ -32,7 +32,20 @@ public final class Histogram: Sendable { @usableFromInline struct State: Sendable { - @usableFromInline var buckets: [(Value, Int)] + @usableFromInline + struct ValueCount: Sendable { + @usableFromInline + let value: Value + @usableFromInline + var count: Int + + @usableFromInline + init(value: Value, count: Int) { + self.value = value + self.count = count + } + } + @usableFromInline var buckets: [ValueCount] @usableFromInline var sum: Value @usableFromInline var count: Int @@ -40,7 +53,7 @@ public final class Histogram: Sendable { init(buckets: [Value]) { self.sum = .zero self.count = 0 - self.buckets = buckets.map { ($0, 0) } + self.buckets = buckets.map { .init(value: $0, count: 0) } } } @@ -58,11 +71,24 @@ public final class Histogram: Sendable { public func record(_ value: Value) { self.box.withLockedValue { state in - for i in state.buckets.startIndex..= value { - state.buckets[i].1 += 1 + + var count = state.buckets.count + var left = state.buckets.startIndex + + while count > 0 { + let half = count / 2 + let mid = state.buckets.index(left, offsetBy: half) + if state.buckets[mid].value >= value { + count = half + } else { + left = state.buckets.index(after: mid) + count -= half + 1 } } + assert(state.buckets.indices.contains(left), "Unexpectedly no suitable bucket found") + + state.buckets[left].count += 1 + state.sum += value state.count += 1 } @@ -96,10 +122,10 @@ extension Histogram: PrometheusMetric { buffer.append(UInt8(ascii: #","#)) } buffer.append(contentsOf: #"le=""#.utf8) - buffer.append(contentsOf: "\(bucket.0.bucketRepresentation)".utf8) + buffer.append(contentsOf: "\(bucket.value.bucketRepresentation)".utf8) buffer.append(UInt8(ascii: #"""#)) buffer.append(contentsOf: #"} "#.utf8) - buffer.append(contentsOf: "\(bucket.1)".utf8) + buffer.append(contentsOf: "\(bucket.count)".utf8) buffer.append(contentsOf: #"\#n"#.utf8) } From 2e2cfb9acd7ea54f8e6fe3dbcfc9949d12975ee0 Mon Sep 17 00:00:00 2001 From: BlindSpot <127803250+blindspotbounty@users.noreply.github.com> Date: Fri, 6 Jun 2025 16:12:20 +0200 Subject: [PATCH 2/2] assert -> if --- Sources/Prometheus/Histogram.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Sources/Prometheus/Histogram.swift b/Sources/Prometheus/Histogram.swift index a790917..a5a7a9c 100644 --- a/Sources/Prometheus/Histogram.swift +++ b/Sources/Prometheus/Histogram.swift @@ -85,9 +85,10 @@ public final class Histogram: Sendable { count -= half + 1 } } - assert(state.buckets.indices.contains(left), "Unexpectedly no suitable bucket found") - state.buckets[left].count += 1 + if state.buckets.indices.contains(left) { + state.buckets[left].count += 1 + } state.sum += value state.count += 1