perfshield 0.0.1
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.
- package/LICENSE +21 -0
- package/README.md +126 -0
- package/examples/simple/README.md +19 -0
- package/examples/simple/bench-source.js +13 -0
- package/examples/simple/build.mjs +17 -0
- package/examples/simple/perfshield.config.json +24 -0
- package/lib/artifacts.js +19 -0
- package/lib/build.js +27 -0
- package/lib/cli.js +74 -0
- package/lib/config.js +328 -0
- package/lib/engines/node-harness.js +217 -0
- package/lib/engines/node.js +214 -0
- package/lib/regression.js +17 -0
- package/lib/report/console.js +25 -0
- package/lib/report/index.js +13 -0
- package/lib/report/json.js +12 -0
- package/lib/runner.js +177 -0
- package/lib/stats.js +108 -0
- package/lib/types.js +0 -0
- package/package.json +43 -0
package/lib/stats.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import jstat from "jstat";
|
|
2
|
+
const sumOf = values => values.reduce((total, value) => total + value, 0);
|
|
3
|
+
const squareResiduals = (values, mean) => values.map(value => {
|
|
4
|
+
const diff = value - mean;
|
|
5
|
+
return diff * diff;
|
|
6
|
+
});
|
|
7
|
+
export const confidenceInterval95 = (distribution, size) => {
|
|
8
|
+
if (size <= 1) {
|
|
9
|
+
return {
|
|
10
|
+
high: distribution.mean,
|
|
11
|
+
low: distribution.mean
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
const t = jstat.studentt.inv(1 - 0.05 / 2, size - 1);
|
|
15
|
+
const stdDev = Math.sqrt(distribution.variance);
|
|
16
|
+
const margin = t * stdDev;
|
|
17
|
+
return {
|
|
18
|
+
high: distribution.mean + margin,
|
|
19
|
+
low: distribution.mean - margin
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
export const samplingDistributionOfTheMean = (distribution, sampleSize) => ({
|
|
23
|
+
mean: distribution.mean,
|
|
24
|
+
variance: distribution.variance / sampleSize
|
|
25
|
+
});
|
|
26
|
+
export const samplingDistributionOfAbsoluteDifferenceOfMeans = (a, b) => ({
|
|
27
|
+
mean: b.mean - a.mean,
|
|
28
|
+
variance: a.variance + b.variance
|
|
29
|
+
});
|
|
30
|
+
export const samplingDistributionOfRelativeDifferenceOfMeans = (a, b) => ({
|
|
31
|
+
mean: (b.mean - a.mean) / a.mean,
|
|
32
|
+
variance: (a.variance * b.mean * b.mean + b.variance * a.mean * a.mean) / (a.mean * a.mean * a.mean * a.mean)
|
|
33
|
+
});
|
|
34
|
+
export const summaryStats = values => {
|
|
35
|
+
if (values.length === 0) {
|
|
36
|
+
throw new Error("Cannot compute stats for an empty sample set.");
|
|
37
|
+
}
|
|
38
|
+
const size = values.length;
|
|
39
|
+
const mean = sumOf(values) / size;
|
|
40
|
+
const residuals = squareResiduals(values, mean);
|
|
41
|
+
const variance = size > 1 ? sumOf(residuals) / (size - 1) : 0;
|
|
42
|
+
const standardDeviation = Math.sqrt(variance);
|
|
43
|
+
const relativeStandardDeviation = mean === 0 ? 0 : standardDeviation / mean;
|
|
44
|
+
return {
|
|
45
|
+
mean,
|
|
46
|
+
meanCI: confidenceInterval95(samplingDistributionOfTheMean({
|
|
47
|
+
mean,
|
|
48
|
+
variance
|
|
49
|
+
}, size), size),
|
|
50
|
+
relativeStandardDeviation,
|
|
51
|
+
size,
|
|
52
|
+
standardDeviation,
|
|
53
|
+
variance
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
export const computeDifference = (baseline, current) => {
|
|
57
|
+
if (baseline.mean === 0) {
|
|
58
|
+
throw new Error("Cannot compute relative difference with baseline mean 0.");
|
|
59
|
+
}
|
|
60
|
+
const baselineDist = samplingDistributionOfTheMean({
|
|
61
|
+
mean: baseline.mean,
|
|
62
|
+
variance: baseline.variance
|
|
63
|
+
}, baseline.size);
|
|
64
|
+
const currentDist = samplingDistributionOfTheMean({
|
|
65
|
+
mean: current.mean,
|
|
66
|
+
variance: current.variance
|
|
67
|
+
}, current.size);
|
|
68
|
+
const absoluteDist = samplingDistributionOfAbsoluteDifferenceOfMeans(baselineDist, currentDist);
|
|
69
|
+
const relativeDist = samplingDistributionOfRelativeDifferenceOfMeans(baselineDist, currentDist);
|
|
70
|
+
const size = Math.min(baseline.size, current.size);
|
|
71
|
+
return {
|
|
72
|
+
absolute: {
|
|
73
|
+
ci: confidenceInterval95(absoluteDist, size),
|
|
74
|
+
mean: absoluteDist.mean
|
|
75
|
+
},
|
|
76
|
+
relative: {
|
|
77
|
+
ci: confidenceInterval95(relativeDist, size),
|
|
78
|
+
mean: relativeDist.mean
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
export const computeDifferences = stats => stats.map(result => ({
|
|
83
|
+
...result,
|
|
84
|
+
differences: stats.map(other => other === result ? null : computeDifference(other.stats, result.stats))
|
|
85
|
+
}));
|
|
86
|
+
const intervalContains = (interval, value) => interval.low <= value && value <= interval.high;
|
|
87
|
+
export const autoSampleConditionsResolved = (resultStats, conditions) => {
|
|
88
|
+
for (const {
|
|
89
|
+
differences
|
|
90
|
+
} of resultStats) {
|
|
91
|
+
for (const diff of differences) {
|
|
92
|
+
if (diff == null) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
for (const condition of conditions.absolute) {
|
|
96
|
+
if (intervalContains(diff.absolute.ci, condition)) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
for (const condition of conditions.relative) {
|
|
101
|
+
if (intervalContains(diff.relative.ci, condition)) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return true;
|
|
108
|
+
};
|
package/lib/types.js
ADDED
|
File without changes
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "perfshield",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "A tool for doing web benchmarking across multiple JS engines and with statistical signifigance",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"perfshield": "lib/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"lib/**",
|
|
12
|
+
"README.md",
|
|
13
|
+
"examples/**"
|
|
14
|
+
],
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"jstat": "1.9.6"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@babel/cli": "^7.28.3",
|
|
20
|
+
"@babel/core": "^7.28.5",
|
|
21
|
+
"@babel/preset-flow": "^7.27.1",
|
|
22
|
+
"@ianvs/prettier-plugin-sort-imports": "^4.7.0",
|
|
23
|
+
"@nkzw/eslint-config": "^3.2.1",
|
|
24
|
+
"@prettier/plugin-hermes": "^0.1.3",
|
|
25
|
+
"babel-plugin-syntax-hermes-parser": "^0.33.0",
|
|
26
|
+
"babel-jest": "^30.0.5",
|
|
27
|
+
"eslint": "^9.33.0",
|
|
28
|
+
"flow-typed": "^4.1.1",
|
|
29
|
+
"flow-bin": "^0.295.0",
|
|
30
|
+
"hermes-eslint": "^0.33.0",
|
|
31
|
+
"jest": "^30.0.5"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"bench:compare": "node src/cli.js compare",
|
|
35
|
+
"bench:prepare": "node src/cli.js prepare",
|
|
36
|
+
"build": "rm -rf lib && babel src --out-dir lib --extensions .js,.jsx",
|
|
37
|
+
"flow-typed": "flow-typed install",
|
|
38
|
+
"format": "prettier . --write",
|
|
39
|
+
"flow": "flow check",
|
|
40
|
+
"lint": "eslint . --ext .js,.jsx --no-error-on-unmatched-pattern",
|
|
41
|
+
"test": "NODE_OPTIONS=--experimental-vm-modules jest --passWithNoTests"
|
|
42
|
+
}
|
|
43
|
+
}
|