benchstats 3.3.2__tar.gz → 3.3.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {benchstats-3.3.2 → benchstats-3.3.3}/CHANGELOG.md +6 -0
- {benchstats-3.3.2/src/benchstats.egg-info → benchstats-3.3.3}/PKG-INFO +1 -1
- {benchstats-3.3.2 → benchstats-3.3.3}/pyproject.toml +1 -1
- {benchstats-3.3.2 → benchstats-3.3.3}/src/benchstats/compare.py +16 -14
- {benchstats-3.3.2 → benchstats-3.3.3/src/benchstats.egg-info}/PKG-INFO +1 -1
- {benchstats-3.3.2 → benchstats-3.3.3}/tests/test_compare.py +4 -1
- {benchstats-3.3.2 → benchstats-3.3.3}/tests/test_qbench.py +12 -5
- {benchstats-3.3.2 → benchstats-3.3.3}/LICENSE +0 -0
- {benchstats-3.3.2 → benchstats-3.3.3}/MANIFEST.in +0 -0
- {benchstats-3.3.2 → benchstats-3.3.3}/README.md +0 -0
- {benchstats-3.3.2 → benchstats-3.3.3}/docs/imgs/simple1.svg +0 -0
- {benchstats-3.3.2 → benchstats-3.3.3}/docs/imgs/simple2.svg +0 -0
- {benchstats-3.3.2 → benchstats-3.3.3}/docs/imgs/simple3.svg +0 -0
- {benchstats-3.3.2 → benchstats-3.3.3}/docs/imgs/simple4.svg +0 -0
- {benchstats-3.3.2 → benchstats-3.3.3}/docs/imgs/simple4_bad.svg +0 -0
- {benchstats-3.3.2 → benchstats-3.3.3}/setup.cfg +0 -0
- {benchstats-3.3.2 → benchstats-3.3.3}/src/benchstats/__init__.py +0 -0
- {benchstats-3.3.2 → benchstats-3.3.3}/src/benchstats/__main__.py +0 -0
- {benchstats-3.3.2 → benchstats-3.3.3}/src/benchstats/cli_parser.py +0 -0
- {benchstats-3.3.2 → benchstats-3.3.3}/src/benchstats/common.py +0 -0
- {benchstats-3.3.2 → benchstats-3.3.3}/src/benchstats/parser_GbenchJson.py +0 -0
- {benchstats-3.3.2 → benchstats-3.3.3}/src/benchstats/parsers.py +0 -0
- {benchstats-3.3.2 → benchstats-3.3.3}/src/benchstats/qbench.py +0 -0
- {benchstats-3.3.2 → benchstats-3.3.3}/src/benchstats/render.py +0 -0
- {benchstats-3.3.2 → benchstats-3.3.3}/src/benchstats.egg-info/SOURCES.txt +0 -0
- {benchstats-3.3.2 → benchstats-3.3.3}/src/benchstats.egg-info/dependency_links.txt +0 -0
- {benchstats-3.3.2 → benchstats-3.3.3}/src/benchstats.egg-info/entry_points.txt +0 -0
- {benchstats-3.3.2 → benchstats-3.3.3}/src/benchstats.egg-info/requires.txt +0 -0
- {benchstats-3.3.2 → benchstats-3.3.3}/src/benchstats.egg-info/top_level.txt +0 -0
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
# 3.3.3
|
|
2
|
+
- breaking change, though unlikely important for anyone: due to T-test also being affected by
|
|
3
|
+
bad date, renamed `compareStats()` parameter `brunnermunzel_workaround` to `edge_cases_workaround`
|
|
4
|
+
and extended its effect on t-test.
|
|
5
|
+
- Fix `_at_least_one_differs` flag handling in `CompareStatsResult::updatePvalStats()`.
|
|
6
|
+
|
|
1
7
|
# 3.3.1-.2
|
|
2
8
|
- Changed the order of default metrics of `qbench.showBench()` to make `mean` the leading metric, as
|
|
3
9
|
it's typically the most correct one.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "benchstats"
|
|
7
|
-
version = "3.3.
|
|
7
|
+
version = "3.3.3"
|
|
8
8
|
description = "Statistical Testing for Benchmark Results Comparison"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [{ name = "Aleksei Rechinskii", email = "5dpea9nhd@mozmail.com" }]
|
|
@@ -162,6 +162,7 @@ class CompareStatsResult:
|
|
|
162
162
|
assert isinstance(other, CompareStatsResult)
|
|
163
163
|
assert other.method == self.method and other.alpha == self.alpha
|
|
164
164
|
assert self._results.keys() == other._results.keys()
|
|
165
|
+
self._at_least_one_differs = self._at_least_one_differs or other._at_least_one_differs
|
|
165
166
|
for bm, omdict in other._results.items():
|
|
166
167
|
mdict = self._results[bm]
|
|
167
168
|
assert mdict.keys() == omdict.keys()
|
|
@@ -249,7 +250,7 @@ def compareStats(
|
|
|
249
250
|
main_metrics: None | list[str] | tuple[str] = None,
|
|
250
251
|
debug_log: None | bool | LoggingConsole = True,
|
|
251
252
|
store_sets: bool = False,
|
|
252
|
-
|
|
253
|
+
edge_cases_workaround: None | bool = None,
|
|
253
254
|
) -> CompareStatsResult:
|
|
254
255
|
"""Perform comparison for statistical significance of benchmark results using a chosen
|
|
255
256
|
statistical method.
|
|
@@ -281,21 +282,22 @@ def compareStats(
|
|
|
281
282
|
`store_sets` is a bool, True will make store whole corresponding dataset into a BmCompResult
|
|
282
283
|
instead of a mean of the set.
|
|
283
284
|
|
|
284
|
-
`
|
|
285
|
+
`edge_cases_workaround`: Formally speaking, Brunner Munzel test method is "underspecified"
|
|
285
286
|
in a sense that by design it doesn't handle a case of non-intersecting sample sets, or sets
|
|
286
287
|
consisting of the same single element (https://aakinshin.net/posts/brunner-munzel-corner-case/).
|
|
287
288
|
scipy.stats.brunnermunzel() implementation at least in versions 1.10-1.15.2 directly follows the
|
|
288
289
|
words of the original paper and doesn't introduce handling for that cases, which causes warnings
|
|
289
290
|
and nans to appear in results. IMO from an engineering point of view this is more like just a
|
|
290
|
-
bug (https://github.com/scipy/scipy/issues/22664).
|
|
291
|
-
handle it: value of None handles the bug only for
|
|
292
|
-
controls whether to handle the bug irrespective of the method
|
|
293
|
-
pvalues for other tests, but this shouldn't affect inference
|
|
294
|
-
value is too wild).
|
|
291
|
+
bug (https://github.com/scipy/scipy/issues/22664). The same issue also applies to Student's
|
|
292
|
+
t-test. `edge_cases_workaround` is a flag to handle it: value of None handles the bug only for
|
|
293
|
+
affected methods, a bool otherwise controls whether to handle the bug irrespective of the method
|
|
294
|
+
used (this might give different pvalues for other tests, but this shouldn't affect inference
|
|
295
|
+
results unless alpha threshold value is too wild).
|
|
296
|
+
It should be noted that for small sample sizes (under 10 elements in any of
|
|
295
297
|
sample sets) this most certainly leads to a wrong p-value result that underestimate a
|
|
296
298
|
probability of error. However, with 10, or preferably more, sample set sizes, this should be
|
|
297
299
|
negligible.
|
|
298
|
-
While
|
|
300
|
+
While edge_cases_workaround allows to disable the workaround, I doubt it has any practical
|
|
299
301
|
value in the context of benchmark results comparison.
|
|
300
302
|
|
|
301
303
|
Returns an instance of CompareStatsResult class
|
|
@@ -306,7 +308,7 @@ def compareStats(
|
|
|
306
308
|
assert isinstance(sg0, dict) and (isinstance(sg1, dict) or (with_alternatives and sg1 is None))
|
|
307
309
|
assert isinstance(method, str) and method in kMethods, "unsupported method"
|
|
308
310
|
assert isinstance(alpha, kAllowedFpTypes) and 0 < alpha and alpha < 0.5
|
|
309
|
-
assert
|
|
311
|
+
assert edge_cases_workaround is None or isinstance(edge_cases_workaround, bool)
|
|
310
312
|
assert main_metrics is None or isinstance(main_metrics, (list, tuple))
|
|
311
313
|
|
|
312
314
|
if debug_log is None or (isinstance(debug_log, bool) and not debug_log):
|
|
@@ -317,8 +319,8 @@ def compareStats(
|
|
|
317
319
|
logger = debug_log
|
|
318
320
|
debug_log = True
|
|
319
321
|
|
|
320
|
-
if
|
|
321
|
-
|
|
322
|
+
if edge_cases_workaround is None:
|
|
323
|
+
edge_cases_workaround = (method in ("brunnermunzel", "ttest_ind"))
|
|
322
324
|
|
|
323
325
|
def warn(*args, **kwargs):
|
|
324
326
|
if debug_log:
|
|
@@ -330,11 +332,11 @@ def compareStats(
|
|
|
330
332
|
|
|
331
333
|
def computePValues(s0, s1):
|
|
332
334
|
"""Computes and return pvalues of s0 being stochastically less or greater than s1"""
|
|
333
|
-
if
|
|
335
|
+
if edge_cases_workaround:
|
|
334
336
|
# test for edge cases that brunnermunzel can't handle properly
|
|
335
337
|
mn0, mx0, mn1, mx1 = np.min(s0), np.max(s0), np.min(s1), np.max(s1)
|
|
336
338
|
assert np.all(np.isfinite([mn0, mx0, mn1, mx1])) # sanity check, more asserts below
|
|
337
|
-
all_eq =
|
|
339
|
+
all_eq = all([mn0 == mx0, mn0 == mn1, mn0 == mx1])
|
|
338
340
|
all_less, all_greater = mx0 < mn1, mn0 > mx1
|
|
339
341
|
|
|
340
342
|
if all_eq:
|
|
@@ -348,7 +350,7 @@ def compareStats(
|
|
|
348
350
|
res_greater = stat_func(s0, s1, alternative="greater")
|
|
349
351
|
|
|
350
352
|
less_pvalue, greater_pvalue = res_less.pvalue, res_greater.pvalue
|
|
351
|
-
if
|
|
353
|
+
if edge_cases_workaround:
|
|
352
354
|
assert np.all(np.isfinite([less_pvalue, greater_pvalue]))
|
|
353
355
|
|
|
354
356
|
return less_pvalue, greater_pvalue
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import sys
|
|
2
1
|
from typing import Iterable
|
|
3
2
|
import unittest
|
|
4
3
|
import numpy as np
|
|
5
|
-
import pytest
|
|
6
4
|
import time
|
|
7
5
|
import traceback
|
|
8
6
|
|
|
@@ -232,7 +230,7 @@ class TestQBench(unittest.TestCase):
|
|
|
232
230
|
check_reset_counters(7 * 2, 7 * 2, 7 * 2, 0, 7 + 14)
|
|
233
231
|
|
|
234
232
|
def test_showBench_with_delimiter(self):
|
|
235
|
-
def_prms = {"alt_delimiter": "|", "render_report": False}
|
|
233
|
+
def_prms = {"alt_delimiter": "|", "render_report": False, "show_progress_each": 0}
|
|
236
234
|
nreps = 10
|
|
237
235
|
results = np.stack(
|
|
238
236
|
(
|
|
@@ -252,7 +250,7 @@ class TestQBench(unittest.TestCase):
|
|
|
252
250
|
assert "~" == sr.results["alg2 | A vs B"]["mean"].result
|
|
253
251
|
|
|
254
252
|
def test_showBench_one_name(self):
|
|
255
|
-
def_prms = {"render_report": False}
|
|
253
|
+
def_prms = {"render_report": False, "show_progress_each": 0}
|
|
256
254
|
nreps = 10
|
|
257
255
|
results = np.stack(
|
|
258
256
|
(
|
|
@@ -268,7 +266,7 @@ class TestQBench(unittest.TestCase):
|
|
|
268
266
|
assert ">" == sr.results["code | 0 vs 2"]["mean"].result
|
|
269
267
|
|
|
270
268
|
def test_showBench_tuple(self):
|
|
271
|
-
def_prms = {"render_report": False}
|
|
269
|
+
def_prms = {"render_report": False, "show_progress_each": 0}
|
|
272
270
|
nreps = 10
|
|
273
271
|
results = np.stack(
|
|
274
272
|
(
|
|
@@ -295,6 +293,7 @@ class TestQBench(unittest.TestCase):
|
|
|
295
293
|
alpha=0.499,
|
|
296
294
|
render_report=False,
|
|
297
295
|
console=None,
|
|
296
|
+
show_progress_each=0,
|
|
298
297
|
)
|
|
299
298
|
# seed is chosen to modify the first bootstrap result and produce the same second result
|
|
300
299
|
|
|
@@ -306,12 +305,20 @@ class TestQBench(unittest.TestCase):
|
|
|
306
305
|
assert len(pvs["~"]) == same_len
|
|
307
306
|
assert all(pvs["~"][i] == 1.0 for i in range(same_len))
|
|
308
307
|
|
|
308
|
+
cr = qb.showBench(r, pvalue_stats_bootstrap=0, **cmnargs)
|
|
309
|
+
assert not cr.at_least_one_differs
|
|
310
|
+
|
|
309
311
|
cr = qb.showBench(r, pvalue_stats_bootstrap=1, **cmnargs)
|
|
312
|
+
assert cr.at_least_one_differs
|
|
310
313
|
test_pvs(next(iter(next(iter(cr.pval_stats.values())).values())), 1)
|
|
311
314
|
|
|
312
315
|
cr = qb.showBench(r, pvalue_stats_bootstrap=2, **cmnargs)
|
|
316
|
+
assert cr.at_least_one_differs
|
|
313
317
|
test_pvs(next(iter(next(iter(cr.pval_stats.values())).values())), 2)
|
|
314
318
|
|
|
315
319
|
|
|
316
320
|
if __name__ == "__main__":
|
|
321
|
+
import sys
|
|
322
|
+
import pytest
|
|
323
|
+
|
|
317
324
|
sys.exit(pytest.main(sys.argv))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|