Skip to content

Commit a61dd74

Browse files
committed
add Sendable conformance
1 parent 4f5230d commit a61dd74

File tree

2 files changed

+60
-60
lines changed

2 files changed

+60
-60
lines changed

Sources/Histogram/Histogram.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@ import Numerics
2121
/**
2222
* Number of significant digits for values recorded in histogram.
2323
*/
24-
public enum SignificantDigits: Int8, Codable {
24+
public enum SignificantDigits: Int8, Codable, Sendable {
2525
case zero, one, two, three, four, five
2626
}
2727

2828
/**
2929
* Histogram output format.
3030
*/
31-
public enum HistogramOutputFormat {
31+
public enum HistogramOutputFormat: Sendable {
3232
case plainText
3333
case csv
3434
}
@@ -56,7 +56,7 @@ public enum HistogramOutputFormat {
5656
* they are encountered. Note that recording calls that cause auto-resizing may take longer to execute, as resizing
5757
* incurs allocation and copying of internal data structures.
5858
*/
59-
public struct Histogram<Count: FixedWidthInteger & Codable>: Codable {
59+
public struct Histogram<Count: FixedWidthInteger & Codable & Sendable>: Codable, Sendable {
6060
/// The lowest value that can be discerned (distinguished from 0) by the histogram.
6161
public let lowestDiscernibleValue: UInt64
6262

Tests/HistogramTests/HistogramDataAccessTests.swift

+57-57
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,23 @@ final class HistogramDataAccessTests: XCTestCase {
2020
private static let numberOfSignificantValueDigits = SignificantDigits.three
2121
private static let value: UInt64 = 4
2222

23-
private static var histogram = Histogram<UInt64>(highestTrackableValue: highestTrackableValue, numberOfSignificantValueDigits: numberOfSignificantValueDigits)
23+
private var histogram = Histogram<UInt64>(highestTrackableValue: highestTrackableValue, numberOfSignificantValueDigits: numberOfSignificantValueDigits)
2424

25-
private static var scaledHistogram = Histogram<UInt64>(
25+
private var scaledHistogram = Histogram<UInt64>(
2626
lowestDiscernibleValue: 1_000,
2727
highestTrackableValue: highestTrackableValue * 512,
2828
numberOfSignificantValueDigits: numberOfSignificantValueDigits
2929
)
3030

31-
private static var rawHistogram = Histogram<UInt64>(highestTrackableValue: highestTrackableValue, numberOfSignificantValueDigits: numberOfSignificantValueDigits)
31+
private var rawHistogram = Histogram<UInt64>(highestTrackableValue: highestTrackableValue, numberOfSignificantValueDigits: numberOfSignificantValueDigits)
3232

33-
private static var scaledRawHistogram = Histogram<UInt64>(
33+
private var scaledRawHistogram = Histogram<UInt64>(
3434
lowestDiscernibleValue: 1_000,
3535
highestTrackableValue: highestTrackableValue * 512,
3636
numberOfSignificantValueDigits: numberOfSignificantValueDigits
3737
)
3838

39-
override static func setUp() {
39+
override func setUp() {
4040
// Log hypothetical scenario: 100 seconds of "perfect" 1msec results, sampled
4141
// 100 times per second (10,000 results), followed by a 100 second pause with
4242
// a single (100 second) recorded result. Recording is done indicating an expected
@@ -54,33 +54,33 @@ final class HistogramDataAccessTests: XCTestCase {
5454
}
5555

5656
func testScalingEquivalence() {
57-
XCTAssertEqual(Self.histogram.mean * 512, Self.scaledHistogram.mean, accuracy: Self.scaledHistogram.mean * 0.000001, "averages should be equivalent")
58-
XCTAssertEqual(Self.histogram.totalCount, Self.scaledHistogram.totalCount, "total count should be the same")
59-
XCTAssertEqual(Self.scaledHistogram.highestEquivalentForValue(Self.histogram.valueAtPercentile(99.0) * 512),
60-
Self.scaledHistogram.highestEquivalentForValue(Self.scaledHistogram.valueAtPercentile(99.0)), "99%'iles should be equivalent")
61-
XCTAssertEqual(Self.scaledHistogram.highestEquivalentForValue(Self.histogram.max * 512), Self.scaledHistogram.max, "Max should be equivalent")
57+
XCTAssertEqual(histogram.mean * 512, scaledHistogram.mean, accuracy: scaledHistogram.mean * 0.000001, "averages should be equivalent")
58+
XCTAssertEqual(histogram.totalCount, scaledHistogram.totalCount, "total count should be the same")
59+
XCTAssertEqual(scaledHistogram.highestEquivalentForValue(histogram.valueAtPercentile(99.0) * 512),
60+
scaledHistogram.highestEquivalentForValue(scaledHistogram.valueAtPercentile(99.0)), "99%'iles should be equivalent")
61+
XCTAssertEqual(scaledHistogram.highestEquivalentForValue(histogram.max * 512), scaledHistogram.max, "Max should be equivalent")
6262
}
6363

6464
func testTotalCount() {
6565
// The overflow value should count in the total count:
66-
XCTAssertEqual(10_001, Self.rawHistogram.totalCount, "Raw total count is 10,001")
67-
XCTAssertEqual(20_000, Self.histogram.totalCount, "Total count is 20,000")
66+
XCTAssertEqual(10_001, rawHistogram.totalCount, "Raw total count is 10,001")
67+
XCTAssertEqual(20_000, histogram.totalCount, "Total count is 20,000")
6868
}
6969

7070
func testMax() {
71-
XCTAssertTrue(Self.histogram.valuesAreEquivalent(100 * 1_000 * 1_000, Self.histogram.max))
71+
XCTAssertTrue(histogram.valuesAreEquivalent(100 * 1_000 * 1_000, histogram.max))
7272
}
7373

7474
func testMin() {
75-
XCTAssertTrue(Self.histogram.valuesAreEquivalent(1_000, Self.histogram.min))
75+
XCTAssertTrue(histogram.valuesAreEquivalent(1_000, histogram.min))
7676
}
7777

7878
func testMean() {
7979
let expectedRawMean = ((10_000.0 * 1_000) + (1.0 * 100_000_000)) / 10_001 // direct avg. of raw results
8080
let expectedMean = (1_000.0 + 50_000_000.0) / 2 // avg. 1 msec for half the time, and 50 sec for other half
8181
// We expect to see the mean to be accurate to ~3 decimal points (~0.1%):
82-
XCTAssertEqual(expectedRawMean, Self.rawHistogram.mean, accuracy: expectedRawMean * 0.001, "Raw mean is \(expectedRawMean) +/- 0.1%")
83-
XCTAssertEqual(expectedMean, Self.histogram.mean, accuracy: expectedMean * 0.001, "Mean is \(expectedMean) +/- 0.1%")
82+
XCTAssertEqual(expectedRawMean, rawHistogram.mean, accuracy: expectedRawMean * 0.001, "Raw mean is \(expectedRawMean) +/- 0.1%")
83+
XCTAssertEqual(expectedMean, histogram.mean, accuracy: expectedMean * 0.001, "Mean is \(expectedMean) +/- 0.1%")
8484
}
8585

8686
func testStdDeviation() {
@@ -97,9 +97,9 @@ final class HistogramDataAccessTests: XCTestCase {
9797
let expectedStdDev = (expectedSquareDeviationSum / 20_000).squareRoot()
9898

9999
// We expect to see the standard deviations to be accurate to ~3 decimal points (~0.1%):
100-
XCTAssertEqual(expectedRawStdDev, Self.rawHistogram.stdDeviation, accuracy: expectedRawStdDev * 0.001,
100+
XCTAssertEqual(expectedRawStdDev, rawHistogram.stdDeviation, accuracy: expectedRawStdDev * 0.001,
101101
"Raw standard deviation is \(expectedRawStdDev) +/- 0.1%")
102-
XCTAssertEqual(expectedStdDev, Self.histogram.stdDeviation, accuracy: expectedStdDev * 0.001,
102+
XCTAssertEqual(expectedStdDev, histogram.stdDeviation, accuracy: expectedStdDev * 0.001,
103103
"Standard deviation is \(expectedStdDev) +/- 0.1%")
104104
}
105105

@@ -140,33 +140,33 @@ final class HistogramDataAccessTests: XCTestCase {
140140
}
141141

142142
func testValueAtPercentile() {
143-
XCTAssertEqual(1_000.0, Double(Self.rawHistogram.valueAtPercentile(30.0)),
143+
XCTAssertEqual(1_000.0, Double(rawHistogram.valueAtPercentile(30.0)),
144144
accuracy: 1_000.0 * 0.001, "raw 30%'ile is 1 msec +/- 0.1%")
145-
XCTAssertEqual(1_000.0, Double(Self.rawHistogram.valueAtPercentile(99.0)),
145+
XCTAssertEqual(1_000.0, Double(rawHistogram.valueAtPercentile(99.0)),
146146
accuracy: 1_000.0 * 0.001, "raw 99%'ile is 1 msec +/- 0.1%")
147-
XCTAssertEqual(1_000.0, Double(Self.rawHistogram.valueAtPercentile(99.99)),
147+
XCTAssertEqual(1_000.0, Double(rawHistogram.valueAtPercentile(99.99)),
148148
accuracy: 1_000.0 * 0.001, "raw 99.99%'ile is 1 msec +/- 0.1%")
149149

150-
XCTAssertEqual(100_000_000.0, Double(Self.rawHistogram.valueAtPercentile(99.999)),
150+
XCTAssertEqual(100_000_000.0, Double(rawHistogram.valueAtPercentile(99.999)),
151151
accuracy: 100_000_000.0 * 0.001, "raw 99.999%'ile is 100 sec +/- 0.1%")
152-
XCTAssertEqual(100_000_000.0, Double(Self.rawHistogram.valueAtPercentile(100.0)),
152+
XCTAssertEqual(100_000_000.0, Double(rawHistogram.valueAtPercentile(100.0)),
153153
accuracy: 100_000_000.0 * 0.001, "raw 100%'ile is 100 sec +/- 0.1%")
154154

155-
XCTAssertEqual(1_000.0, Double(Self.histogram.valueAtPercentile(30.0)),
155+
XCTAssertEqual(1_000.0, Double(histogram.valueAtPercentile(30.0)),
156156
accuracy: 1_000.0 * 0.001, "30%'ile is 1 msec +/- 0.1%")
157-
XCTAssertEqual(1_000.0, Double(Self.histogram.valueAtPercentile(50.0)),
157+
XCTAssertEqual(1_000.0, Double(histogram.valueAtPercentile(50.0)),
158158
accuracy: 1_000.0 * 0.001, "50%'ile is 1 msec +/- 0.1%")
159159

160-
XCTAssertEqual(50_000_000.0, Double(Self.histogram.valueAtPercentile(75.0)),
160+
XCTAssertEqual(50_000_000.0, Double(histogram.valueAtPercentile(75.0)),
161161
accuracy: 50_000_000.0 * 0.001, "75%'ile is 50 sec +/- 0.1%")
162162

163-
XCTAssertEqual(80_000_000.0, Double(Self.histogram.valueAtPercentile(90.0)),
163+
XCTAssertEqual(80_000_000.0, Double(histogram.valueAtPercentile(90.0)),
164164
accuracy: 80_000_000.0 * 0.001, "90%'ile is 80 sec +/- 0.1%")
165-
XCTAssertEqual(98_000_000.0, Double(Self.histogram.valueAtPercentile(99.0)),
165+
XCTAssertEqual(98_000_000.0, Double(histogram.valueAtPercentile(99.0)),
166166
accuracy: 98_000_000.0 * 0.001, "99%'ile is 98 sec +/- 0.1%")
167-
XCTAssertEqual(100_000_000.0, Double(Self.histogram.valueAtPercentile(99.999)),
167+
XCTAssertEqual(100_000_000.0, Double(histogram.valueAtPercentile(99.999)),
168168
accuracy: 100_000_000.0 * 0.001, "99.999%'ile is 100 sec +/- 0.1%")
169-
XCTAssertEqual(100_000_000.0, Double(Self.histogram.valueAtPercentile(100.0)),
169+
XCTAssertEqual(100_000_000.0, Double(histogram.valueAtPercentile(100.0)),
170170
accuracy: 100_000_000.0 * 0.001, "100%'ile is 100 sec +/- 0.1%")
171171
}
172172

@@ -181,41 +181,41 @@ final class HistogramDataAccessTests: XCTestCase {
181181
}
182182

183183
func testPercentileAtOrBelowValue() {
184-
XCTAssertEqual(99.99, Self.rawHistogram.percentileAtOrBelowValue(5_000),
184+
XCTAssertEqual(99.99, rawHistogram.percentileAtOrBelowValue(5_000),
185185
accuracy: 0.0001, "Raw percentile at or below 5 msec is 99.99% +/- 0.0001")
186-
XCTAssertEqual(50.0, Self.histogram.percentileAtOrBelowValue(5_000),
186+
XCTAssertEqual(50.0, histogram.percentileAtOrBelowValue(5_000),
187187
accuracy: 0.0001, "Percentile at or below 5 msec is 50% +/- 0.0001%")
188-
XCTAssertEqual(100.0, Self.histogram.percentileAtOrBelowValue(100_000_000),
188+
XCTAssertEqual(100.0, histogram.percentileAtOrBelowValue(100_000_000),
189189
accuracy: 0.0001, "Percentile at or below 100 sec is 100% +/- 0.0001%")
190190
}
191191

192192
func testCountWithinRange() {
193-
XCTAssertEqual(10_000, Self.rawHistogram.count(within: 1_000 ... 1_000),
193+
XCTAssertEqual(10_000, rawHistogram.count(within: 1_000 ... 1_000),
194194
"Count of raw values between 1 msec and 1 msec is 1")
195-
XCTAssertEqual(1, Self.rawHistogram.count(within: 5_000 ... 150_000_000),
195+
XCTAssertEqual(1, rawHistogram.count(within: 5_000 ... 150_000_000),
196196
"Count of raw values between 5 msec and 150 sec is 1")
197-
XCTAssertEqual(10_000, Self.histogram.count(within: 5_000 ... 150_000_000),
197+
XCTAssertEqual(10_000, histogram.count(within: 5_000 ... 150_000_000),
198198
"Count of values between 5 msec and 150 sec is 10,000")
199199
}
200200

201201
func testCountForValue() {
202-
XCTAssertEqual(0, Self.rawHistogram.count(within: 10_000 ... 10_010),
202+
XCTAssertEqual(0, rawHistogram.count(within: 10_000 ... 10_010),
203203
"Count of raw values at 10 msec is 0")
204-
XCTAssertEqual(1, Self.histogram.count(within: 10_000 ... 10_010),
204+
XCTAssertEqual(1, histogram.count(within: 10_000 ... 10_010),
205205
"Count of values at 10 msec is 0")
206-
XCTAssertEqual(10_000, Self.rawHistogram.countForValue(1_000),
206+
XCTAssertEqual(10_000, rawHistogram.countForValue(1_000),
207207
"Count of raw values at 1 msec is 10,000")
208-
XCTAssertEqual(10_000, Self.histogram.countForValue(1_000),
208+
XCTAssertEqual(10_000, histogram.countForValue(1_000),
209209
"Count of values at 1 msec is 10,000")
210210
}
211211

212212
func testPercentiles() {
213-
for iv in Self.histogram.percentiles(ticksPerHalfDistance: 5) {
213+
for iv in histogram.percentiles(ticksPerHalfDistance: 5) {
214214
XCTAssertEqual(
215-
iv.value, Self.histogram.highestEquivalentForValue(Self.histogram.valueAtPercentile(iv.percentile)),
215+
iv.value, histogram.highestEquivalentForValue(histogram.valueAtPercentile(iv.percentile)),
216216
"Iterator value: \(iv.value), count: \(iv.count), percentile: \(iv.percentile)\n" +
217-
"histogram valueAtPercentile(\(iv.percentile)): \(Self.histogram.valueAtPercentile(iv.percentile)), " +
218-
"highest equivalent value: \(Self.histogram.highestEquivalentForValue(Self.histogram.valueAtPercentile(iv.percentile)))"
217+
"histogram valueAtPercentile(\(iv.percentile)): \(histogram.valueAtPercentile(iv.percentile)), " +
218+
"highest equivalent value: \(histogram.highestEquivalentForValue(histogram.valueAtPercentile(iv.percentile)))"
219219
)
220220
}
221221
}
@@ -255,7 +255,7 @@ final class HistogramDataAccessTests: XCTestCase {
255255

256256
// Iterate data using linear buckets of 100 msec each.
257257
var index = 0
258-
for iv in Self.rawHistogram.linearBucketValues(valueUnitsPerBucket: 100_000 /* 100 msec */ ) {
258+
for iv in rawHistogram.linearBucketValues(valueUnitsPerBucket: 100_000 /* 100 msec */ ) {
259259
let countAddedInThisBucket = iv.countAddedInThisIterationStep
260260
if index == 0 {
261261
XCTAssertEqual(10_000, countAddedInThisBucket, "Raw Linear 100 msec bucket # 0 added a count of 10000")
@@ -272,7 +272,7 @@ final class HistogramDataAccessTests: XCTestCase {
272272
// Iterate data using linear buckets of 10 msec each.
273273
index = 0
274274
var totalAddedCounts: UInt64 = 0
275-
for iv in Self.histogram.linearBucketValues(valueUnitsPerBucket: 10_000 /* 10 msec */ ) {
275+
for iv in histogram.linearBucketValues(valueUnitsPerBucket: 10_000 /* 10 msec */ ) {
276276
let countAddedInThisBucket = iv.countAddedInThisIterationStep
277277
if index == 0 {
278278
XCTAssertEqual(10_000, countAddedInThisBucket, "Linear 1 sec bucket # 0 [\(iv.prevValue)..\(iv.value)] added a count of 10000")
@@ -292,7 +292,7 @@ final class HistogramDataAccessTests: XCTestCase {
292292
// Iterate data using linear buckets of 1 msec each.
293293
index = 0
294294
totalAddedCounts = 0
295-
for iv in Self.histogram.linearBucketValues(valueUnitsPerBucket: 1_000 /* 1 msec */ ) {
295+
for iv in histogram.linearBucketValues(valueUnitsPerBucket: 1_000 /* 1 msec */ ) {
296296
let countAddedInThisBucket = iv.countAddedInThisIterationStep
297297
if index == 1 {
298298
XCTAssertEqual(10_000, countAddedInThisBucket, "Linear 1 sec bucket # 0 [\(iv.prevValue)..\(iv.value)] added a count of 10000")
@@ -319,7 +319,7 @@ final class HistogramDataAccessTests: XCTestCase {
319319
func testLogarithmicBucketValues() {
320320
// Iterate raw data using logarithmic buckets starting at 10 msec.
321321
var index = 0
322-
for iv in Self.rawHistogram.logarithmicBucketValues(valueUnitsInFirstBucket: 10_000 /* 10 msec */, logBase: 2) {
322+
for iv in rawHistogram.logarithmicBucketValues(valueUnitsInFirstBucket: 10_000 /* 10 msec */, logBase: 2) {
323323
let countAddedInThisBucket = iv.countAddedInThisIterationStep
324324
if index == 0 {
325325
XCTAssertEqual(10_000, countAddedInThisBucket, "Raw Logarithmic 10 msec bucket # 0 added a count of 10000")
@@ -334,7 +334,7 @@ final class HistogramDataAccessTests: XCTestCase {
334334

335335
index = 0
336336
var totalAddedCounts: UInt64 = 0
337-
for iv in Self.histogram.logarithmicBucketValues(valueUnitsInFirstBucket: 10_000 /* 10 msec */, logBase: 2) {
337+
for iv in histogram.logarithmicBucketValues(valueUnitsInFirstBucket: 10_000 /* 10 msec */, logBase: 2) {
338338
let countAddedInThisBucket = iv.countAddedInThisIterationStep
339339
if index == 0 {
340340
XCTAssertEqual(10_000, countAddedInThisBucket,
@@ -350,7 +350,7 @@ final class HistogramDataAccessTests: XCTestCase {
350350
func testRecordedValues() {
351351
// Iterate raw data by stepping through every value that has a count recorded:
352352
var index = 0
353-
for iv in Self.rawHistogram.recordedValues() {
353+
for iv in rawHistogram.recordedValues() {
354354
let countAddedInThisBucket = iv.countAddedInThisIterationStep
355355
if index == 0 {
356356
XCTAssertEqual(10_000, countAddedInThisBucket, "Raw recorded value bucket # 0 added a count of 10000")
@@ -363,7 +363,7 @@ final class HistogramDataAccessTests: XCTestCase {
363363

364364
index = 0
365365
var totalAddedCounts: UInt64 = 0
366-
for iv in Self.histogram.recordedValues() {
366+
for iv in histogram.recordedValues() {
367367
let countAddedInThisBucket = iv.countAddedInThisIterationStep
368368
if index == 0 {
369369
XCTAssertEqual(10_000, countAddedInThisBucket,
@@ -385,11 +385,11 @@ final class HistogramDataAccessTests: XCTestCase {
385385
var totalValueToThisPoint: UInt64 = 0
386386

387387
// Iterate raw data by stepping through every value that has a count recorded:
388-
for v in Self.rawHistogram.allValues() {
388+
for v in rawHistogram.allValues() {
389389
let countAddedInThisBucket = v.countAddedInThisIterationStep
390390
if index == 1_000 {
391391
XCTAssertEqual(10_000, countAddedInThisBucket, "Raw allValues bucket # 0 added a count of 10000")
392-
} else if Self.histogram.valuesAreEquivalent(v.value, 100_000_000) {
392+
} else if histogram.valuesAreEquivalent(v.value, 100_000_000) {
393393
XCTAssertEqual(1, countAddedInThisBucket, "Raw allValues value bucket # \(index) added a count of 1")
394394
} else {
395395
XCTAssertEqual(0, countAddedInThisBucket, "Raw allValues value bucket # \(index) added a count of 0")
@@ -400,12 +400,12 @@ final class HistogramDataAccessTests: XCTestCase {
400400
XCTAssertEqual(totalValueToThisPoint, v.totalValueToThisValue, "total Value should match")
401401
index += 1
402402
}
403-
XCTAssertEqual(Self.histogram.counts.count, index, "index should be equal to counts array length")
403+
XCTAssertEqual(histogram.counts.count, index, "index should be equal to counts array length")
404404

405405
index = 0
406406
var totalAddedCounts: UInt64 = 0
407407

408-
for v in Self.histogram.allValues() {
408+
for v in histogram.allValues() {
409409
let countAddedInThisBucket = v.countAddedInThisIterationStep
410410
if index == 1_000 {
411411
XCTAssertEqual(10_000, countAddedInThisBucket,
@@ -415,10 +415,10 @@ final class HistogramDataAccessTests: XCTestCase {
415415
"The count in AllValues bucket # \(index)" +
416416
" is exactly the amount added since the last iteration")
417417
totalAddedCounts += countAddedInThisBucket
418-
XCTAssertTrue(Self.histogram.valuesAreEquivalent(Self.histogram.valueFromIndex(index), v.value), "valueFromIndex() should be equal to value")
418+
XCTAssertTrue(histogram.valuesAreEquivalent(histogram.valueFromIndex(index), v.value), "valueFromIndex() should be equal to value")
419419
index += 1
420420
}
421-
XCTAssertEqual(Self.histogram.counts.count, index, "index should be equal to counts array length")
421+
XCTAssertEqual(self.histogram.counts.count, index, "index should be equal to counts array length")
422422
XCTAssertEqual(20_000, totalAddedCounts, "Total added counts should be 20000")
423423
}
424424

0 commit comments

Comments
 (0)