@buoy-gg/benchmark 2.1.3 → 2.1.4-beta.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/lib/commonjs/benchmarking/BenchmarkComparator.js +221 -1
- package/lib/commonjs/benchmarking/BenchmarkRecorder.js +497 -1
- package/lib/commonjs/benchmarking/BenchmarkStorage.js +235 -1
- package/lib/commonjs/benchmarking/index.js +83 -1
- package/lib/commonjs/benchmarking/types.js +13 -1
- package/lib/commonjs/components/BenchmarkCompareView.js +475 -1
- package/lib/commonjs/components/BenchmarkDetailView.js +346 -1
- package/lib/commonjs/components/BenchmarkModal.js +505 -1
- package/lib/commonjs/components/BenchmarkSessionCard.js +193 -1
- package/lib/commonjs/index.js +62 -1
- package/lib/commonjs/preset.js +86 -1
- package/lib/module/benchmarking/BenchmarkComparator.js +216 -1
- package/lib/module/benchmarking/BenchmarkRecorder.js +493 -1
- package/lib/module/benchmarking/BenchmarkStorage.js +227 -1
- package/lib/module/benchmarking/index.js +48 -1
- package/lib/module/benchmarking/types.js +13 -1
- package/lib/module/components/BenchmarkCompareView.js +469 -1
- package/lib/module/components/BenchmarkDetailView.js +340 -1
- package/lib/module/components/BenchmarkModal.js +499 -1
- package/lib/module/components/BenchmarkSessionCard.js +187 -1
- package/lib/module/index.js +39 -1
- package/lib/module/preset.js +81 -1
- package/lib/typescript/benchmarking/BenchmarkComparator.d.ts.map +1 -0
- package/lib/typescript/benchmarking/BenchmarkRecorder.d.ts.map +1 -0
- package/lib/typescript/benchmarking/BenchmarkStorage.d.ts.map +1 -0
- package/lib/typescript/benchmarking/index.d.ts.map +1 -0
- package/lib/typescript/benchmarking/types.d.ts.map +1 -0
- package/lib/typescript/components/BenchmarkCompareView.d.ts.map +1 -0
- package/lib/typescript/components/BenchmarkDetailView.d.ts.map +1 -0
- package/lib/typescript/components/BenchmarkModal.d.ts.map +1 -0
- package/lib/typescript/components/BenchmarkSessionCard.d.ts.map +1 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/preset.d.ts.map +1 -0
- package/package.json +2 -2
|
@@ -1 +1,193 @@
|
|
|
1
|
-
"use strict";
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.BenchmarkSessionCard = BenchmarkSessionCard;
|
|
7
|
+
exports.default = void 0;
|
|
8
|
+
var _react = _interopRequireDefault(require("react"));
|
|
9
|
+
var _reactNative = require("react-native");
|
|
10
|
+
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
11
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
12
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
13
|
+
/**
|
|
14
|
+
* BenchmarkSessionCard
|
|
15
|
+
*
|
|
16
|
+
* Displays a single benchmark report as a card with key metrics.
|
|
17
|
+
* Shows name, duration, batch count, and timing stats.
|
|
18
|
+
* Supports selection for comparison.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Format duration in human-readable form
|
|
23
|
+
*/
|
|
24
|
+
function formatDuration(ms) {
|
|
25
|
+
if (ms < 1000) {
|
|
26
|
+
return `${ms.toFixed(0)}ms`;
|
|
27
|
+
}
|
|
28
|
+
if (ms < 60000) {
|
|
29
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
30
|
+
}
|
|
31
|
+
const minutes = Math.floor(ms / 60000);
|
|
32
|
+
const seconds = (ms % 60000 / 1000).toFixed(0);
|
|
33
|
+
return `${minutes}m ${seconds}s`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Format date in compact form
|
|
38
|
+
*/
|
|
39
|
+
function formatDate(timestamp) {
|
|
40
|
+
const date = new Date(timestamp);
|
|
41
|
+
const now = new Date();
|
|
42
|
+
const diffMs = now.getTime() - date.getTime();
|
|
43
|
+
const diffMins = Math.floor(diffMs / 60000);
|
|
44
|
+
const diffHours = Math.floor(diffMs / 3600000);
|
|
45
|
+
const diffDays = Math.floor(diffMs / 86400000);
|
|
46
|
+
if (diffMins < 1) return "Just now";
|
|
47
|
+
if (diffMins < 60) return `${diffMins}m ago`;
|
|
48
|
+
if (diffHours < 24) return `${diffHours}h ago`;
|
|
49
|
+
if (diffDays < 7) return `${diffDays}d ago`;
|
|
50
|
+
return date.toLocaleDateString(undefined, {
|
|
51
|
+
month: "short",
|
|
52
|
+
day: "numeric"
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function BenchmarkSessionCard({
|
|
56
|
+
metadata,
|
|
57
|
+
isSelected = false,
|
|
58
|
+
onPress,
|
|
59
|
+
onLongPress,
|
|
60
|
+
selectionMode = false
|
|
61
|
+
}) {
|
|
62
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
|
|
63
|
+
onPress: onPress,
|
|
64
|
+
onLongPress: onLongPress,
|
|
65
|
+
style: [styles.card, isSelected && styles.cardSelected],
|
|
66
|
+
activeOpacity: 0.7,
|
|
67
|
+
children: [selectionMode && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
68
|
+
style: [styles.checkbox, isSelected && styles.checkboxSelected],
|
|
69
|
+
children: isSelected && /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Check, {
|
|
70
|
+
size: 12,
|
|
71
|
+
color: "#fff"
|
|
72
|
+
})
|
|
73
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
74
|
+
style: styles.content,
|
|
75
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
76
|
+
style: styles.header,
|
|
77
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
78
|
+
style: styles.name,
|
|
79
|
+
numberOfLines: 1,
|
|
80
|
+
children: metadata.name
|
|
81
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
82
|
+
style: styles.date,
|
|
83
|
+
children: formatDate(metadata.createdAt)
|
|
84
|
+
})]
|
|
85
|
+
}), metadata.description && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
86
|
+
style: styles.description,
|
|
87
|
+
numberOfLines: 1,
|
|
88
|
+
children: metadata.description
|
|
89
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
90
|
+
style: styles.statsRow,
|
|
91
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
92
|
+
style: styles.stat,
|
|
93
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Clock, {
|
|
94
|
+
size: 12,
|
|
95
|
+
color: _sharedUi.macOSColors.text.secondary
|
|
96
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
97
|
+
style: styles.statValue,
|
|
98
|
+
children: formatDuration(metadata.duration)
|
|
99
|
+
})]
|
|
100
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
101
|
+
style: styles.stat,
|
|
102
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Layers, {
|
|
103
|
+
size: 12,
|
|
104
|
+
color: _sharedUi.macOSColors.text.secondary
|
|
105
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
|
|
106
|
+
style: styles.statValue,
|
|
107
|
+
children: [metadata.batchCount, " batch", metadata.batchCount !== 1 ? "es" : ""]
|
|
108
|
+
})]
|
|
109
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
110
|
+
style: styles.stat,
|
|
111
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Zap, {
|
|
112
|
+
size: 12,
|
|
113
|
+
color: metadata.duration < 10000 ? _sharedUi.macOSColors.semantic.success : metadata.duration < 30000 ? _sharedUi.macOSColors.semantic.warning : _sharedUi.macOSColors.semantic.error
|
|
114
|
+
})
|
|
115
|
+
})]
|
|
116
|
+
})]
|
|
117
|
+
})]
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
const styles = _reactNative.StyleSheet.create({
|
|
121
|
+
card: {
|
|
122
|
+
flexDirection: "row",
|
|
123
|
+
alignItems: "center",
|
|
124
|
+
backgroundColor: _sharedUi.macOSColors.background.card,
|
|
125
|
+
borderRadius: 8,
|
|
126
|
+
padding: 12,
|
|
127
|
+
marginHorizontal: 16,
|
|
128
|
+
borderWidth: 1,
|
|
129
|
+
borderColor: _sharedUi.macOSColors.border.default
|
|
130
|
+
},
|
|
131
|
+
cardSelected: {
|
|
132
|
+
borderColor: _sharedUi.macOSColors.semantic.info,
|
|
133
|
+
backgroundColor: _sharedUi.macOSColors.semantic.infoBackground
|
|
134
|
+
},
|
|
135
|
+
checkbox: {
|
|
136
|
+
width: 20,
|
|
137
|
+
height: 20,
|
|
138
|
+
borderRadius: 4,
|
|
139
|
+
borderWidth: 1.5,
|
|
140
|
+
borderColor: _sharedUi.macOSColors.border.hover,
|
|
141
|
+
marginRight: 12,
|
|
142
|
+
alignItems: "center",
|
|
143
|
+
justifyContent: "center"
|
|
144
|
+
},
|
|
145
|
+
checkboxSelected: {
|
|
146
|
+
backgroundColor: _sharedUi.macOSColors.semantic.info,
|
|
147
|
+
borderColor: _sharedUi.macOSColors.semantic.info
|
|
148
|
+
},
|
|
149
|
+
content: {
|
|
150
|
+
flex: 1
|
|
151
|
+
},
|
|
152
|
+
header: {
|
|
153
|
+
flexDirection: "row",
|
|
154
|
+
justifyContent: "space-between",
|
|
155
|
+
alignItems: "center",
|
|
156
|
+
marginBottom: 4
|
|
157
|
+
},
|
|
158
|
+
name: {
|
|
159
|
+
fontSize: 14,
|
|
160
|
+
fontWeight: "600",
|
|
161
|
+
color: _sharedUi.macOSColors.text.primary,
|
|
162
|
+
fontFamily: "monospace",
|
|
163
|
+
flex: 1,
|
|
164
|
+
marginRight: 8
|
|
165
|
+
},
|
|
166
|
+
date: {
|
|
167
|
+
fontSize: 11,
|
|
168
|
+
color: _sharedUi.macOSColors.text.muted,
|
|
169
|
+
fontFamily: "monospace"
|
|
170
|
+
},
|
|
171
|
+
description: {
|
|
172
|
+
fontSize: 12,
|
|
173
|
+
color: _sharedUi.macOSColors.text.secondary,
|
|
174
|
+
fontFamily: "monospace",
|
|
175
|
+
marginBottom: 8
|
|
176
|
+
},
|
|
177
|
+
statsRow: {
|
|
178
|
+
flexDirection: "row",
|
|
179
|
+
alignItems: "center",
|
|
180
|
+
gap: 16
|
|
181
|
+
},
|
|
182
|
+
stat: {
|
|
183
|
+
flexDirection: "row",
|
|
184
|
+
alignItems: "center",
|
|
185
|
+
gap: 4
|
|
186
|
+
},
|
|
187
|
+
statValue: {
|
|
188
|
+
fontSize: 11,
|
|
189
|
+
color: _sharedUi.macOSColors.text.secondary,
|
|
190
|
+
fontFamily: "monospace"
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
var _default = exports.default = BenchmarkSessionCard;
|
package/lib/commonjs/index.js
CHANGED
|
@@ -1 +1,62 @@
|
|
|
1
|
-
"use strict";
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
Object.defineProperty(exports, "BenchmarkComparator", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function () {
|
|
9
|
+
return _benchmarking.BenchmarkComparator;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
Object.defineProperty(exports, "BenchmarkModal", {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
get: function () {
|
|
15
|
+
return _BenchmarkModal.BenchmarkModal;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
Object.defineProperty(exports, "BenchmarkRecorder", {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
get: function () {
|
|
21
|
+
return _benchmarking.BenchmarkRecorder;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
Object.defineProperty(exports, "BenchmarkStorage", {
|
|
25
|
+
enumerable: true,
|
|
26
|
+
get: function () {
|
|
27
|
+
return _benchmarking.BenchmarkStorage;
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
Object.defineProperty(exports, "benchmarkPreset", {
|
|
31
|
+
enumerable: true,
|
|
32
|
+
get: function () {
|
|
33
|
+
return _preset.benchmarkPreset;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
Object.defineProperty(exports, "benchmarkRecorder", {
|
|
37
|
+
enumerable: true,
|
|
38
|
+
get: function () {
|
|
39
|
+
return _benchmarking.benchmarkRecorder;
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
Object.defineProperty(exports, "createAsyncStorageAdapter", {
|
|
43
|
+
enumerable: true,
|
|
44
|
+
get: function () {
|
|
45
|
+
return _benchmarking.createAsyncStorageAdapter;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
Object.defineProperty(exports, "createBenchmarkTool", {
|
|
49
|
+
enumerable: true,
|
|
50
|
+
get: function () {
|
|
51
|
+
return _preset.createBenchmarkTool;
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
Object.defineProperty(exports, "createMemoryStorageAdapter", {
|
|
55
|
+
enumerable: true,
|
|
56
|
+
get: function () {
|
|
57
|
+
return _benchmarking.createMemoryStorageAdapter;
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
var _preset = require("./preset");
|
|
61
|
+
var _benchmarking = require("./benchmarking");
|
|
62
|
+
var _BenchmarkModal = require("./components/BenchmarkModal");
|
package/lib/commonjs/preset.js
CHANGED
|
@@ -1 +1,86 @@
|
|
|
1
|
-
"use strict";
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.benchmarkPreset = void 0;
|
|
7
|
+
exports.createBenchmarkTool = createBenchmarkTool;
|
|
8
|
+
var _react = _interopRequireDefault(require("react"));
|
|
9
|
+
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
10
|
+
var _BenchmarkModal = require("./components/BenchmarkModal");
|
|
11
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
12
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
13
|
+
/**
|
|
14
|
+
* Pre-configured benchmark tool for FloatingDevTools
|
|
15
|
+
*
|
|
16
|
+
* This preset provides performance benchmarking functionality.
|
|
17
|
+
* Record, save, and compare performance metrics.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```tsx
|
|
21
|
+
* import { benchmarkPreset } from '@buoy-gg/benchmark';
|
|
22
|
+
*
|
|
23
|
+
* <FloatingDevTools apps={[benchmarkPreset]} />
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Pre-configured benchmark tool for FloatingDevTools.
|
|
29
|
+
* Provides performance benchmarking with recording, saving, and comparison features.
|
|
30
|
+
*
|
|
31
|
+
* Features:
|
|
32
|
+
* - Start/Stop recording sessions
|
|
33
|
+
* - View saved benchmark reports
|
|
34
|
+
* - Compare two benchmarks side-by-side
|
|
35
|
+
* - Delete individual or all benchmarks
|
|
36
|
+
*/
|
|
37
|
+
const benchmarkPreset = exports.benchmarkPreset = {
|
|
38
|
+
id: "benchmark",
|
|
39
|
+
name: "BENCH",
|
|
40
|
+
description: "Performance benchmarking",
|
|
41
|
+
slot: "both",
|
|
42
|
+
icon: ({
|
|
43
|
+
size
|
|
44
|
+
}) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.BenchmarkIcon, {
|
|
45
|
+
size: size,
|
|
46
|
+
color: "#F59E0B"
|
|
47
|
+
}),
|
|
48
|
+
component: _BenchmarkModal.BenchmarkModal,
|
|
49
|
+
props: {
|
|
50
|
+
enableSharedModalDimensions: false
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Create a custom benchmark tool configuration.
|
|
56
|
+
* Use this if you want to override default settings.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```tsx
|
|
60
|
+
* import { createBenchmarkTool } from '@buoy-gg/benchmark';
|
|
61
|
+
*
|
|
62
|
+
* const myBenchmarkTool = createBenchmarkTool({
|
|
63
|
+
* name: "PERF",
|
|
64
|
+
* iconColor: "#059669",
|
|
65
|
+
* });
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
function createBenchmarkTool(options) {
|
|
69
|
+
const iconColor = options?.iconColor || "#F59E0B";
|
|
70
|
+
return {
|
|
71
|
+
id: options?.id || "benchmark",
|
|
72
|
+
name: options?.name || "BENCH",
|
|
73
|
+
description: options?.description || "Performance benchmarking",
|
|
74
|
+
slot: "both",
|
|
75
|
+
icon: ({
|
|
76
|
+
size
|
|
77
|
+
}) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.BenchmarkIcon, {
|
|
78
|
+
size: size,
|
|
79
|
+
color: iconColor
|
|
80
|
+
}),
|
|
81
|
+
component: _BenchmarkModal.BenchmarkModal,
|
|
82
|
+
props: {
|
|
83
|
+
enableSharedModalDimensions: true
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
}
|
|
@@ -1 +1,216 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* BenchmarkComparator
|
|
3
|
+
*
|
|
4
|
+
* Compares two benchmark reports and generates a detailed comparison result.
|
|
5
|
+
* Calculates improvements/regressions across all metrics and provides
|
|
6
|
+
* human-readable summaries.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* const comparison = BenchmarkComparator.compare(baselineReport, comparisonReport);
|
|
10
|
+
* console.log(comparison.summary);
|
|
11
|
+
* console.log(`Overall improvement: ${comparison.overallImprovement.toFixed(1)}%`);
|
|
12
|
+
*
|
|
13
|
+
* @packageDocumentation
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
"use strict";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Calculate percentage improvement (positive = better)
|
|
20
|
+
* @param baseline - The baseline value
|
|
21
|
+
* @param comparison - The comparison value
|
|
22
|
+
* @returns Percentage improvement (positive = faster/better, negative = slower/worse)
|
|
23
|
+
*/
|
|
24
|
+
function calculateImprovement(baseline, comparison) {
|
|
25
|
+
if (baseline === 0) return 0;
|
|
26
|
+
return (baseline - comparison) / baseline * 100;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Format a percentage with sign
|
|
31
|
+
*/
|
|
32
|
+
function formatPercent(value) {
|
|
33
|
+
const sign = value >= 0 ? "+" : "";
|
|
34
|
+
return `${sign}${value.toFixed(1)}%`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Format milliseconds
|
|
39
|
+
*/
|
|
40
|
+
function formatMs(value) {
|
|
41
|
+
return `${value.toFixed(1)}ms`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Format bytes as MB
|
|
46
|
+
*/
|
|
47
|
+
function formatBytes(bytes) {
|
|
48
|
+
const mb = bytes / 1024 / 1024;
|
|
49
|
+
const sign = mb >= 0 ? "+" : "";
|
|
50
|
+
return `${sign}${mb.toFixed(2)}MB`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Get an emoji indicator for improvement
|
|
55
|
+
*/
|
|
56
|
+
function getIndicator(improvement, threshold = 5) {
|
|
57
|
+
if (improvement > threshold) return "✅";
|
|
58
|
+
if (improvement < -threshold) return "❌";
|
|
59
|
+
return "➖";
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* BenchmarkComparator - Compares two benchmark reports
|
|
64
|
+
*/
|
|
65
|
+
export class BenchmarkComparator {
|
|
66
|
+
/**
|
|
67
|
+
* Compare two benchmark reports
|
|
68
|
+
* @param baseline - The baseline (before) report
|
|
69
|
+
* @param comparison - The comparison (after) report
|
|
70
|
+
* @returns Detailed comparison result
|
|
71
|
+
*/
|
|
72
|
+
static compare(baseline, comparison) {
|
|
73
|
+
const baselineStats = baseline.stats;
|
|
74
|
+
const comparisonStats = comparison.stats;
|
|
75
|
+
|
|
76
|
+
// Duration changes
|
|
77
|
+
const durationDelta = comparison.duration - baseline.duration;
|
|
78
|
+
const durationImprovement = calculateImprovement(baseline.duration, comparison.duration);
|
|
79
|
+
|
|
80
|
+
// Pipeline timing improvements
|
|
81
|
+
const filterTimeImprovement = calculateImprovement(baselineStats.avgFilterTime, comparisonStats.avgFilterTime);
|
|
82
|
+
const measureTimeImprovement = calculateImprovement(baselineStats.avgMeasureTime, comparisonStats.avgMeasureTime);
|
|
83
|
+
const trackTimeImprovement = calculateImprovement(baselineStats.avgTrackTime, comparisonStats.avgTrackTime);
|
|
84
|
+
const pipelineTimeImprovement = calculateImprovement(baselineStats.avgTotalTime, comparisonStats.avgTotalTime);
|
|
85
|
+
|
|
86
|
+
// Overlay render improvement
|
|
87
|
+
const overlayRenderImprovement = calculateImprovement(baselineStats.avgOverlayRenderTime, comparisonStats.avgOverlayRenderTime);
|
|
88
|
+
|
|
89
|
+
// Memory changes
|
|
90
|
+
let memoryDeltaChange = null;
|
|
91
|
+
if (baseline.memoryDelta != null && comparison.memoryDelta != null) {
|
|
92
|
+
memoryDeltaChange = comparison.memoryDelta - baseline.memoryDelta;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Overall improvement (weighted average of key metrics)
|
|
96
|
+
// Weights: measurement time (40%), pipeline time (30%), overlay render (20%), memory (10%)
|
|
97
|
+
const weights = {
|
|
98
|
+
measure: 0.4,
|
|
99
|
+
pipeline: 0.3,
|
|
100
|
+
overlay: 0.2,
|
|
101
|
+
memory: 0.1
|
|
102
|
+
};
|
|
103
|
+
let overallImprovement = measureTimeImprovement * weights.measure + pipelineTimeImprovement * weights.pipeline + overlayRenderImprovement * weights.overlay;
|
|
104
|
+
|
|
105
|
+
// Add memory improvement if available
|
|
106
|
+
if (baseline.memoryDelta != null && comparison.memoryDelta != null && baseline.memoryDelta !== 0) {
|
|
107
|
+
const memoryImprovement = calculateImprovement(baseline.memoryDelta, comparison.memoryDelta);
|
|
108
|
+
overallImprovement += memoryImprovement * weights.memory;
|
|
109
|
+
}
|
|
110
|
+
const isImproved = overallImprovement > 0;
|
|
111
|
+
|
|
112
|
+
// Generate summary
|
|
113
|
+
const summary = this.generateSummary({
|
|
114
|
+
baseline,
|
|
115
|
+
comparison,
|
|
116
|
+
durationDelta,
|
|
117
|
+
durationImprovement,
|
|
118
|
+
measureTimeImprovement,
|
|
119
|
+
pipelineTimeImprovement,
|
|
120
|
+
overlayRenderImprovement,
|
|
121
|
+
memoryDeltaChange,
|
|
122
|
+
overallImprovement,
|
|
123
|
+
isImproved
|
|
124
|
+
});
|
|
125
|
+
return {
|
|
126
|
+
baselineId: baseline.id,
|
|
127
|
+
baselineName: baseline.name,
|
|
128
|
+
comparisonId: comparison.id,
|
|
129
|
+
comparisonName: comparison.name,
|
|
130
|
+
comparedAt: Date.now(),
|
|
131
|
+
durationDelta,
|
|
132
|
+
durationImprovement,
|
|
133
|
+
filterTimeImprovement,
|
|
134
|
+
measureTimeImprovement,
|
|
135
|
+
trackTimeImprovement,
|
|
136
|
+
pipelineTimeImprovement,
|
|
137
|
+
overlayRenderImprovement,
|
|
138
|
+
memoryDeltaChange,
|
|
139
|
+
overallImprovement,
|
|
140
|
+
isImproved,
|
|
141
|
+
summary
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Generate a human-readable summary
|
|
147
|
+
*/
|
|
148
|
+
static generateSummary(data) {
|
|
149
|
+
const {
|
|
150
|
+
baseline,
|
|
151
|
+
comparison,
|
|
152
|
+
measureTimeImprovement,
|
|
153
|
+
pipelineTimeImprovement,
|
|
154
|
+
overlayRenderImprovement,
|
|
155
|
+
memoryDeltaChange,
|
|
156
|
+
overallImprovement,
|
|
157
|
+
isImproved
|
|
158
|
+
} = data;
|
|
159
|
+
const lines = [];
|
|
160
|
+
lines.push("╔══════════════════════════════════════════════════════════════╗");
|
|
161
|
+
lines.push("║ BENCHMARK COMPARISON ║");
|
|
162
|
+
lines.push("╠══════════════════════════════════════════════════════════════╣");
|
|
163
|
+
lines.push(`║ Baseline: ${baseline.name.substring(0, 50).padEnd(50)} ║`);
|
|
164
|
+
lines.push(`║ ${new Date(baseline.createdAt).toLocaleString().substring(0, 58).padEnd(58)} ║`);
|
|
165
|
+
lines.push(`║ Comparison: ${comparison.name.substring(0, 48).padEnd(48)} ║`);
|
|
166
|
+
lines.push(`║ ${new Date(comparison.createdAt).toLocaleString().substring(0, 58).padEnd(58)} ║`);
|
|
167
|
+
lines.push("╠══════════════════════════════════════════════════════════════╣");
|
|
168
|
+
lines.push("║ TIMING COMPARISON ║");
|
|
169
|
+
lines.push("║ Baseline After Change ║");
|
|
170
|
+
lines.push(`║ Measurement Time: ${formatMs(baseline.stats.avgMeasureTime).padStart(8)} → ` + `${formatMs(comparison.stats.avgMeasureTime).padStart(8)} ` + `${formatPercent(measureTimeImprovement).padStart(7)} ${getIndicator(measureTimeImprovement)} ║`);
|
|
171
|
+
lines.push(`║ Pipeline Time: ${formatMs(baseline.stats.avgTotalTime).padStart(8)} → ` + `${formatMs(comparison.stats.avgTotalTime).padStart(8)} ` + `${formatPercent(pipelineTimeImprovement).padStart(7)} ${getIndicator(pipelineTimeImprovement)} ║`);
|
|
172
|
+
lines.push(`║ Overlay Render: ${formatMs(baseline.stats.avgOverlayRenderTime).padStart(8)} → ` + `${formatMs(comparison.stats.avgOverlayRenderTime).padStart(8)} ` + `${formatPercent(overlayRenderImprovement).padStart(7)} ${getIndicator(overlayRenderImprovement)} ║`);
|
|
173
|
+
if (memoryDeltaChange != null && baseline.memoryDelta != null && comparison.memoryDelta != null) {
|
|
174
|
+
const memoryImprovement = calculateImprovement(baseline.memoryDelta, comparison.memoryDelta);
|
|
175
|
+
lines.push("╠══════════════════════════════════════════════════════════════╣");
|
|
176
|
+
lines.push("║ MEMORY ║");
|
|
177
|
+
lines.push(`║ Delta: ${formatBytes(baseline.memoryDelta).padStart(8)} → ` + `${formatBytes(comparison.memoryDelta).padStart(8)} ` + `${formatPercent(memoryImprovement).padStart(7)} ${getIndicator(memoryImprovement)} ║`);
|
|
178
|
+
}
|
|
179
|
+
lines.push("╠══════════════════════════════════════════════════════════════╣");
|
|
180
|
+
lines.push("║ PERCENTILES (P95) ║");
|
|
181
|
+
const p95Improvement = calculateImprovement(baseline.stats.p95TotalTime, comparison.stats.p95TotalTime);
|
|
182
|
+
lines.push(`║ P95 Pipeline: ${formatMs(baseline.stats.p95TotalTime).padStart(8)} → ` + `${formatMs(comparison.stats.p95TotalTime).padStart(8)} ` + `${formatPercent(p95Improvement).padStart(7)} ${getIndicator(p95Improvement)} ║`);
|
|
183
|
+
lines.push("╠══════════════════════════════════════════════════════════════╣");
|
|
184
|
+
const resultEmoji = isImproved ? "🎉" : "⚠️";
|
|
185
|
+
const resultText = isImproved ? "IMPROVED" : "REGRESSED";
|
|
186
|
+
lines.push(`║ ${resultEmoji} OVERALL: ${formatPercent(overallImprovement).padStart(7)} ${resultText.padEnd(40)} ║`);
|
|
187
|
+
lines.push("╚══════════════════════════════════════════════════════════════╝");
|
|
188
|
+
return lines.join("\n");
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Log a comparison to the console
|
|
193
|
+
*/
|
|
194
|
+
static logComparison(comparison) {
|
|
195
|
+
console.log("\n" + comparison.summary + "\n");
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Quick compare: compare baseline vs comparison and log results
|
|
200
|
+
*/
|
|
201
|
+
static quickCompare(baseline, comparison) {
|
|
202
|
+
const result = this.compare(baseline, comparison);
|
|
203
|
+
this.logComparison(result);
|
|
204
|
+
return result;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Generate a brief one-line summary
|
|
209
|
+
*/
|
|
210
|
+
static getBriefSummary(comparison) {
|
|
211
|
+
const direction = comparison.isImproved ? "faster" : "slower";
|
|
212
|
+
const emoji = comparison.isImproved ? "✅" : "❌";
|
|
213
|
+
return `${emoji} ${Math.abs(comparison.overallImprovement).toFixed(1)}% ${direction} (measurement: ${formatPercent(comparison.measureTimeImprovement)}, pipeline: ${formatPercent(comparison.pipelineTimeImprovement)})`;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
export default BenchmarkComparator;
|