@camstack/addon-benchmark 1.1.29 → 1.1.31
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/dist/{MotionZonesSettings-DSC2vwhs.mjs → MotionZonesSettings-BnSK9yPe.mjs} +1 -1
- package/dist/{PrivacyMaskSettings-DnAI_kvA.mjs → PrivacyMaskSettings-B8Taq4MB.mjs} +2 -2
- package/dist/_stub.js +12665 -5189
- package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_benchmark_page-DyBacwqh.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_benchmark_page-BfDP01Ni.mjs} +4 -4
- package/dist/_virtual_mf___mfe_internal__addon_benchmark_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-D-M1jOv7.mjs +26 -0
- package/dist/addon-benchmark.css +1 -1
- package/dist/{hostInit-B6kurrI9.mjs → hostInit-REeKy4vE.mjs} +3 -3
- package/dist/index.js +361 -45
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +361 -45
- package/dist/index.mjs.map +1 -1
- package/dist/{player-overlays-BeOevLYN.mjs → player-overlays-Bc8ERrs2.mjs} +1 -1
- package/dist/remoteEntry.js +1 -1
- package/dist/{trash-2-CxFtcULX.mjs → trash-2-BuO8VeXk.mjs} +1 -1
- package/dist/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_benchmark_page__remoteEntry_js-CkjRY1rm.mjs → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_benchmark_page__remoteEntry_js-I5X3CISD.mjs} +1 -1
- package/package.json +1 -1
- package/dist/_virtual_mf___mfe_internal__addon_benchmark_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-ZSUIXwFh.mjs +0 -26
package/dist/index.mjs
CHANGED
|
@@ -516,7 +516,7 @@ __export(core_exports2, {
|
|
|
516
516
|
parse: () => parse,
|
|
517
517
|
parseAsync: () => parseAsync,
|
|
518
518
|
prettifyError: () => prettifyError,
|
|
519
|
-
process: () =>
|
|
519
|
+
process: () => process2,
|
|
520
520
|
regexes: () => regexes_exports,
|
|
521
521
|
registry: () => registry,
|
|
522
522
|
safeDecode: () => safeDecode,
|
|
@@ -11441,7 +11441,7 @@ function initializeContext(params) {
|
|
|
11441
11441
|
external: params?.external ?? void 0
|
|
11442
11442
|
};
|
|
11443
11443
|
}
|
|
11444
|
-
function
|
|
11444
|
+
function process2(schema, ctx, _params = { path: [], schemaPath: [] }) {
|
|
11445
11445
|
var _a3;
|
|
11446
11446
|
const def = schema._zod.def;
|
|
11447
11447
|
const seen = ctx.seen.get(schema);
|
|
@@ -11478,7 +11478,7 @@ function process(schema, ctx, _params = { path: [], schemaPath: [] }) {
|
|
|
11478
11478
|
if (parent) {
|
|
11479
11479
|
if (!result.ref)
|
|
11480
11480
|
result.ref = parent;
|
|
11481
|
-
|
|
11481
|
+
process2(parent, ctx, params);
|
|
11482
11482
|
ctx.seen.get(parent).isParent = true;
|
|
11483
11483
|
}
|
|
11484
11484
|
}
|
|
@@ -11766,14 +11766,14 @@ function isTransforming(_schema, _ctx) {
|
|
|
11766
11766
|
}
|
|
11767
11767
|
var createToJSONSchemaMethod = (schema, processors = {}) => (params) => {
|
|
11768
11768
|
const ctx = initializeContext({ ...params, processors });
|
|
11769
|
-
|
|
11769
|
+
process2(schema, ctx);
|
|
11770
11770
|
extractDefs(ctx, schema);
|
|
11771
11771
|
return finalize(ctx, schema);
|
|
11772
11772
|
};
|
|
11773
11773
|
var createStandardJSONSchemaMethod = (schema, io, processors = {}) => (params) => {
|
|
11774
11774
|
const { libraryOptions, target } = params ?? {};
|
|
11775
11775
|
const ctx = initializeContext({ ...libraryOptions ?? {}, target, io, processors });
|
|
11776
|
-
|
|
11776
|
+
process2(schema, ctx);
|
|
11777
11777
|
extractDefs(ctx, schema);
|
|
11778
11778
|
return finalize(ctx, schema);
|
|
11779
11779
|
};
|
|
@@ -12019,7 +12019,7 @@ var arrayProcessor = (schema, ctx, _json, params) => {
|
|
|
12019
12019
|
if (typeof maximum === "number")
|
|
12020
12020
|
json2.maxItems = maximum;
|
|
12021
12021
|
json2.type = "array";
|
|
12022
|
-
json2.items =
|
|
12022
|
+
json2.items = process2(def.element, ctx, {
|
|
12023
12023
|
...params,
|
|
12024
12024
|
path: [...params.path, "items"]
|
|
12025
12025
|
});
|
|
@@ -12031,7 +12031,7 @@ var objectProcessor = (schema, ctx, _json, params) => {
|
|
|
12031
12031
|
json2.properties = {};
|
|
12032
12032
|
const shape = def.shape;
|
|
12033
12033
|
for (const key in shape) {
|
|
12034
|
-
json2.properties[key] =
|
|
12034
|
+
json2.properties[key] = process2(shape[key], ctx, {
|
|
12035
12035
|
...params,
|
|
12036
12036
|
path: [...params.path, "properties", key]
|
|
12037
12037
|
});
|
|
@@ -12054,7 +12054,7 @@ var objectProcessor = (schema, ctx, _json, params) => {
|
|
|
12054
12054
|
if (ctx.io === "output")
|
|
12055
12055
|
json2.additionalProperties = false;
|
|
12056
12056
|
} else if (def.catchall) {
|
|
12057
|
-
json2.additionalProperties =
|
|
12057
|
+
json2.additionalProperties = process2(def.catchall, ctx, {
|
|
12058
12058
|
...params,
|
|
12059
12059
|
path: [...params.path, "additionalProperties"]
|
|
12060
12060
|
});
|
|
@@ -12063,7 +12063,7 @@ var objectProcessor = (schema, ctx, _json, params) => {
|
|
|
12063
12063
|
var unionProcessor = (schema, ctx, json2, params) => {
|
|
12064
12064
|
const def = schema._zod.def;
|
|
12065
12065
|
const isExclusive = def.inclusive === false;
|
|
12066
|
-
const options = def.options.map((x, i) =>
|
|
12066
|
+
const options = def.options.map((x, i) => process2(x, ctx, {
|
|
12067
12067
|
...params,
|
|
12068
12068
|
path: [...params.path, isExclusive ? "oneOf" : "anyOf", i]
|
|
12069
12069
|
}));
|
|
@@ -12075,11 +12075,11 @@ var unionProcessor = (schema, ctx, json2, params) => {
|
|
|
12075
12075
|
};
|
|
12076
12076
|
var intersectionProcessor = (schema, ctx, json2, params) => {
|
|
12077
12077
|
const def = schema._zod.def;
|
|
12078
|
-
const a =
|
|
12078
|
+
const a = process2(def.left, ctx, {
|
|
12079
12079
|
...params,
|
|
12080
12080
|
path: [...params.path, "allOf", 0]
|
|
12081
12081
|
});
|
|
12082
|
-
const b =
|
|
12082
|
+
const b = process2(def.right, ctx, {
|
|
12083
12083
|
...params,
|
|
12084
12084
|
path: [...params.path, "allOf", 1]
|
|
12085
12085
|
});
|
|
@@ -12096,11 +12096,11 @@ var tupleProcessor = (schema, ctx, _json, params) => {
|
|
|
12096
12096
|
json2.type = "array";
|
|
12097
12097
|
const prefixPath = ctx.target === "draft-2020-12" ? "prefixItems" : "items";
|
|
12098
12098
|
const restPath = ctx.target === "draft-2020-12" ? "items" : ctx.target === "openapi-3.0" ? "items" : "additionalItems";
|
|
12099
|
-
const prefixItems = def.items.map((x, i) =>
|
|
12099
|
+
const prefixItems = def.items.map((x, i) => process2(x, ctx, {
|
|
12100
12100
|
...params,
|
|
12101
12101
|
path: [...params.path, prefixPath, i]
|
|
12102
12102
|
}));
|
|
12103
|
-
const rest = def.rest ?
|
|
12103
|
+
const rest = def.rest ? process2(def.rest, ctx, {
|
|
12104
12104
|
...params,
|
|
12105
12105
|
path: [...params.path, restPath, ...ctx.target === "openapi-3.0" ? [def.items.length] : []]
|
|
12106
12106
|
}) : null;
|
|
@@ -12140,7 +12140,7 @@ var recordProcessor = (schema, ctx, _json, params) => {
|
|
|
12140
12140
|
const keyBag = keyType._zod.bag;
|
|
12141
12141
|
const patterns = keyBag?.patterns;
|
|
12142
12142
|
if (def.mode === "loose" && patterns && patterns.size > 0) {
|
|
12143
|
-
const valueSchema =
|
|
12143
|
+
const valueSchema = process2(def.valueType, ctx, {
|
|
12144
12144
|
...params,
|
|
12145
12145
|
path: [...params.path, "patternProperties", "*"]
|
|
12146
12146
|
});
|
|
@@ -12150,12 +12150,12 @@ var recordProcessor = (schema, ctx, _json, params) => {
|
|
|
12150
12150
|
}
|
|
12151
12151
|
} else {
|
|
12152
12152
|
if (ctx.target === "draft-07" || ctx.target === "draft-2020-12") {
|
|
12153
|
-
json2.propertyNames =
|
|
12153
|
+
json2.propertyNames = process2(def.keyType, ctx, {
|
|
12154
12154
|
...params,
|
|
12155
12155
|
path: [...params.path, "propertyNames"]
|
|
12156
12156
|
});
|
|
12157
12157
|
}
|
|
12158
|
-
json2.additionalProperties =
|
|
12158
|
+
json2.additionalProperties = process2(def.valueType, ctx, {
|
|
12159
12159
|
...params,
|
|
12160
12160
|
path: [...params.path, "additionalProperties"]
|
|
12161
12161
|
});
|
|
@@ -12170,7 +12170,7 @@ var recordProcessor = (schema, ctx, _json, params) => {
|
|
|
12170
12170
|
};
|
|
12171
12171
|
var nullableProcessor = (schema, ctx, json2, params) => {
|
|
12172
12172
|
const def = schema._zod.def;
|
|
12173
|
-
const inner =
|
|
12173
|
+
const inner = process2(def.innerType, ctx, params);
|
|
12174
12174
|
const seen = ctx.seen.get(schema);
|
|
12175
12175
|
if (ctx.target === "openapi-3.0") {
|
|
12176
12176
|
seen.ref = def.innerType;
|
|
@@ -12181,20 +12181,20 @@ var nullableProcessor = (schema, ctx, json2, params) => {
|
|
|
12181
12181
|
};
|
|
12182
12182
|
var nonoptionalProcessor = (schema, ctx, _json, params) => {
|
|
12183
12183
|
const def = schema._zod.def;
|
|
12184
|
-
|
|
12184
|
+
process2(def.innerType, ctx, params);
|
|
12185
12185
|
const seen = ctx.seen.get(schema);
|
|
12186
12186
|
seen.ref = def.innerType;
|
|
12187
12187
|
};
|
|
12188
12188
|
var defaultProcessor = (schema, ctx, json2, params) => {
|
|
12189
12189
|
const def = schema._zod.def;
|
|
12190
|
-
|
|
12190
|
+
process2(def.innerType, ctx, params);
|
|
12191
12191
|
const seen = ctx.seen.get(schema);
|
|
12192
12192
|
seen.ref = def.innerType;
|
|
12193
12193
|
json2.default = JSON.parse(JSON.stringify(def.defaultValue));
|
|
12194
12194
|
};
|
|
12195
12195
|
var prefaultProcessor = (schema, ctx, json2, params) => {
|
|
12196
12196
|
const def = schema._zod.def;
|
|
12197
|
-
|
|
12197
|
+
process2(def.innerType, ctx, params);
|
|
12198
12198
|
const seen = ctx.seen.get(schema);
|
|
12199
12199
|
seen.ref = def.innerType;
|
|
12200
12200
|
if (ctx.io === "input")
|
|
@@ -12202,7 +12202,7 @@ var prefaultProcessor = (schema, ctx, json2, params) => {
|
|
|
12202
12202
|
};
|
|
12203
12203
|
var catchProcessor = (schema, ctx, json2, params) => {
|
|
12204
12204
|
const def = schema._zod.def;
|
|
12205
|
-
|
|
12205
|
+
process2(def.innerType, ctx, params);
|
|
12206
12206
|
const seen = ctx.seen.get(schema);
|
|
12207
12207
|
seen.ref = def.innerType;
|
|
12208
12208
|
let catchValue;
|
|
@@ -12217,32 +12217,32 @@ var pipeProcessor = (schema, ctx, _json, params) => {
|
|
|
12217
12217
|
const def = schema._zod.def;
|
|
12218
12218
|
const inIsTransform = def.in._zod.traits.has("$ZodTransform");
|
|
12219
12219
|
const innerType = ctx.io === "input" ? inIsTransform ? def.out : def.in : def.out;
|
|
12220
|
-
|
|
12220
|
+
process2(innerType, ctx, params);
|
|
12221
12221
|
const seen = ctx.seen.get(schema);
|
|
12222
12222
|
seen.ref = innerType;
|
|
12223
12223
|
};
|
|
12224
12224
|
var readonlyProcessor = (schema, ctx, json2, params) => {
|
|
12225
12225
|
const def = schema._zod.def;
|
|
12226
|
-
|
|
12226
|
+
process2(def.innerType, ctx, params);
|
|
12227
12227
|
const seen = ctx.seen.get(schema);
|
|
12228
12228
|
seen.ref = def.innerType;
|
|
12229
12229
|
json2.readOnly = true;
|
|
12230
12230
|
};
|
|
12231
12231
|
var promiseProcessor = (schema, ctx, _json, params) => {
|
|
12232
12232
|
const def = schema._zod.def;
|
|
12233
|
-
|
|
12233
|
+
process2(def.innerType, ctx, params);
|
|
12234
12234
|
const seen = ctx.seen.get(schema);
|
|
12235
12235
|
seen.ref = def.innerType;
|
|
12236
12236
|
};
|
|
12237
12237
|
var optionalProcessor = (schema, ctx, _json, params) => {
|
|
12238
12238
|
const def = schema._zod.def;
|
|
12239
|
-
|
|
12239
|
+
process2(def.innerType, ctx, params);
|
|
12240
12240
|
const seen = ctx.seen.get(schema);
|
|
12241
12241
|
seen.ref = def.innerType;
|
|
12242
12242
|
};
|
|
12243
12243
|
var lazyProcessor = (schema, ctx, _json, params) => {
|
|
12244
12244
|
const innerType = schema._zod.innerType;
|
|
12245
|
-
|
|
12245
|
+
process2(innerType, ctx, params);
|
|
12246
12246
|
const seen = ctx.seen.get(schema);
|
|
12247
12247
|
seen.ref = innerType;
|
|
12248
12248
|
};
|
|
@@ -12294,7 +12294,7 @@ function toJSONSchema(input, params) {
|
|
|
12294
12294
|
const defs = {};
|
|
12295
12295
|
for (const entry of registry2._idmap.entries()) {
|
|
12296
12296
|
const [_, schema] = entry;
|
|
12297
|
-
|
|
12297
|
+
process2(schema, ctx2);
|
|
12298
12298
|
}
|
|
12299
12299
|
const schemas = {};
|
|
12300
12300
|
const external = {
|
|
@@ -12317,7 +12317,7 @@ function toJSONSchema(input, params) {
|
|
|
12317
12317
|
return { schemas };
|
|
12318
12318
|
}
|
|
12319
12319
|
const ctx = initializeContext({ ...params, processors: allProcessors });
|
|
12320
|
-
|
|
12320
|
+
process2(input, ctx);
|
|
12321
12321
|
extractDefs(ctx, input);
|
|
12322
12322
|
return finalize(ctx, input);
|
|
12323
12323
|
}
|
|
@@ -12375,7 +12375,7 @@ var JSONSchemaGenerator = class {
|
|
|
12375
12375
|
* This must be called before emit().
|
|
12376
12376
|
*/
|
|
12377
12377
|
process(schema, _params = { path: [], schemaPath: [] }) {
|
|
12378
|
-
return
|
|
12378
|
+
return process2(schema, this.ctx, _params);
|
|
12379
12379
|
}
|
|
12380
12380
|
/**
|
|
12381
12381
|
* Emit the final JSON Schema after processing.
|
|
@@ -14740,6 +14740,62 @@ var MultiCameraStressResultSchema = external_exports.object({
|
|
|
14740
14740
|
inferMsMean: external_exports.number(),
|
|
14741
14741
|
errors: external_exports.number()
|
|
14742
14742
|
});
|
|
14743
|
+
var SustainedEngineSchema = external_exports.object({
|
|
14744
|
+
runtime: external_exports.enum(["python", "node"]),
|
|
14745
|
+
backend: external_exports.string(),
|
|
14746
|
+
format: external_exports.enum(["onnx", "coreml", "openvino"]),
|
|
14747
|
+
device: external_exports.string().optional()
|
|
14748
|
+
}).optional();
|
|
14749
|
+
var SustainedNodeTargetSchema = external_exports.object({
|
|
14750
|
+
nodeId: external_exports.string().optional(),
|
|
14751
|
+
engine: SustainedEngineSchema,
|
|
14752
|
+
steps: external_exports.array(PipelineStepInputSchema).readonly(),
|
|
14753
|
+
label: external_exports.string().optional()
|
|
14754
|
+
});
|
|
14755
|
+
var SustainedThroughputInputSchema = external_exports.object({
|
|
14756
|
+
frameId: external_exports.string().optional(),
|
|
14757
|
+
imageBase64: external_exports.string().optional(),
|
|
14758
|
+
durationSec: external_exports.number().min(1).max(3600),
|
|
14759
|
+
batchSize: external_exports.number().int().min(1).max(255),
|
|
14760
|
+
concurrency: external_exports.number().int().min(1).max(64),
|
|
14761
|
+
targetSize: external_exports.number().int().min(64).max(2048).optional(),
|
|
14762
|
+
sessionId: external_exports.string(),
|
|
14763
|
+
nodes: external_exports.array(SustainedNodeTargetSchema).min(1).readonly()
|
|
14764
|
+
});
|
|
14765
|
+
var SustainedLatencyStatsSchema = external_exports.object({
|
|
14766
|
+
p50: external_exports.number(),
|
|
14767
|
+
p95: external_exports.number(),
|
|
14768
|
+
p99: external_exports.number(),
|
|
14769
|
+
mean: external_exports.number(),
|
|
14770
|
+
min: external_exports.number(),
|
|
14771
|
+
max: external_exports.number()
|
|
14772
|
+
});
|
|
14773
|
+
var SustainedNodeResultSchema = external_exports.object({
|
|
14774
|
+
nodeId: external_exports.string().nullable(),
|
|
14775
|
+
label: external_exports.string().optional(),
|
|
14776
|
+
/** Total frames processed by this node's loop. */
|
|
14777
|
+
totalFrames: external_exports.number(),
|
|
14778
|
+
/** Total completed batch calls. */
|
|
14779
|
+
totalBatches: external_exports.number(),
|
|
14780
|
+
/** Measured wall-clock window in ms. */
|
|
14781
|
+
wallMs: external_exports.number(),
|
|
14782
|
+
/** Sustained throughput. */
|
|
14783
|
+
fps: external_exports.number(),
|
|
14784
|
+
detsPerSec: external_exports.number(),
|
|
14785
|
+
/** Mean detections per frame — stable since the frame is fixed. */
|
|
14786
|
+
avgDetections: external_exports.number(),
|
|
14787
|
+
latency: SustainedLatencyStatsSchema,
|
|
14788
|
+
/** Batch calls that errored (transport / inference). */
|
|
14789
|
+
errors: external_exports.number(),
|
|
14790
|
+
/** True when this node ended via the stop button rather than the deadline. */
|
|
14791
|
+
aborted: external_exports.boolean()
|
|
14792
|
+
});
|
|
14793
|
+
var SustainedThroughputResultSchema = external_exports.object({
|
|
14794
|
+
perNode: external_exports.array(SustainedNodeResultSchema).readonly(),
|
|
14795
|
+
durationSec: external_exports.number(),
|
|
14796
|
+
batchSize: external_exports.number(),
|
|
14797
|
+
concurrency: external_exports.number()
|
|
14798
|
+
});
|
|
14743
14799
|
|
|
14744
14800
|
// src/benchmark-actions.ts
|
|
14745
14801
|
import { customAction, defineCustomActions, PipelineRunResultBridge } from "@camstack/types";
|
|
@@ -14788,6 +14844,25 @@ var benchmarkActions = defineCustomActions({
|
|
|
14788
14844
|
MultiCameraStressResultSchema,
|
|
14789
14845
|
{ kind: "mutation" }
|
|
14790
14846
|
),
|
|
14847
|
+
/**
|
|
14848
|
+
* Sustained-throughput ROOT-detection e2e run. Pins ONE frame (decoded
|
|
14849
|
+
* once), then feeds the identical frame to root object-detection for
|
|
14850
|
+
* `durationSec` seconds — driven by BATCHES (`runPipelineBatch`,
|
|
14851
|
+
* `batchSize` frames/call) with `concurrency` calls in flight, fanned out
|
|
14852
|
+
* across `nodes`, each on its resolved runtime/model + root-only steps.
|
|
14853
|
+
* Returns per-node sustained fps / det/s + latency percentiles on the
|
|
14854
|
+
* identical pinned frame. Bounded by N + `abortSustainedThroughput`.
|
|
14855
|
+
*/
|
|
14856
|
+
runSustainedThroughput: customAction(
|
|
14857
|
+
SustainedThroughputInputSchema,
|
|
14858
|
+
SustainedThroughputResultSchema,
|
|
14859
|
+
{ kind: "mutation" }
|
|
14860
|
+
),
|
|
14861
|
+
abortSustainedThroughput: customAction(
|
|
14862
|
+
external_exports.object({ sessionId: external_exports.string() }),
|
|
14863
|
+
external_exports.object({ aborted: external_exports.boolean() }),
|
|
14864
|
+
{ kind: "mutation" }
|
|
14865
|
+
),
|
|
14791
14866
|
// History (shared across pipeline + decoder-perf entries)
|
|
14792
14867
|
listBenchmarkHistory: customAction(external_exports.void(), external_exports.array(HistoryEntrySchema).readonly()),
|
|
14793
14868
|
saveBenchmarkResult: customAction(
|
|
@@ -15118,10 +15193,110 @@ async function runDecoderPerfTest(input, deps) {
|
|
|
15118
15193
|
};
|
|
15119
15194
|
}
|
|
15120
15195
|
|
|
15196
|
+
// src/throughput/sustained-runner.ts
|
|
15197
|
+
async function runSustainedLoop(opts) {
|
|
15198
|
+
const concurrency = Math.max(1, Math.floor(opts.concurrency));
|
|
15199
|
+
const start = opts.now();
|
|
15200
|
+
const deadline = start + Math.max(0, opts.durationMs);
|
|
15201
|
+
const callMsList = [];
|
|
15202
|
+
let totalFrames = 0;
|
|
15203
|
+
let totalBatches = 0;
|
|
15204
|
+
let totalDetections = 0;
|
|
15205
|
+
let detectionSamples = 0;
|
|
15206
|
+
let errors = 0;
|
|
15207
|
+
const worker = async () => {
|
|
15208
|
+
while (!opts.shouldStop() && opts.now() < deadline) {
|
|
15209
|
+
const t0 = opts.now();
|
|
15210
|
+
try {
|
|
15211
|
+
const out = await opts.dispatchBatch();
|
|
15212
|
+
const t1 = opts.now();
|
|
15213
|
+
callMsList.push(t1 - t0);
|
|
15214
|
+
totalBatches += 1;
|
|
15215
|
+
totalFrames += out.frameCount;
|
|
15216
|
+
for (const c of out.detectionCounts) {
|
|
15217
|
+
totalDetections += c;
|
|
15218
|
+
detectionSamples += 1;
|
|
15219
|
+
}
|
|
15220
|
+
} catch {
|
|
15221
|
+
errors += 1;
|
|
15222
|
+
}
|
|
15223
|
+
}
|
|
15224
|
+
};
|
|
15225
|
+
await Promise.all(Array.from({ length: concurrency }, () => worker()));
|
|
15226
|
+
const wallMs = opts.now() - start;
|
|
15227
|
+
return {
|
|
15228
|
+
callMsList,
|
|
15229
|
+
totalFrames,
|
|
15230
|
+
totalBatches,
|
|
15231
|
+
totalDetections,
|
|
15232
|
+
detectionSamples,
|
|
15233
|
+
errors,
|
|
15234
|
+
wallMs,
|
|
15235
|
+
aborted: opts.shouldStop()
|
|
15236
|
+
};
|
|
15237
|
+
}
|
|
15238
|
+
|
|
15239
|
+
// src/throughput/throughput-stats.ts
|
|
15240
|
+
var EMPTY_LATENCY = { p50: 0, p95: 0, p99: 0, mean: 0, min: 0, max: 0 };
|
|
15241
|
+
function percentile(sortedAsc, p) {
|
|
15242
|
+
const n = sortedAsc.length;
|
|
15243
|
+
if (n === 0) return 0;
|
|
15244
|
+
const clamped = p < 0 ? 0 : p > 1 ? 1 : p;
|
|
15245
|
+
if (clamped <= 0) return sortedAsc[0] ?? 0;
|
|
15246
|
+
if (clamped >= 1) return sortedAsc[n - 1] ?? 0;
|
|
15247
|
+
const rank = Math.ceil(clamped * n);
|
|
15248
|
+
const idx = Math.min(n - 1, Math.max(0, rank - 1));
|
|
15249
|
+
return sortedAsc[idx] ?? 0;
|
|
15250
|
+
}
|
|
15251
|
+
function computeLatencyStats(samplesMs) {
|
|
15252
|
+
if (samplesMs.length === 0) return EMPTY_LATENCY;
|
|
15253
|
+
const sorted = [...samplesMs].sort((a, b) => a - b);
|
|
15254
|
+
const sum = sorted.reduce((acc, x) => acc + x, 0);
|
|
15255
|
+
return {
|
|
15256
|
+
p50: percentile(sorted, 0.5),
|
|
15257
|
+
p95: percentile(sorted, 0.95),
|
|
15258
|
+
p99: percentile(sorted, 0.99),
|
|
15259
|
+
mean: sum / sorted.length,
|
|
15260
|
+
min: sorted[0] ?? 0,
|
|
15261
|
+
max: sorted[sorted.length - 1] ?? 0
|
|
15262
|
+
};
|
|
15263
|
+
}
|
|
15264
|
+
function computeFps(totalFrames, wallMs) {
|
|
15265
|
+
if (wallMs <= 0) return 0;
|
|
15266
|
+
return totalFrames * 1e3 / wallMs;
|
|
15267
|
+
}
|
|
15268
|
+
function computeDetsPerSec(totalDetections, wallMs) {
|
|
15269
|
+
if (wallMs <= 0) return 0;
|
|
15270
|
+
return totalDetections * 1e3 / wallMs;
|
|
15271
|
+
}
|
|
15272
|
+
function computeAvgDetections(totalDetections, sampledFrames) {
|
|
15273
|
+
if (sampledFrames <= 0) return 0;
|
|
15274
|
+
return totalDetections / sampledFrames;
|
|
15275
|
+
}
|
|
15276
|
+
|
|
15121
15277
|
// src/benchmark-addon.ts
|
|
15122
15278
|
var KEY_BENCHMARK_HISTORY = "benchmarkHistory";
|
|
15123
15279
|
var MAX_HISTORY_ENTRIES = 100;
|
|
15124
15280
|
var EMPTY_HISTORY_JSON = "[]";
|
|
15281
|
+
var BENCH_RUN_TIMEOUT_MS = Math.max(
|
|
15282
|
+
1e4,
|
|
15283
|
+
Number(process.env["CAMSTACK_BENCH_RUN_TIMEOUT_MS"]) || 12e4
|
|
15284
|
+
);
|
|
15285
|
+
async function withHardTimeout(promise2, ms, label) {
|
|
15286
|
+
let timer;
|
|
15287
|
+
const timeout = new Promise((_resolve, reject) => {
|
|
15288
|
+
timer = setTimeout(
|
|
15289
|
+
() => reject(new Error(`${label} exceeded ${ms}ms \u2014 aborting (likely a wedged step)`)),
|
|
15290
|
+
ms
|
|
15291
|
+
);
|
|
15292
|
+
timer.unref?.();
|
|
15293
|
+
});
|
|
15294
|
+
try {
|
|
15295
|
+
return await Promise.race([promise2, timeout]);
|
|
15296
|
+
} finally {
|
|
15297
|
+
if (timer) clearTimeout(timer);
|
|
15298
|
+
}
|
|
15299
|
+
}
|
|
15125
15300
|
var HistoryArraySchema = external_exports.array(HistoryEntrySchema);
|
|
15126
15301
|
var FIXTURE_META = {
|
|
15127
15302
|
"camera-sim-360p.h264": {
|
|
@@ -15149,6 +15324,8 @@ var BenchmarkAddon = class extends BaseAddon {
|
|
|
15149
15324
|
id = "benchmark";
|
|
15150
15325
|
/** Per-sessionId abort flags for in-flight decoder perf runs. */
|
|
15151
15326
|
decoderPerfAbortFlags = /* @__PURE__ */ new Map();
|
|
15327
|
+
/** Per-sessionId abort flags for in-flight sustained-throughput runs. */
|
|
15328
|
+
sustainedAbortFlags = /* @__PURE__ */ new Map();
|
|
15152
15329
|
/**
|
|
15153
15330
|
* In-memory cache of original JPEG bytes keyed by `frameId`. Populated by
|
|
15154
15331
|
* `cacheBenchFrame` (UI uploads JPEG once at bench start). Subsequent
|
|
@@ -15191,6 +15368,8 @@ var BenchmarkAddon = class extends BaseAddon {
|
|
|
15191
15368
|
runPipelineTest: async (input) => this.runPipelineTest(input),
|
|
15192
15369
|
runPipelineBatchTest: async (input) => this.runPipelineBatchTest(input),
|
|
15193
15370
|
runMultiCameraStressTest: async (input) => this.runMultiCameraStressTest(input),
|
|
15371
|
+
runSustainedThroughput: async (input) => this.runSustainedThroughput(input),
|
|
15372
|
+
abortSustainedThroughput: async (input) => this.abortSustainedThroughput(input),
|
|
15194
15373
|
cacheBenchFrame: async (input) => this.cacheBenchFrame(input),
|
|
15195
15374
|
releaseBenchFrame: async (input) => this.releaseBenchFrame(input),
|
|
15196
15375
|
listBenchmarkHistory: async () => this.listBenchmarkHistory(),
|
|
@@ -15210,6 +15389,8 @@ var BenchmarkAddon = class extends BaseAddon {
|
|
|
15210
15389
|
this.frameCacheSweeper = null;
|
|
15211
15390
|
}
|
|
15212
15391
|
this.frameCache.clear();
|
|
15392
|
+
for (const flag of this.sustainedAbortFlags.values()) flag.aborted = true;
|
|
15393
|
+
this.sustainedAbortFlags.clear();
|
|
15213
15394
|
}
|
|
15214
15395
|
// ── Frame cache (raw RGB) ────────────────────────────────────────────────
|
|
15215
15396
|
async cacheBenchFrame(input) {
|
|
@@ -15279,15 +15460,19 @@ var BenchmarkAddon = class extends BaseAddon {
|
|
|
15279
15460
|
});
|
|
15280
15461
|
return globalSteps.map(toInput);
|
|
15281
15462
|
})();
|
|
15282
|
-
return
|
|
15283
|
-
|
|
15284
|
-
|
|
15285
|
-
|
|
15286
|
-
|
|
15287
|
-
|
|
15288
|
-
|
|
15289
|
-
|
|
15290
|
-
|
|
15463
|
+
return withHardTimeout(
|
|
15464
|
+
this.api.pipelineExecutor.runPipeline.mutate({
|
|
15465
|
+
nodeId,
|
|
15466
|
+
steps: stepsToRun,
|
|
15467
|
+
...image ? { image } : {},
|
|
15468
|
+
...!image && imageBase64 ? { imageBase64 } : {},
|
|
15469
|
+
referenceImage,
|
|
15470
|
+
sessionId,
|
|
15471
|
+
...engine ? { engine } : {}
|
|
15472
|
+
}),
|
|
15473
|
+
BENCH_RUN_TIMEOUT_MS,
|
|
15474
|
+
"runPipelineTest"
|
|
15475
|
+
);
|
|
15291
15476
|
}
|
|
15292
15477
|
/**
|
|
15293
15478
|
* Batched pipeline tester.
|
|
@@ -15340,13 +15525,17 @@ var BenchmarkAddon = class extends BaseAddon {
|
|
|
15340
15525
|
timestamp: Date.now()
|
|
15341
15526
|
}));
|
|
15342
15527
|
const callStart = Date.now();
|
|
15343
|
-
const out = await
|
|
15344
|
-
|
|
15345
|
-
|
|
15346
|
-
|
|
15347
|
-
|
|
15348
|
-
|
|
15349
|
-
|
|
15528
|
+
const out = await withHardTimeout(
|
|
15529
|
+
this.api.pipelineExecutor.runPipelineBatch.mutate({
|
|
15530
|
+
nodeId,
|
|
15531
|
+
steps: [...steps],
|
|
15532
|
+
frames,
|
|
15533
|
+
sessionId,
|
|
15534
|
+
...engine ? { engine } : {}
|
|
15535
|
+
}),
|
|
15536
|
+
BENCH_RUN_TIMEOUT_MS,
|
|
15537
|
+
"runPipelineBatchTest"
|
|
15538
|
+
);
|
|
15350
15539
|
const callMs = Date.now() - callStart;
|
|
15351
15540
|
const inferMs = out.results.reduce((acc, r) => Math.max(acc, r.debug?.totalInferenceMs ?? 0), 0);
|
|
15352
15541
|
return {
|
|
@@ -15491,6 +15680,133 @@ var BenchmarkAddon = class extends BaseAddon {
|
|
|
15491
15680
|
errors
|
|
15492
15681
|
};
|
|
15493
15682
|
}
|
|
15683
|
+
// ── Sustained throughput (root-detection e2e) ────────────────────────────
|
|
15684
|
+
/**
|
|
15685
|
+
* SUSTAINED THROUGHPUT test of ROOT detection.
|
|
15686
|
+
*
|
|
15687
|
+
* 1. Pin ONE frame in memory: resolve `frameId` (from `cacheBenchFrame`)
|
|
15688
|
+
* or the one-shot `imageBase64`, decode + square-resize ONCE to raw
|
|
15689
|
+
* RGB. Every node + every iteration consumes the IDENTICAL buffer — no
|
|
15690
|
+
* per-iteration decode or upload.
|
|
15691
|
+
* 2. For each target node, run a `concurrency`-bounded loop of
|
|
15692
|
+
* `runPipelineBatch` (batchSize frames/call) on the node's ROOT-ONLY
|
|
15693
|
+
* steps until `durationSec` elapses (or the stop flag fires). Root-only
|
|
15694
|
+
* steps stay on the batch single-root fast path == live `plane:'frame'`
|
|
15695
|
+
* root detection.
|
|
15696
|
+
* 3. Aggregate sustained fps / det/s + latency percentiles per node on the
|
|
15697
|
+
* identical pinned frame for apples-to-apples comparison.
|
|
15698
|
+
*
|
|
15699
|
+
* Non-destructive: targets the benchmark's own cached frame (never a live
|
|
15700
|
+
* camera session), is explicitly started, and is bounded by N + the stop
|
|
15701
|
+
* button (`abortSustainedThroughput`).
|
|
15702
|
+
*/
|
|
15703
|
+
async runSustainedThroughput(input) {
|
|
15704
|
+
const { frameId, imageBase64, durationSec, batchSize, concurrency, sessionId, nodes } = input;
|
|
15705
|
+
const targetSize = input.targetSize ?? 640;
|
|
15706
|
+
const cached2 = frameId ? this.frameCache.get(frameId) : void 0;
|
|
15707
|
+
if (frameId && !cached2)
|
|
15708
|
+
throw new Error(
|
|
15709
|
+
`runSustainedThroughput: frameId ${frameId} not in cache (expired or never cached)`
|
|
15710
|
+
);
|
|
15711
|
+
const jpegSource = cached2 ? cached2.jpeg : imageBase64 ? Buffer.from(imageBase64, "base64") : null;
|
|
15712
|
+
if (!jpegSource)
|
|
15713
|
+
throw new Error("runSustainedThroughput: no frame source (need frameId or imageBase64)");
|
|
15714
|
+
const sharp = (await import("sharp")).default;
|
|
15715
|
+
const rgb = await sharp(jpegSource).resize({ width: targetSize, height: targetSize, fit: "fill" }).raw().toColorspace("srgb").toBuffer();
|
|
15716
|
+
const rgbBytes = new Uint8Array(rgb.buffer, rgb.byteOffset, rgb.byteLength);
|
|
15717
|
+
const batchFrames = Array.from({ length: batchSize }, () => ({
|
|
15718
|
+
data: rgbBytes,
|
|
15719
|
+
format: "rgb",
|
|
15720
|
+
width: targetSize,
|
|
15721
|
+
height: targetSize,
|
|
15722
|
+
timestamp: Date.now()
|
|
15723
|
+
}));
|
|
15724
|
+
const benchFrameId = Math.floor(Math.random() * 4294967294) + 1 >>> 0;
|
|
15725
|
+
const abortFlag = { aborted: false };
|
|
15726
|
+
this.sustainedAbortFlags.set(sessionId, abortFlag);
|
|
15727
|
+
this.ctx.logger.info("runSustainedThroughput start", {
|
|
15728
|
+
meta: {
|
|
15729
|
+
sessionId,
|
|
15730
|
+
durationSec,
|
|
15731
|
+
batchSize,
|
|
15732
|
+
concurrency,
|
|
15733
|
+
targetSize,
|
|
15734
|
+
nodeCount: nodes.length,
|
|
15735
|
+
nodes: nodes.map((n) => n.nodeId ?? "hub")
|
|
15736
|
+
}
|
|
15737
|
+
});
|
|
15738
|
+
try {
|
|
15739
|
+
const perNode = await Promise.all(
|
|
15740
|
+
nodes.map(async (node) => {
|
|
15741
|
+
if (node.steps.length === 0)
|
|
15742
|
+
throw new Error(
|
|
15743
|
+
`runSustainedThroughput: node ${node.nodeId ?? "hub"} has no root steps`
|
|
15744
|
+
);
|
|
15745
|
+
const dispatchBatch = async () => {
|
|
15746
|
+
const out = await this.api.pipelineExecutor.runPipelineBatch.mutate({
|
|
15747
|
+
nodeId: node.nodeId,
|
|
15748
|
+
steps: [...node.steps],
|
|
15749
|
+
frames: [...batchFrames],
|
|
15750
|
+
sessionId,
|
|
15751
|
+
frameId: benchFrameId,
|
|
15752
|
+
...node.engine ? { engine: node.engine } : {}
|
|
15753
|
+
});
|
|
15754
|
+
return {
|
|
15755
|
+
frameCount: out.results.length,
|
|
15756
|
+
detectionCounts: out.results.map((r) => r.detections.length)
|
|
15757
|
+
};
|
|
15758
|
+
};
|
|
15759
|
+
let raw;
|
|
15760
|
+
try {
|
|
15761
|
+
raw = await runSustainedLoop({
|
|
15762
|
+
dispatchBatch,
|
|
15763
|
+
concurrency,
|
|
15764
|
+
durationMs: durationSec * 1e3,
|
|
15765
|
+
now: () => Date.now(),
|
|
15766
|
+
shouldStop: () => abortFlag.aborted
|
|
15767
|
+
});
|
|
15768
|
+
} finally {
|
|
15769
|
+
await this.api.pipelineExecutor.uncacheFrame.mutate({ nodeId: node.nodeId, frameId: benchFrameId }).catch(() => {
|
|
15770
|
+
});
|
|
15771
|
+
}
|
|
15772
|
+
const summary = {
|
|
15773
|
+
nodeId: node.nodeId ?? null,
|
|
15774
|
+
...node.label ? { label: node.label } : {},
|
|
15775
|
+
totalFrames: raw.totalFrames,
|
|
15776
|
+
totalBatches: raw.totalBatches,
|
|
15777
|
+
wallMs: raw.wallMs,
|
|
15778
|
+
fps: computeFps(raw.totalFrames, raw.wallMs),
|
|
15779
|
+
detsPerSec: computeDetsPerSec(raw.totalDetections, raw.wallMs),
|
|
15780
|
+
avgDetections: computeAvgDetections(raw.totalDetections, raw.detectionSamples),
|
|
15781
|
+
latency: computeLatencyStats(raw.callMsList),
|
|
15782
|
+
errors: raw.errors,
|
|
15783
|
+
aborted: raw.aborted
|
|
15784
|
+
};
|
|
15785
|
+
this.ctx.logger.info("runSustainedThroughput node done", {
|
|
15786
|
+
meta: {
|
|
15787
|
+
nodeId: summary.nodeId,
|
|
15788
|
+
fps: Number(summary.fps.toFixed(2)),
|
|
15789
|
+
detsPerSec: Number(summary.detsPerSec.toFixed(2)),
|
|
15790
|
+
p95Ms: Number(summary.latency.p95.toFixed(2)),
|
|
15791
|
+
totalFrames: summary.totalFrames,
|
|
15792
|
+
errors: summary.errors,
|
|
15793
|
+
aborted: summary.aborted
|
|
15794
|
+
}
|
|
15795
|
+
});
|
|
15796
|
+
return summary;
|
|
15797
|
+
})
|
|
15798
|
+
);
|
|
15799
|
+
return { perNode, durationSec, batchSize, concurrency };
|
|
15800
|
+
} finally {
|
|
15801
|
+
this.sustainedAbortFlags.delete(sessionId);
|
|
15802
|
+
}
|
|
15803
|
+
}
|
|
15804
|
+
async abortSustainedThroughput(input) {
|
|
15805
|
+
const flag = this.sustainedAbortFlags.get(input.sessionId);
|
|
15806
|
+
if (!flag) return { aborted: false };
|
|
15807
|
+
flag.aborted = true;
|
|
15808
|
+
return { aborted: true };
|
|
15809
|
+
}
|
|
15494
15810
|
// ── Decoder perf — fixture discovery + run orchestration ────────────────
|
|
15495
15811
|
async listDecoderFixtures() {
|
|
15496
15812
|
const dir = resolveFixturesDir();
|