Skip to content

Commit 19fc355

Browse files
authored
Rename horizon to auto sample conditions (#225)
I never liked the name "horizons" (except on the day I chose it I guess). I think a much clearer name is "auto sample conditions". This changes the `--horizon` flag and the `horizons` JSON config setting (yes... one was singular, the other plural) to `--auto-sample-conditions` and `autoSampleConditions` respectively. It's backwards compatible, with a warning printed if you use the old style. Related to #221
1 parent d110258 commit 19fc355

13 files changed

+256
-100
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ project adheres to [Semantic Versioning](http://semver.org/).
77

88
## Unreleased
99

10+
- `--horizons` flag and `horizon` config setting has been replaced with the
11+
`--auto-sample-conditions` and `autoSampleConditions`. `--horizon` will
12+
continue to work for backwards compatibility, but please do update to the new
13+
name.
14+
1015
- Copyright notice owner changed from "The Polymer Project Authors" to "Google
1116
LLC". Trivial reformatting for `LICENSE` file to match spdx.org version.
1217
Source license headers replaced with concise SPDX-style.

README.md

+35-32
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ By default, a **minimum of 50 samples** are taken from **each** benchmark. You
120120
can change the minimum sample size with the `--sample-size` flag or the
121121
`sampleSize` JSON config option.
122122

123-
### Auto sampling
123+
### Auto sample
124124

125125
After the initial 50 samples, tachometer will continue taking samples until
126126
there is a clear statistically significant difference between all benchmarks,
@@ -130,33 +130,36 @@ You can change this duration with the `--timeout` flag or the `timeout` JSON
130130
config option, measured in minutes. Set `--timeout=0` to disable auto sampling
131131
entirely. Set `--timeout=60` to sample for up to an hour.
132132

133-
### Horizons
133+
### Auto sample conditions
134134

135135
You can also configure which statistical conditions tachometer should check for
136-
when deciding when to stop auto sampling by configuring _horizons_.
136+
when deciding when to stop auto sampling by configuring _auto sample
137+
conditions_.
137138

138-
To set horizons from the command-line, use the `--horizon` flag with a
139-
comma-delimited list:
139+
To set auto sample conditions from the command-line, use the
140+
`--auto-sample-conditions` flag with a comma-delimited list:
140141

141142
```sh
142-
--horizon=0%,10%
143+
--auto-sample-conditions=0%,10%
143144
```
144145

145-
To set horizons from a JSON config file, use the `horizons` property with an
146-
array of strings (including if there is only one condition):
146+
To set auto sample conditions from a JSON config file, use the
147+
`autoSampleConditions` property with an array of strings (including if there is
148+
only one condition):
147149

148150
```json
149151
{
150-
"horizons": ["0%", "10%"]
152+
"autoSampleConditions": ["0%", "10%"]
151153
}
152154
```
153155

154-
A horizon can be thought of as a point of interest on the number-line of either
155-
absolute milliseconds, or relative percent change. By setting a horizon, you are
156-
asking tachometer to try to shrink the confidence interval until it is
157-
unambiguously placed on one side or the other of that horizon.
156+
An auto sample condition can be thought of as a point of interest on the
157+
number-line of either absolute milliseconds, or relative percent change. By
158+
setting a condition, you are asking tachometer to try to shrink the confidence
159+
interval until it is unambiguously placed on one side or the other of that
160+
condition.
158161

159-
| Example horizon | Question |
162+
| Example condition | Question |
160163
| ------------------- | ---------------------------------------------------------- |
161164
| `0%` | Is A faster or slower than B _at all_? (The **default**) |
162165
| `10%` | Is A faster or slower than B by at least 10%? |
@@ -166,24 +169,24 @@ unambiguously placed on one side or the other of that horizon.
166169
| `0%`, `10%`, `100%` | Is A at all, a little, or a lot slower or faster than B? |
167170
| `0.5ms` | Is A faster or slower than B by at least 0.5 milliseconds? |
168171

169-
In the following example, we have set `--horizon=10%`, meaning we are interested
170-
in knowing whether A differs from B by at least 10% in either direction. The
171-
sample size automatically increases until the confidence interval is narrow
172-
enough to place the estimated difference squarely on one side or the other of
173-
both horizons.
172+
In the following example, we have set `--auto-sample-conditions=10%`, meaning we
173+
are interested in knowing whether A differs from B by at least 10% in either
174+
direction. The sample size automatically increases until the confidence interval
175+
is narrow enough to place the estimated difference squarely on one side or the
176+
other of both conditions.
174177

175178
```
176-
<-------------------------------> n=50 -10% +10%
177-
<------------------> n=100 ✔️ -10% +10%
179+
<-------------------------------> n=50 X -10% X +10%
180+
<------------------> n=100 ✔️ -10% X +10%
178181
<-----> n=200 ✔️ -10% ✔️ +10%
179182
180183
|---------|---------|---------|---------| difference in runtime
181184
-20% -10% 0 +10% +20%
182185
183-
n = sample size
184-
<---> = confidence interval for percent difference of mean runtimes
185-
✔️ = resolved horizon
186-
= unresolved horizon
186+
n = sample size
187+
<--> = confidence interval for percent difference of mean runtimes
188+
✔️ = resolved condition
189+
X = unresolved condition
187190
```
188191

189192
In this example, by `n=50` we are not sure whether A is faster or slower than B
@@ -192,10 +195,10 @@ than 10%, but we're still not sure if it's _slower_ by more than 10%. By `n=200`
192195
we have also ruled out that B is slower than A by more than 10%, so we stop
193196
sampling. Note that we still don't know which is _absolutely_ faster, we just
194197
know that whatever the difference is, it is neither faster nor slower than 10%
195-
(and if we did want to know, we could add `0` to our horizons).
198+
(and if we did want to know, we could add `0` to our conditions).
196199

197-
Note that, if the _actual_ difference is very close to a horizon, then it is
198-
likely that the horizon will never be met, and the timeout will expire.
200+
Note that, if the _actual_ difference is very close to a condition, then it is
201+
likely that the condition will never be met, and the timeout will expire.
199202

200203
## Measurement modes
201204

@@ -705,7 +708,7 @@ Defaults are the same as the corresponding command-line flags.
705708
"root": "./benchmarks",
706709
"sampleSize": 50,
707710
"timeout": 3,
708-
"horizons": ["0%", "1%"],
711+
"autoSampleConditions": ["0%", "1%"],
709712
"benchmarks": [
710713
{
711714
"name": "foo",
@@ -806,9 +809,9 @@ tach http://example.com
806809
| `--package-version` / `-p` | _(none)_ | Specify an NPM package version to swap in ([details](#swap-npm-dependencies)) |
807810
| `--browser` / `-b` | `chrome` | Which browsers to launch in automatic mode, comma-delimited (chrome, firefox, safari, edge, ie) ([details](#browsers)) |
808811
| `--window-size` | `1024,768` | "width,height" in pixels of the browser windows that will be created |
809-
| `--sample-size` / `-n` | `50` | Minimum number of times to run each benchmark ([details](#sample-size)] |
810-
| `--horizon` | `0%` | The degrees of difference to try and resolve when auto-sampling ("N%" or "Nms", comma-delimited) ([details](#auto-sampling)) |
811-
| `--timeout` | `3` | The maximum number of minutes to spend auto-sampling ([details](#auto-sampling)) |
812+
| `--sample-size` / `-n` | `50` | Minimum number of times to run each benchmark ([details](#sample-size)) |
813+
| `--auto-sample-conditions` | `0%` | The degrees of difference to try and resolve when auto-sampling ("N%" or "Nms", comma-delimited) ([details](#auto-sample-conditions)) |
814+
| `--timeout` | `3` | The maximum number of minutes to spend auto-sampling ([details](#auto-sample)) |
812815
| `--measure` | `callback` | Which time interval to measure (`callback`, `global`, `fcp`) ([details](#measurement-modes)) |
813816
| `--measurement-expression` | `window.tachometerResult` | JS expression to poll for on page to retrieve measurement result when `measure` setting is set to `global` |
814817
| `--remote-accessible-host` | matches `--host` | When using a browser over a remote WebDriver connection, the URL that those browsers should use to access the local tachometer server ([details](#remote-control)) |

config.schema.json

+8-1
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,13 @@
453453
"description": "An optional reference to the JSON Schema for this file.\n\nIf none is given, and the file is a valid tachometer config file,\ntachometer will write back to the config file to give this a value.",
454454
"type": "string"
455455
},
456+
"autoSampleConditions": {
457+
"description": "The degrees of difference to try and resolve when auto-sampling\n(e.g. 0ms, +1ms, -1ms, 0%, +1%, -1%, default 0%).",
458+
"items": {
459+
"type": "string"
460+
},
461+
"type": "array"
462+
},
456463
"benchmarks": {
457464
"description": "Benchmarks to run.",
458465
"items": {
@@ -462,7 +469,7 @@
462469
"type": "array"
463470
},
464471
"horizons": {
465-
"description": "The degrees of difference to try and resolve when auto-sampling\n(e.g. 0ms, +1ms, -1ms, 0%, +1%, -1%, default 0%).",
472+
"description": "Deprecated alias for autoSampleConditions.",
466473
"items": {
467474
"type": "string"
468475
},

src/config.ts

+22-16
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import * as defaults from './defaults';
1313
import {Opts} from './flags';
1414
import {CheckConfig, parseGithubCheckFlag} from './github';
1515
import {specsFromOpts} from './specs';
16-
import {Horizons} from './stats';
16+
import {AutoSampleConditions} from './stats';
1717
import {BenchmarkSpec} from './types';
1818
import {fileKind} from './util';
1919

@@ -25,7 +25,7 @@ export interface Config {
2525
sampleSize: number;
2626
timeout: number;
2727
benchmarks: BenchmarkSpec[];
28-
horizons: Horizons;
28+
autoSampleConditions: AutoSampleConditions;
2929
mode: 'automatic' | 'manual';
3030
jsonFile: string;
3131
// TODO(aomarks) Remove this in next major version.
@@ -69,8 +69,10 @@ export async function makeConfig(opts: Opts): Promise<Config> {
6969
if (opts.timeout !== undefined) {
7070
throw new Error('--timeout cannot be specified when using --config');
7171
}
72-
if (opts.horizon !== undefined) {
73-
throw new Error('--horizon cannot be specified when using --config');
72+
if (opts['auto-sample-conditions'] !== undefined) {
73+
throw new Error(
74+
'--auto-sample-conditions cannot be specified when using --config'
75+
);
7476
}
7577
if (opts.measure !== undefined) {
7678
throw new Error('--measure cannot be specified when using --config');
@@ -98,9 +100,9 @@ export async function makeConfig(opts: Opts): Promise<Config> {
98100
root: opts.root,
99101
sampleSize: opts['sample-size'],
100102
timeout: opts.timeout,
101-
horizons:
102-
opts.horizon !== undefined
103-
? parseHorizons(opts.horizon.split(','))
103+
autoSampleConditions:
104+
opts['auto-sample-conditions'] !== undefined
105+
? parseAutoSampleConditions(opts['auto-sample-conditions'].split(','))
104106
: undefined,
105107
benchmarks: await specsFromOpts(opts),
106108
resolveBareModules: opts['resolve-bare-modules'],
@@ -148,10 +150,10 @@ export function applyDefaults(partial: Partial<Config>): Config {
148150
? partial.forceCleanNpmInstall
149151
: defaults.forceCleanNpmInstall,
150152
githubCheck: partial.githubCheck,
151-
horizons:
152-
partial.horizons !== undefined
153-
? partial.horizons
154-
: parseHorizons([...defaults.horizons]),
153+
autoSampleConditions:
154+
partial.autoSampleConditions !== undefined
155+
? partial.autoSampleConditions
156+
: parseAutoSampleConditions([...defaults.autoSampleConditions]),
155157
jsonFile: partial.jsonFile !== undefined ? partial.jsonFile : '',
156158
legacyJsonFile:
157159
partial.legacyJsonFile !== undefined ? partial.legacyJsonFile : '',
@@ -209,13 +211,17 @@ export async function urlFromLocalPath(
209211
return urlPath;
210212
}
211213

212-
/** Parse horizon flags into signed horizon values. */
213-
export function parseHorizons(strs: string[]): Horizons {
214+
/**
215+
* Parse auto sample condition strings.
216+
*/
217+
export function parseAutoSampleConditions(
218+
strs: string[]
219+
): AutoSampleConditions {
214220
const absolute = new Set<number>();
215221
const relative = new Set<number>();
216222
for (const str of strs) {
217223
if (!str.match(/^[-+]?(\d*\.)?\d+(ms|%)$/)) {
218-
throw new Error(`Invalid horizon ${str}`);
224+
throw new Error(`Invalid auto sample condition ${str}`);
219225
}
220226

221227
let num;
@@ -232,10 +238,10 @@ export function parseHorizons(strs: string[]): Horizons {
232238

233239
if (str.startsWith('+') || str.startsWith('-') || num === 0) {
234240
// If the sign was explicit (e.g. "+0.1", "-0.1") then we're only
235-
// interested in that signed horizon.
241+
// interested in that signed condition.
236242
absOrRel.add(num);
237243
} else {
238-
// Otherwise (e.g. "0.1") we're interested in the horizon as a
244+
// Otherwise (e.g. "0.1") we're interested in the condition as a
239245
// difference in either direction.
240246
absOrRel.add(-num);
241247
absOrRel.add(num);

src/configfile.ts

+22-4
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
parseBrowserConfigString,
1616
validateBrowserConfig,
1717
} from './browser';
18-
import {Config, parseHorizons, urlFromLocalPath} from './config';
18+
import {Config, parseAutoSampleConditions, urlFromLocalPath} from './config';
1919
import * as defaults from './defaults';
2020
import {makeUniqueSpecLabelFn} from './format';
2121
import {
@@ -53,6 +53,11 @@ export interface ConfigFile {
5353
* The degrees of difference to try and resolve when auto-sampling
5454
* (e.g. 0ms, +1ms, -1ms, 0%, +1%, -1%, default 0%).
5555
*/
56+
autoSampleConditions?: string[];
57+
58+
/**
59+
* Deprecated alias for autoSampleConditions.
60+
*/
5661
horizons?: string[];
5762

5863
/**
@@ -342,13 +347,26 @@ export async function parseConfigFile(
342347
}
343348
}
344349

350+
if (validated.horizons !== undefined) {
351+
if (validated.autoSampleConditions !== undefined) {
352+
throw new Error(
353+
'Please use only "autoSampleConditions" and not "horizons".'
354+
);
355+
}
356+
console.warn(
357+
'\nNOTE: The "horizons" setting has been renamed to "autoSampleConditions".\n' +
358+
'Please rename it.\n'
359+
);
360+
validated.autoSampleConditions = validated.horizons;
361+
}
362+
345363
return {
346364
root,
347365
sampleSize: validated.sampleSize,
348366
timeout: validated.timeout,
349-
horizons:
350-
validated.horizons !== undefined
351-
? parseHorizons(validated.horizons)
367+
autoSampleConditions:
368+
validated.autoSampleConditions !== undefined
369+
? parseAutoSampleConditions(validated.autoSampleConditions)
352370
: undefined,
353371
benchmarks,
354372
resolveBareModules: validated.resolveBareModules,

src/defaults.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export const browserName: BrowserName = 'chrome';
1515
export const headless = false;
1616
export const sampleSize = 50;
1717
export const timeout = 3;
18-
export const horizons = ['0%'] as const;
18+
export const autoSampleConditions = ['0%'] as const;
1919
export const mode = 'automatic';
2020
export const resolveBareModules = true;
2121
export const forceCleanNpmInstall = false;

src/flags.ts

+30-5
Original file line numberDiff line numberDiff line change
@@ -170,11 +170,16 @@ export const optDefs: commandLineUsage.OptionDefinition[] = [
170170
defaultValue: defaults.measurementExpression,
171171
},
172172
{
173-
name: 'horizon',
173+
name: 'auto-sample-conditions',
174174
description:
175175
'The degrees of difference to try and resolve when auto-sampling ' +
176176
'(milliseconds, comma-delimited, optionally signed, ' +
177-
`default ${defaults.horizons.join(',')})`,
177+
`default ${defaults.autoSampleConditions.join(',')})`,
178+
type: String,
179+
},
180+
{
181+
name: 'horizon',
182+
description: 'Deprecated alias for --auto-sample-conditions',
178183
type: String,
179184
},
180185
{
@@ -241,7 +246,7 @@ export interface Opts {
241246
save: string;
242247
measure: CommandLineMeasurements | undefined;
243248
'measurement-expression': string | undefined;
244-
horizon: string | undefined;
249+
'auto-sample-conditions': string | undefined;
245250
timeout: number | undefined;
246251
'github-check': string;
247252
'resolve-bare-modules': boolean | undefined;
@@ -265,6 +270,10 @@ export interface Opts {
265270
_unknown?: string[];
266271
}
267272

273+
interface OptsWithDeprecated extends Opts {
274+
horizon: string | undefined;
275+
}
276+
268277
/**
269278
* Boolean flags that default to true are not supported
270279
* (https://github.com/75lb/command-line-args/issues/71).
@@ -286,7 +295,10 @@ function booleanString(flagName: string): (str: string) => boolean {
286295
* Parse the given CLI argument list.
287296
*/
288297
export function parseFlags(argv: string[]): Opts {
289-
const opts = commandLineArgs(optDefs, {partial: true, argv}) as Opts;
298+
const opts = commandLineArgs(optDefs, {
299+
partial: true,
300+
argv,
301+
}) as OptsWithDeprecated;
290302
// Note that when a flag is used but not set to a value (i.e. "tachometer
291303
// --resolve-bare-modules ..."), then the type function is not invoked, and
292304
// the value will be null. Since in default-false cases (which aren't
@@ -295,5 +307,18 @@ export function parseFlags(argv: string[]): Opts {
295307
if (opts['resolve-bare-modules'] === null) {
296308
opts['resolve-bare-modules'] = true;
297309
}
298-
return opts;
310+
if (opts['horizon']) {
311+
if (opts['auto-sample-conditions']) {
312+
throw new Error(
313+
'Please use only --auto-sample-conditions and not --horizons.'
314+
);
315+
}
316+
console.warn(
317+
'\nNOTE: The --horizon flag has been renamed to --auto-sample-conditions.\n' +
318+
'Please use --auto-sample-conditions going forward.\n'
319+
);
320+
opts['auto-sample-conditions'] = opts['horizon'];
321+
delete opts['horizon'];
322+
}
323+
return opts as Opts;
299324
}

0 commit comments

Comments
 (0)