Skip to content

Commit cb6e66f

Browse files
authored
Merge a51b65f into d2c3709
2 parents d2c3709 + a51b65f commit cb6e66f

File tree

2 files changed

+84
-100
lines changed

2 files changed

+84
-100
lines changed

bench/bench.mbt

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,12 @@ fn iter_count(name? : String, inner : () -> Unit, count : UInt) -> Summary {
4242
} else {
4343
target_batch_size.ceil().to_int()
4444
}
45-
let samples = Array::new(capacity=count)
46-
samples.resize(count, 0.0)
47-
for i in 0..<count {
48-
samples[i] = iter_n_microseconds(inner, batch_size) / batch_size.to_double()
49-
}
50-
winsorize(samples, 5.0)
51-
Summary::new(name?, samples, batch_size)
45+
let samples = Array::makei(count, fn(_) {
46+
iter_n_microseconds(inner, batch_size) / batch_size.to_double()
47+
})
48+
samples.sort()
49+
winsorize(sorted_data=samples, 5.0)
50+
Summary::new(name?, sorted_data=samples, batch_size)
5251
}
5352

5453
///|

bench/stats.mbt

Lines changed: 78 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -37,21 +37,21 @@ struct Summary {
3737
///|
3838
fn Summary::new(
3939
name? : String,
40-
data : Array[Double],
40+
sorted_data~ : Array[Double],
4141
batch_size : Int
4242
) -> Summary {
43-
let sum = sum(data)
44-
let min = min(data)
45-
let max = max(data)
46-
let mean = mean(data)
47-
let median = median(data)
48-
let var = var(data)
49-
let std_dev = std_dev(data)
50-
let std_dev_pct = std_dev_pct(data)
51-
let median_abs_dev = median_abs_dev(data)
52-
let median_abs_dev_pct = median_abs_dev_pct(data)
53-
let quartiles = quartiles(data)
54-
let iqr = iqr(data)
43+
let sum = sum(sorted_data)
44+
let min = min(sorted_data~)
45+
let max = max(sorted_data~)
46+
let mean = mean(sorted_data, sum~)
47+
let median = median(sorted_data~)
48+
let var = var(sorted_data, mean~)
49+
let std_dev = std_dev(var~)
50+
let std_dev_pct = std_dev_pct(mean~, std_dev~)
51+
let median_abs_dev = median_abs_dev(sorted_data, median_=median)
52+
let median_abs_dev_pct = median_abs_dev_pct(median~, median_abs_dev~)
53+
let quartiles = quartiles(sorted_data~)
54+
let iqr = iqr(quartiles~)
5555
{
5656
name,
5757
sum,
@@ -67,7 +67,7 @@ fn Summary::new(
6767
quartiles,
6868
iqr,
6969
batch_size,
70-
runs: data.length(),
70+
runs: sorted_data.length(),
7171
}
7272
}
7373

@@ -81,45 +81,31 @@ fn sum(data : Array[Double]) -> Double {
8181
}
8282

8383
///|
84-
fn min(data : Array[Double]) -> Double {
85-
let mut min = data[0]
86-
for i in data {
87-
if i < min {
88-
min = i
89-
}
90-
}
91-
min
84+
fn min(sorted_data~ : Array[Double]) -> Double {
85+
sorted_data[0]
9286
}
9387

9488
///|
95-
fn max(data : Array[Double]) -> Double {
96-
let mut max = data[0]
97-
for i in data {
98-
if i > max {
99-
max = i
100-
}
101-
}
102-
max
89+
fn max(sorted_data~ : Array[Double]) -> Double {
90+
sorted_data[sorted_data.length() - 1]
10391
}
10492

10593
///|
106-
fn mean(data : Array[Double]) -> Double {
107-
let sum = sum(data)
94+
fn mean(data : Array[Double], sum~ : Double) -> Double {
10895
let count = data.length()
10996
sum / count.to_double()
11097
}
11198

11299
///|
113-
fn median(data : Array[Double]) -> Double {
114-
percentile(data, 50.0)
100+
fn median(sorted_data~ : Array[Double]) -> Double {
101+
percentile(sorted_data~, pct=50.0)
115102
}
116103

117104
///|
118-
fn var(data : Array[Double]) -> Double {
105+
fn var(data : Array[Double], mean~ : Double) -> Double {
119106
if data.length() < 2 {
120107
return 0.0
121108
}
122-
let mean = mean(data)
123109
let mut v = 0.0
124110
for i in data {
125111
let d = i - mean
@@ -129,102 +115,101 @@ fn var(data : Array[Double]) -> Double {
129115
}
130116

131117
///|
132-
fn std_dev(data : Array[Double]) -> Double {
133-
var(data).sqrt()
118+
fn std_dev(var~ : Double) -> Double {
119+
var.sqrt()
134120
}
135121

136122
///|
137-
fn std_dev_pct(data : Array[Double]) -> Double {
138-
let mean = mean(data)
139-
let std_dev = std_dev(data)
123+
fn std_dev_pct(mean~ : Double, std_dev~ : Double) -> Double {
140124
if mean == 0.0 {
141125
return 0.0
142126
}
143127
std_dev / mean * 100.0
144128
}
145129

146130
///|
147-
fn median_abs_dev(data : Array[Double]) -> Double {
148-
let med = median(data)
149-
let abs_devs = data.map(fn(x) { (med - x).abs() })
131+
fn median_abs_dev(data : Array[Double], median_~ : Double) -> Double {
132+
let abs_devs = data.map(fn(x) { (median_ - x).abs() })
133+
abs_devs.sort()
150134
// https://en.wikipedia.org/wiki/Median_absolute_deviation
151-
median(abs_devs) * 1.4826
135+
median(sorted_data=abs_devs) * 1.4826
152136
}
153137

154138
///|
155-
fn median_abs_dev_pct(data : Array[Double]) -> Double {
156-
let med = median(data)
157-
let mad = median_abs_dev(data)
158-
if med == 0.0 {
139+
fn median_abs_dev_pct(median~ : Double, median_abs_dev~ : Double) -> Double {
140+
if median == 0.0 {
159141
return 0.0
160142
}
161-
mad / med * 100.0
143+
median_abs_dev / median * 100.0
162144
}
163145

164146
///|
165-
fn quartiles(data : Array[Double]) -> (Double, Double, Double) {
166-
let sorted = data.copy()
167-
sorted.sort()
168-
try {
169-
let q1 = percentile_of_sorted(sorted, 25.0)
170-
let q2 = percentile_of_sorted(sorted, 50.0)
171-
let q3 = percentile_of_sorted(sorted, 75.0)
172-
(q1, q2, q3)
173-
} catch {
174-
_ => panic()
175-
}
147+
fn quartiles(sorted_data~ : Array[Double]) -> (Double, Double, Double) {
148+
let q1 = percentile(sorted_data~, pct=25.0)
149+
let q2 = percentile(sorted_data~, pct=50.0)
150+
let q3 = percentile(sorted_data~, pct=75.0)
151+
(q1, q2, q3)
176152
}
177153

178154
///|
179-
fn iqr(data : Array[Double]) -> Double {
180-
let (q1, _, q3) = quartiles(data)
155+
fn iqr(quartiles~ : (Double, Double, Double)) -> Double {
156+
let (q1, _, q3) = quartiles
181157
q3 - q1
182158
}
183159

184160
///|
185-
fn percentile_of_sorted(data : Array[Double], pct : Double) -> Double! {
186-
assert_false(data.is_empty())
187-
assert_true(pct >= 0.0 && pct <= 100.0)
188-
if data.length() == 1 {
189-
return data[0]
161+
fn percentile(sorted_data~ : Array[Double], pct~ : Double) -> Double {
162+
guard sorted_data.length() > 0
163+
guard pct >= 0.0 && pct <= 100.0
164+
if sorted_data.length() == 1 {
165+
return sorted_data[0]
190166
}
191167
if pct == 100.0 {
192-
return data[data.length() - 1]
168+
return sorted_data[sorted_data.length() - 1]
193169
}
194-
let length = (data.length() - 1).to_double()
170+
let length = (sorted_data.length() - 1).to_double()
195171
let rank = pct / 100 * length
196172
let lrank = rank.floor()
197173
let d = rank - lrank
198174
let n = lrank.to_int()
199-
let lo = data[n]
200-
let hi = data[n + 1]
175+
let lo = sorted_data[n]
176+
let hi = sorted_data[n + 1]
201177
lo + (hi - lo) * d
202178
}
203179

204180
///|
205-
fn percentile(data : Array[Double], pct : Double) -> Double {
206-
let sorted = data.copy()
207-
sorted.sort()
208-
try percentile_of_sorted(sorted, pct) catch {
209-
_ => panic()
181+
fn winsorize(sorted_data~ : Array[Double], pct : Double) -> Unit {
182+
let lo = percentile(sorted_data~, pct~)
183+
let hi = percentile(sorted_data~, pct=100.0 - pct)
184+
for i, samp in sorted_data {
185+
if samp > hi {
186+
sorted_data[i] = hi
187+
} else if samp < lo {
188+
sorted_data[i] = lo
189+
}
210190
}
211191
}
212192

213193
///|
214-
fn winsorize(data : Array[Double], pct : Double) -> Unit {
215-
let sorted = data.copy()
216-
sorted.sort()
217-
try {
218-
let lo = percentile_of_sorted(sorted, pct)
219-
let hi = percentile_of_sorted(sorted, 100.0 - pct)
220-
for i, samp in data {
221-
if samp > hi {
222-
data[i] = hi
223-
} else if samp < lo {
224-
data[i] = lo
225-
}
226-
}
227-
} catch {
228-
_ => panic()
229-
}
194+
test {
195+
let data = [1.1, 21.4, 2.2, 3.3, 4.5, 12.5, 33.3, 14.4]
196+
data.sort()
197+
let summary = Summary::new(sorted_data=data, 3)
198+
assert_true(summary.sum.is_close(92.7))
199+
assert_true(summary.min.is_close(1.1))
200+
assert_true(summary.max.is_close(33.3))
201+
assert_true(summary.mean.is_close(11.5875))
202+
assert_true(summary.median.is_close(8.5))
203+
assert_true(summary.var.is_close(127.64125))
204+
assert_true(summary.std_dev.is_close(11.297842714430043))
205+
assert_true(summary.std_dev_pct.is_close(97.50026075020534))
206+
assert_true(summary.median_abs_dev.is_close(9.04386))
207+
assert_true(summary.median_abs_dev_pct.is_close(106.39835294117646))
208+
let (q1, q2, q3) = summary.quartiles
209+
assert_true(q1.is_close(3.025))
210+
assert_true(q2.is_close(8.5))
211+
assert_true(q3.is_close(16.15))
212+
assert_true(summary.iqr.is_close(13.125))
213+
assert_true(summary.batch_size == 3)
214+
assert_true(summary.runs == 8)
230215
}

0 commit comments

Comments
 (0)