@camstack/addon-benchmark 1.1.30 → 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-DAxhcQ2Y.mjs → MotionZonesSettings-BnSK9yPe.mjs} +1 -1
- package/dist/{PrivacyMaskSettings-BxrWR2JE.mjs → PrivacyMaskSettings-B8Taq4MB.mjs} +2 -2
- package/dist/_stub.js +12842 -5586
- package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_benchmark_page-UNZbjT1-.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_benchmark_page-BfDP01Ni.mjs} +3 -3
- 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-HkxHCa90.mjs → hostInit-REeKy4vE.mjs} +2 -2
- 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-C0eYLxa6.mjs → player-overlays-Bc8ERrs2.mjs} +1 -1
- package/dist/remoteEntry.js +1 -1
- package/dist/{trash-2-D9dlLW6z.mjs → trash-2-BuO8VeXk.mjs} +1 -1
- package/dist/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_benchmark_page__remoteEntry_js-B-uib-3v.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-f0UbIzok.mjs +0 -26
package/dist/index.js
CHANGED
|
@@ -551,7 +551,7 @@ __export(core_exports2, {
|
|
|
551
551
|
parse: () => parse,
|
|
552
552
|
parseAsync: () => parseAsync,
|
|
553
553
|
prettifyError: () => prettifyError,
|
|
554
|
-
process: () =>
|
|
554
|
+
process: () => process2,
|
|
555
555
|
regexes: () => regexes_exports,
|
|
556
556
|
registry: () => registry,
|
|
557
557
|
safeDecode: () => safeDecode,
|
|
@@ -11476,7 +11476,7 @@ function initializeContext(params) {
|
|
|
11476
11476
|
external: params?.external ?? void 0
|
|
11477
11477
|
};
|
|
11478
11478
|
}
|
|
11479
|
-
function
|
|
11479
|
+
function process2(schema, ctx, _params = { path: [], schemaPath: [] }) {
|
|
11480
11480
|
var _a3;
|
|
11481
11481
|
const def = schema._zod.def;
|
|
11482
11482
|
const seen = ctx.seen.get(schema);
|
|
@@ -11513,7 +11513,7 @@ function process(schema, ctx, _params = { path: [], schemaPath: [] }) {
|
|
|
11513
11513
|
if (parent) {
|
|
11514
11514
|
if (!result.ref)
|
|
11515
11515
|
result.ref = parent;
|
|
11516
|
-
|
|
11516
|
+
process2(parent, ctx, params);
|
|
11517
11517
|
ctx.seen.get(parent).isParent = true;
|
|
11518
11518
|
}
|
|
11519
11519
|
}
|
|
@@ -11801,14 +11801,14 @@ function isTransforming(_schema, _ctx) {
|
|
|
11801
11801
|
}
|
|
11802
11802
|
var createToJSONSchemaMethod = (schema, processors = {}) => (params) => {
|
|
11803
11803
|
const ctx = initializeContext({ ...params, processors });
|
|
11804
|
-
|
|
11804
|
+
process2(schema, ctx);
|
|
11805
11805
|
extractDefs(ctx, schema);
|
|
11806
11806
|
return finalize(ctx, schema);
|
|
11807
11807
|
};
|
|
11808
11808
|
var createStandardJSONSchemaMethod = (schema, io, processors = {}) => (params) => {
|
|
11809
11809
|
const { libraryOptions, target } = params ?? {};
|
|
11810
11810
|
const ctx = initializeContext({ ...libraryOptions ?? {}, target, io, processors });
|
|
11811
|
-
|
|
11811
|
+
process2(schema, ctx);
|
|
11812
11812
|
extractDefs(ctx, schema);
|
|
11813
11813
|
return finalize(ctx, schema);
|
|
11814
11814
|
};
|
|
@@ -12054,7 +12054,7 @@ var arrayProcessor = (schema, ctx, _json, params) => {
|
|
|
12054
12054
|
if (typeof maximum === "number")
|
|
12055
12055
|
json2.maxItems = maximum;
|
|
12056
12056
|
json2.type = "array";
|
|
12057
|
-
json2.items =
|
|
12057
|
+
json2.items = process2(def.element, ctx, {
|
|
12058
12058
|
...params,
|
|
12059
12059
|
path: [...params.path, "items"]
|
|
12060
12060
|
});
|
|
@@ -12066,7 +12066,7 @@ var objectProcessor = (schema, ctx, _json, params) => {
|
|
|
12066
12066
|
json2.properties = {};
|
|
12067
12067
|
const shape = def.shape;
|
|
12068
12068
|
for (const key in shape) {
|
|
12069
|
-
json2.properties[key] =
|
|
12069
|
+
json2.properties[key] = process2(shape[key], ctx, {
|
|
12070
12070
|
...params,
|
|
12071
12071
|
path: [...params.path, "properties", key]
|
|
12072
12072
|
});
|
|
@@ -12089,7 +12089,7 @@ var objectProcessor = (schema, ctx, _json, params) => {
|
|
|
12089
12089
|
if (ctx.io === "output")
|
|
12090
12090
|
json2.additionalProperties = false;
|
|
12091
12091
|
} else if (def.catchall) {
|
|
12092
|
-
json2.additionalProperties =
|
|
12092
|
+
json2.additionalProperties = process2(def.catchall, ctx, {
|
|
12093
12093
|
...params,
|
|
12094
12094
|
path: [...params.path, "additionalProperties"]
|
|
12095
12095
|
});
|
|
@@ -12098,7 +12098,7 @@ var objectProcessor = (schema, ctx, _json, params) => {
|
|
|
12098
12098
|
var unionProcessor = (schema, ctx, json2, params) => {
|
|
12099
12099
|
const def = schema._zod.def;
|
|
12100
12100
|
const isExclusive = def.inclusive === false;
|
|
12101
|
-
const options = def.options.map((x, i) =>
|
|
12101
|
+
const options = def.options.map((x, i) => process2(x, ctx, {
|
|
12102
12102
|
...params,
|
|
12103
12103
|
path: [...params.path, isExclusive ? "oneOf" : "anyOf", i]
|
|
12104
12104
|
}));
|
|
@@ -12110,11 +12110,11 @@ var unionProcessor = (schema, ctx, json2, params) => {
|
|
|
12110
12110
|
};
|
|
12111
12111
|
var intersectionProcessor = (schema, ctx, json2, params) => {
|
|
12112
12112
|
const def = schema._zod.def;
|
|
12113
|
-
const a =
|
|
12113
|
+
const a = process2(def.left, ctx, {
|
|
12114
12114
|
...params,
|
|
12115
12115
|
path: [...params.path, "allOf", 0]
|
|
12116
12116
|
});
|
|
12117
|
-
const b =
|
|
12117
|
+
const b = process2(def.right, ctx, {
|
|
12118
12118
|
...params,
|
|
12119
12119
|
path: [...params.path, "allOf", 1]
|
|
12120
12120
|
});
|
|
@@ -12131,11 +12131,11 @@ var tupleProcessor = (schema, ctx, _json, params) => {
|
|
|
12131
12131
|
json2.type = "array";
|
|
12132
12132
|
const prefixPath = ctx.target === "draft-2020-12" ? "prefixItems" : "items";
|
|
12133
12133
|
const restPath = ctx.target === "draft-2020-12" ? "items" : ctx.target === "openapi-3.0" ? "items" : "additionalItems";
|
|
12134
|
-
const prefixItems = def.items.map((x, i) =>
|
|
12134
|
+
const prefixItems = def.items.map((x, i) => process2(x, ctx, {
|
|
12135
12135
|
...params,
|
|
12136
12136
|
path: [...params.path, prefixPath, i]
|
|
12137
12137
|
}));
|
|
12138
|
-
const rest = def.rest ?
|
|
12138
|
+
const rest = def.rest ? process2(def.rest, ctx, {
|
|
12139
12139
|
...params,
|
|
12140
12140
|
path: [...params.path, restPath, ...ctx.target === "openapi-3.0" ? [def.items.length] : []]
|
|
12141
12141
|
}) : null;
|
|
@@ -12175,7 +12175,7 @@ var recordProcessor = (schema, ctx, _json, params) => {
|
|
|
12175
12175
|
const keyBag = keyType._zod.bag;
|
|
12176
12176
|
const patterns = keyBag?.patterns;
|
|
12177
12177
|
if (def.mode === "loose" && patterns && patterns.size > 0) {
|
|
12178
|
-
const valueSchema =
|
|
12178
|
+
const valueSchema = process2(def.valueType, ctx, {
|
|
12179
12179
|
...params,
|
|
12180
12180
|
path: [...params.path, "patternProperties", "*"]
|
|
12181
12181
|
});
|
|
@@ -12185,12 +12185,12 @@ var recordProcessor = (schema, ctx, _json, params) => {
|
|
|
12185
12185
|
}
|
|
12186
12186
|
} else {
|
|
12187
12187
|
if (ctx.target === "draft-07" || ctx.target === "draft-2020-12") {
|
|
12188
|
-
json2.propertyNames =
|
|
12188
|
+
json2.propertyNames = process2(def.keyType, ctx, {
|
|
12189
12189
|
...params,
|
|
12190
12190
|
path: [...params.path, "propertyNames"]
|
|
12191
12191
|
});
|
|
12192
12192
|
}
|
|
12193
|
-
json2.additionalProperties =
|
|
12193
|
+
json2.additionalProperties = process2(def.valueType, ctx, {
|
|
12194
12194
|
...params,
|
|
12195
12195
|
path: [...params.path, "additionalProperties"]
|
|
12196
12196
|
});
|
|
@@ -12205,7 +12205,7 @@ var recordProcessor = (schema, ctx, _json, params) => {
|
|
|
12205
12205
|
};
|
|
12206
12206
|
var nullableProcessor = (schema, ctx, json2, params) => {
|
|
12207
12207
|
const def = schema._zod.def;
|
|
12208
|
-
const inner =
|
|
12208
|
+
const inner = process2(def.innerType, ctx, params);
|
|
12209
12209
|
const seen = ctx.seen.get(schema);
|
|
12210
12210
|
if (ctx.target === "openapi-3.0") {
|
|
12211
12211
|
seen.ref = def.innerType;
|
|
@@ -12216,20 +12216,20 @@ var nullableProcessor = (schema, ctx, json2, params) => {
|
|
|
12216
12216
|
};
|
|
12217
12217
|
var nonoptionalProcessor = (schema, ctx, _json, params) => {
|
|
12218
12218
|
const def = schema._zod.def;
|
|
12219
|
-
|
|
12219
|
+
process2(def.innerType, ctx, params);
|
|
12220
12220
|
const seen = ctx.seen.get(schema);
|
|
12221
12221
|
seen.ref = def.innerType;
|
|
12222
12222
|
};
|
|
12223
12223
|
var defaultProcessor = (schema, ctx, json2, params) => {
|
|
12224
12224
|
const def = schema._zod.def;
|
|
12225
|
-
|
|
12225
|
+
process2(def.innerType, ctx, params);
|
|
12226
12226
|
const seen = ctx.seen.get(schema);
|
|
12227
12227
|
seen.ref = def.innerType;
|
|
12228
12228
|
json2.default = JSON.parse(JSON.stringify(def.defaultValue));
|
|
12229
12229
|
};
|
|
12230
12230
|
var prefaultProcessor = (schema, ctx, json2, params) => {
|
|
12231
12231
|
const def = schema._zod.def;
|
|
12232
|
-
|
|
12232
|
+
process2(def.innerType, ctx, params);
|
|
12233
12233
|
const seen = ctx.seen.get(schema);
|
|
12234
12234
|
seen.ref = def.innerType;
|
|
12235
12235
|
if (ctx.io === "input")
|
|
@@ -12237,7 +12237,7 @@ var prefaultProcessor = (schema, ctx, json2, params) => {
|
|
|
12237
12237
|
};
|
|
12238
12238
|
var catchProcessor = (schema, ctx, json2, params) => {
|
|
12239
12239
|
const def = schema._zod.def;
|
|
12240
|
-
|
|
12240
|
+
process2(def.innerType, ctx, params);
|
|
12241
12241
|
const seen = ctx.seen.get(schema);
|
|
12242
12242
|
seen.ref = def.innerType;
|
|
12243
12243
|
let catchValue;
|
|
@@ -12252,32 +12252,32 @@ var pipeProcessor = (schema, ctx, _json, params) => {
|
|
|
12252
12252
|
const def = schema._zod.def;
|
|
12253
12253
|
const inIsTransform = def.in._zod.traits.has("$ZodTransform");
|
|
12254
12254
|
const innerType = ctx.io === "input" ? inIsTransform ? def.out : def.in : def.out;
|
|
12255
|
-
|
|
12255
|
+
process2(innerType, ctx, params);
|
|
12256
12256
|
const seen = ctx.seen.get(schema);
|
|
12257
12257
|
seen.ref = innerType;
|
|
12258
12258
|
};
|
|
12259
12259
|
var readonlyProcessor = (schema, ctx, json2, params) => {
|
|
12260
12260
|
const def = schema._zod.def;
|
|
12261
|
-
|
|
12261
|
+
process2(def.innerType, ctx, params);
|
|
12262
12262
|
const seen = ctx.seen.get(schema);
|
|
12263
12263
|
seen.ref = def.innerType;
|
|
12264
12264
|
json2.readOnly = true;
|
|
12265
12265
|
};
|
|
12266
12266
|
var promiseProcessor = (schema, ctx, _json, params) => {
|
|
12267
12267
|
const def = schema._zod.def;
|
|
12268
|
-
|
|
12268
|
+
process2(def.innerType, ctx, params);
|
|
12269
12269
|
const seen = ctx.seen.get(schema);
|
|
12270
12270
|
seen.ref = def.innerType;
|
|
12271
12271
|
};
|
|
12272
12272
|
var optionalProcessor = (schema, ctx, _json, params) => {
|
|
12273
12273
|
const def = schema._zod.def;
|
|
12274
|
-
|
|
12274
|
+
process2(def.innerType, ctx, params);
|
|
12275
12275
|
const seen = ctx.seen.get(schema);
|
|
12276
12276
|
seen.ref = def.innerType;
|
|
12277
12277
|
};
|
|
12278
12278
|
var lazyProcessor = (schema, ctx, _json, params) => {
|
|
12279
12279
|
const innerType = schema._zod.innerType;
|
|
12280
|
-
|
|
12280
|
+
process2(innerType, ctx, params);
|
|
12281
12281
|
const seen = ctx.seen.get(schema);
|
|
12282
12282
|
seen.ref = innerType;
|
|
12283
12283
|
};
|
|
@@ -12329,7 +12329,7 @@ function toJSONSchema(input, params) {
|
|
|
12329
12329
|
const defs = {};
|
|
12330
12330
|
for (const entry of registry2._idmap.entries()) {
|
|
12331
12331
|
const [_, schema] = entry;
|
|
12332
|
-
|
|
12332
|
+
process2(schema, ctx2);
|
|
12333
12333
|
}
|
|
12334
12334
|
const schemas = {};
|
|
12335
12335
|
const external = {
|
|
@@ -12352,7 +12352,7 @@ function toJSONSchema(input, params) {
|
|
|
12352
12352
|
return { schemas };
|
|
12353
12353
|
}
|
|
12354
12354
|
const ctx = initializeContext({ ...params, processors: allProcessors });
|
|
12355
|
-
|
|
12355
|
+
process2(input, ctx);
|
|
12356
12356
|
extractDefs(ctx, input);
|
|
12357
12357
|
return finalize(ctx, input);
|
|
12358
12358
|
}
|
|
@@ -12410,7 +12410,7 @@ var JSONSchemaGenerator = class {
|
|
|
12410
12410
|
* This must be called before emit().
|
|
12411
12411
|
*/
|
|
12412
12412
|
process(schema, _params = { path: [], schemaPath: [] }) {
|
|
12413
|
-
return
|
|
12413
|
+
return process2(schema, this.ctx, _params);
|
|
12414
12414
|
}
|
|
12415
12415
|
/**
|
|
12416
12416
|
* Emit the final JSON Schema after processing.
|
|
@@ -14775,6 +14775,62 @@ var MultiCameraStressResultSchema = external_exports.object({
|
|
|
14775
14775
|
inferMsMean: external_exports.number(),
|
|
14776
14776
|
errors: external_exports.number()
|
|
14777
14777
|
});
|
|
14778
|
+
var SustainedEngineSchema = external_exports.object({
|
|
14779
|
+
runtime: external_exports.enum(["python", "node"]),
|
|
14780
|
+
backend: external_exports.string(),
|
|
14781
|
+
format: external_exports.enum(["onnx", "coreml", "openvino"]),
|
|
14782
|
+
device: external_exports.string().optional()
|
|
14783
|
+
}).optional();
|
|
14784
|
+
var SustainedNodeTargetSchema = external_exports.object({
|
|
14785
|
+
nodeId: external_exports.string().optional(),
|
|
14786
|
+
engine: SustainedEngineSchema,
|
|
14787
|
+
steps: external_exports.array(import_types.PipelineStepInputSchema).readonly(),
|
|
14788
|
+
label: external_exports.string().optional()
|
|
14789
|
+
});
|
|
14790
|
+
var SustainedThroughputInputSchema = external_exports.object({
|
|
14791
|
+
frameId: external_exports.string().optional(),
|
|
14792
|
+
imageBase64: external_exports.string().optional(),
|
|
14793
|
+
durationSec: external_exports.number().min(1).max(3600),
|
|
14794
|
+
batchSize: external_exports.number().int().min(1).max(255),
|
|
14795
|
+
concurrency: external_exports.number().int().min(1).max(64),
|
|
14796
|
+
targetSize: external_exports.number().int().min(64).max(2048).optional(),
|
|
14797
|
+
sessionId: external_exports.string(),
|
|
14798
|
+
nodes: external_exports.array(SustainedNodeTargetSchema).min(1).readonly()
|
|
14799
|
+
});
|
|
14800
|
+
var SustainedLatencyStatsSchema = external_exports.object({
|
|
14801
|
+
p50: external_exports.number(),
|
|
14802
|
+
p95: external_exports.number(),
|
|
14803
|
+
p99: external_exports.number(),
|
|
14804
|
+
mean: external_exports.number(),
|
|
14805
|
+
min: external_exports.number(),
|
|
14806
|
+
max: external_exports.number()
|
|
14807
|
+
});
|
|
14808
|
+
var SustainedNodeResultSchema = external_exports.object({
|
|
14809
|
+
nodeId: external_exports.string().nullable(),
|
|
14810
|
+
label: external_exports.string().optional(),
|
|
14811
|
+
/** Total frames processed by this node's loop. */
|
|
14812
|
+
totalFrames: external_exports.number(),
|
|
14813
|
+
/** Total completed batch calls. */
|
|
14814
|
+
totalBatches: external_exports.number(),
|
|
14815
|
+
/** Measured wall-clock window in ms. */
|
|
14816
|
+
wallMs: external_exports.number(),
|
|
14817
|
+
/** Sustained throughput. */
|
|
14818
|
+
fps: external_exports.number(),
|
|
14819
|
+
detsPerSec: external_exports.number(),
|
|
14820
|
+
/** Mean detections per frame — stable since the frame is fixed. */
|
|
14821
|
+
avgDetections: external_exports.number(),
|
|
14822
|
+
latency: SustainedLatencyStatsSchema,
|
|
14823
|
+
/** Batch calls that errored (transport / inference). */
|
|
14824
|
+
errors: external_exports.number(),
|
|
14825
|
+
/** True when this node ended via the stop button rather than the deadline. */
|
|
14826
|
+
aborted: external_exports.boolean()
|
|
14827
|
+
});
|
|
14828
|
+
var SustainedThroughputResultSchema = external_exports.object({
|
|
14829
|
+
perNode: external_exports.array(SustainedNodeResultSchema).readonly(),
|
|
14830
|
+
durationSec: external_exports.number(),
|
|
14831
|
+
batchSize: external_exports.number(),
|
|
14832
|
+
concurrency: external_exports.number()
|
|
14833
|
+
});
|
|
14778
14834
|
|
|
14779
14835
|
// src/benchmark-actions.ts
|
|
14780
14836
|
var import_types2 = require("@camstack/types");
|
|
@@ -14823,6 +14879,25 @@ var benchmarkActions = (0, import_types2.defineCustomActions)({
|
|
|
14823
14879
|
MultiCameraStressResultSchema,
|
|
14824
14880
|
{ kind: "mutation" }
|
|
14825
14881
|
),
|
|
14882
|
+
/**
|
|
14883
|
+
* Sustained-throughput ROOT-detection e2e run. Pins ONE frame (decoded
|
|
14884
|
+
* once), then feeds the identical frame to root object-detection for
|
|
14885
|
+
* `durationSec` seconds — driven by BATCHES (`runPipelineBatch`,
|
|
14886
|
+
* `batchSize` frames/call) with `concurrency` calls in flight, fanned out
|
|
14887
|
+
* across `nodes`, each on its resolved runtime/model + root-only steps.
|
|
14888
|
+
* Returns per-node sustained fps / det/s + latency percentiles on the
|
|
14889
|
+
* identical pinned frame. Bounded by N + `abortSustainedThroughput`.
|
|
14890
|
+
*/
|
|
14891
|
+
runSustainedThroughput: (0, import_types2.customAction)(
|
|
14892
|
+
SustainedThroughputInputSchema,
|
|
14893
|
+
SustainedThroughputResultSchema,
|
|
14894
|
+
{ kind: "mutation" }
|
|
14895
|
+
),
|
|
14896
|
+
abortSustainedThroughput: (0, import_types2.customAction)(
|
|
14897
|
+
external_exports.object({ sessionId: external_exports.string() }),
|
|
14898
|
+
external_exports.object({ aborted: external_exports.boolean() }),
|
|
14899
|
+
{ kind: "mutation" }
|
|
14900
|
+
),
|
|
14826
14901
|
// History (shared across pipeline + decoder-perf entries)
|
|
14827
14902
|
listBenchmarkHistory: (0, import_types2.customAction)(external_exports.void(), external_exports.array(HistoryEntrySchema).readonly()),
|
|
14828
14903
|
saveBenchmarkResult: (0, import_types2.customAction)(
|
|
@@ -15153,10 +15228,110 @@ async function runDecoderPerfTest(input, deps) {
|
|
|
15153
15228
|
};
|
|
15154
15229
|
}
|
|
15155
15230
|
|
|
15231
|
+
// src/throughput/sustained-runner.ts
|
|
15232
|
+
async function runSustainedLoop(opts) {
|
|
15233
|
+
const concurrency = Math.max(1, Math.floor(opts.concurrency));
|
|
15234
|
+
const start = opts.now();
|
|
15235
|
+
const deadline = start + Math.max(0, opts.durationMs);
|
|
15236
|
+
const callMsList = [];
|
|
15237
|
+
let totalFrames = 0;
|
|
15238
|
+
let totalBatches = 0;
|
|
15239
|
+
let totalDetections = 0;
|
|
15240
|
+
let detectionSamples = 0;
|
|
15241
|
+
let errors = 0;
|
|
15242
|
+
const worker = async () => {
|
|
15243
|
+
while (!opts.shouldStop() && opts.now() < deadline) {
|
|
15244
|
+
const t0 = opts.now();
|
|
15245
|
+
try {
|
|
15246
|
+
const out = await opts.dispatchBatch();
|
|
15247
|
+
const t1 = opts.now();
|
|
15248
|
+
callMsList.push(t1 - t0);
|
|
15249
|
+
totalBatches += 1;
|
|
15250
|
+
totalFrames += out.frameCount;
|
|
15251
|
+
for (const c of out.detectionCounts) {
|
|
15252
|
+
totalDetections += c;
|
|
15253
|
+
detectionSamples += 1;
|
|
15254
|
+
}
|
|
15255
|
+
} catch {
|
|
15256
|
+
errors += 1;
|
|
15257
|
+
}
|
|
15258
|
+
}
|
|
15259
|
+
};
|
|
15260
|
+
await Promise.all(Array.from({ length: concurrency }, () => worker()));
|
|
15261
|
+
const wallMs = opts.now() - start;
|
|
15262
|
+
return {
|
|
15263
|
+
callMsList,
|
|
15264
|
+
totalFrames,
|
|
15265
|
+
totalBatches,
|
|
15266
|
+
totalDetections,
|
|
15267
|
+
detectionSamples,
|
|
15268
|
+
errors,
|
|
15269
|
+
wallMs,
|
|
15270
|
+
aborted: opts.shouldStop()
|
|
15271
|
+
};
|
|
15272
|
+
}
|
|
15273
|
+
|
|
15274
|
+
// src/throughput/throughput-stats.ts
|
|
15275
|
+
var EMPTY_LATENCY = { p50: 0, p95: 0, p99: 0, mean: 0, min: 0, max: 0 };
|
|
15276
|
+
function percentile(sortedAsc, p) {
|
|
15277
|
+
const n = sortedAsc.length;
|
|
15278
|
+
if (n === 0) return 0;
|
|
15279
|
+
const clamped = p < 0 ? 0 : p > 1 ? 1 : p;
|
|
15280
|
+
if (clamped <= 0) return sortedAsc[0] ?? 0;
|
|
15281
|
+
if (clamped >= 1) return sortedAsc[n - 1] ?? 0;
|
|
15282
|
+
const rank = Math.ceil(clamped * n);
|
|
15283
|
+
const idx = Math.min(n - 1, Math.max(0, rank - 1));
|
|
15284
|
+
return sortedAsc[idx] ?? 0;
|
|
15285
|
+
}
|
|
15286
|
+
function computeLatencyStats(samplesMs) {
|
|
15287
|
+
if (samplesMs.length === 0) return EMPTY_LATENCY;
|
|
15288
|
+
const sorted = [...samplesMs].sort((a, b) => a - b);
|
|
15289
|
+
const sum = sorted.reduce((acc, x) => acc + x, 0);
|
|
15290
|
+
return {
|
|
15291
|
+
p50: percentile(sorted, 0.5),
|
|
15292
|
+
p95: percentile(sorted, 0.95),
|
|
15293
|
+
p99: percentile(sorted, 0.99),
|
|
15294
|
+
mean: sum / sorted.length,
|
|
15295
|
+
min: sorted[0] ?? 0,
|
|
15296
|
+
max: sorted[sorted.length - 1] ?? 0
|
|
15297
|
+
};
|
|
15298
|
+
}
|
|
15299
|
+
function computeFps(totalFrames, wallMs) {
|
|
15300
|
+
if (wallMs <= 0) return 0;
|
|
15301
|
+
return totalFrames * 1e3 / wallMs;
|
|
15302
|
+
}
|
|
15303
|
+
function computeDetsPerSec(totalDetections, wallMs) {
|
|
15304
|
+
if (wallMs <= 0) return 0;
|
|
15305
|
+
return totalDetections * 1e3 / wallMs;
|
|
15306
|
+
}
|
|
15307
|
+
function computeAvgDetections(totalDetections, sampledFrames) {
|
|
15308
|
+
if (sampledFrames <= 0) return 0;
|
|
15309
|
+
return totalDetections / sampledFrames;
|
|
15310
|
+
}
|
|
15311
|
+
|
|
15156
15312
|
// src/benchmark-addon.ts
|
|
15157
15313
|
var KEY_BENCHMARK_HISTORY = "benchmarkHistory";
|
|
15158
15314
|
var MAX_HISTORY_ENTRIES = 100;
|
|
15159
15315
|
var EMPTY_HISTORY_JSON = "[]";
|
|
15316
|
+
var BENCH_RUN_TIMEOUT_MS = Math.max(
|
|
15317
|
+
1e4,
|
|
15318
|
+
Number(process.env["CAMSTACK_BENCH_RUN_TIMEOUT_MS"]) || 12e4
|
|
15319
|
+
);
|
|
15320
|
+
async function withHardTimeout(promise2, ms, label) {
|
|
15321
|
+
let timer;
|
|
15322
|
+
const timeout = new Promise((_resolve, reject) => {
|
|
15323
|
+
timer = setTimeout(
|
|
15324
|
+
() => reject(new Error(`${label} exceeded ${ms}ms \u2014 aborting (likely a wedged step)`)),
|
|
15325
|
+
ms
|
|
15326
|
+
);
|
|
15327
|
+
timer.unref?.();
|
|
15328
|
+
});
|
|
15329
|
+
try {
|
|
15330
|
+
return await Promise.race([promise2, timeout]);
|
|
15331
|
+
} finally {
|
|
15332
|
+
if (timer) clearTimeout(timer);
|
|
15333
|
+
}
|
|
15334
|
+
}
|
|
15160
15335
|
var HistoryArraySchema = external_exports.array(HistoryEntrySchema);
|
|
15161
15336
|
var FIXTURE_META = {
|
|
15162
15337
|
"camera-sim-360p.h264": {
|
|
@@ -15184,6 +15359,8 @@ var BenchmarkAddon = class extends import_types4.BaseAddon {
|
|
|
15184
15359
|
id = "benchmark";
|
|
15185
15360
|
/** Per-sessionId abort flags for in-flight decoder perf runs. */
|
|
15186
15361
|
decoderPerfAbortFlags = /* @__PURE__ */ new Map();
|
|
15362
|
+
/** Per-sessionId abort flags for in-flight sustained-throughput runs. */
|
|
15363
|
+
sustainedAbortFlags = /* @__PURE__ */ new Map();
|
|
15187
15364
|
/**
|
|
15188
15365
|
* In-memory cache of original JPEG bytes keyed by `frameId`. Populated by
|
|
15189
15366
|
* `cacheBenchFrame` (UI uploads JPEG once at bench start). Subsequent
|
|
@@ -15226,6 +15403,8 @@ var BenchmarkAddon = class extends import_types4.BaseAddon {
|
|
|
15226
15403
|
runPipelineTest: async (input) => this.runPipelineTest(input),
|
|
15227
15404
|
runPipelineBatchTest: async (input) => this.runPipelineBatchTest(input),
|
|
15228
15405
|
runMultiCameraStressTest: async (input) => this.runMultiCameraStressTest(input),
|
|
15406
|
+
runSustainedThroughput: async (input) => this.runSustainedThroughput(input),
|
|
15407
|
+
abortSustainedThroughput: async (input) => this.abortSustainedThroughput(input),
|
|
15229
15408
|
cacheBenchFrame: async (input) => this.cacheBenchFrame(input),
|
|
15230
15409
|
releaseBenchFrame: async (input) => this.releaseBenchFrame(input),
|
|
15231
15410
|
listBenchmarkHistory: async () => this.listBenchmarkHistory(),
|
|
@@ -15245,6 +15424,8 @@ var BenchmarkAddon = class extends import_types4.BaseAddon {
|
|
|
15245
15424
|
this.frameCacheSweeper = null;
|
|
15246
15425
|
}
|
|
15247
15426
|
this.frameCache.clear();
|
|
15427
|
+
for (const flag of this.sustainedAbortFlags.values()) flag.aborted = true;
|
|
15428
|
+
this.sustainedAbortFlags.clear();
|
|
15248
15429
|
}
|
|
15249
15430
|
// ── Frame cache (raw RGB) ────────────────────────────────────────────────
|
|
15250
15431
|
async cacheBenchFrame(input) {
|
|
@@ -15314,15 +15495,19 @@ var BenchmarkAddon = class extends import_types4.BaseAddon {
|
|
|
15314
15495
|
});
|
|
15315
15496
|
return globalSteps.map(toInput);
|
|
15316
15497
|
})();
|
|
15317
|
-
return
|
|
15318
|
-
|
|
15319
|
-
|
|
15320
|
-
|
|
15321
|
-
|
|
15322
|
-
|
|
15323
|
-
|
|
15324
|
-
|
|
15325
|
-
|
|
15498
|
+
return withHardTimeout(
|
|
15499
|
+
this.api.pipelineExecutor.runPipeline.mutate({
|
|
15500
|
+
nodeId,
|
|
15501
|
+
steps: stepsToRun,
|
|
15502
|
+
...image ? { image } : {},
|
|
15503
|
+
...!image && imageBase64 ? { imageBase64 } : {},
|
|
15504
|
+
referenceImage,
|
|
15505
|
+
sessionId,
|
|
15506
|
+
...engine ? { engine } : {}
|
|
15507
|
+
}),
|
|
15508
|
+
BENCH_RUN_TIMEOUT_MS,
|
|
15509
|
+
"runPipelineTest"
|
|
15510
|
+
);
|
|
15326
15511
|
}
|
|
15327
15512
|
/**
|
|
15328
15513
|
* Batched pipeline tester.
|
|
@@ -15375,13 +15560,17 @@ var BenchmarkAddon = class extends import_types4.BaseAddon {
|
|
|
15375
15560
|
timestamp: Date.now()
|
|
15376
15561
|
}));
|
|
15377
15562
|
const callStart = Date.now();
|
|
15378
|
-
const out = await
|
|
15379
|
-
|
|
15380
|
-
|
|
15381
|
-
|
|
15382
|
-
|
|
15383
|
-
|
|
15384
|
-
|
|
15563
|
+
const out = await withHardTimeout(
|
|
15564
|
+
this.api.pipelineExecutor.runPipelineBatch.mutate({
|
|
15565
|
+
nodeId,
|
|
15566
|
+
steps: [...steps],
|
|
15567
|
+
frames,
|
|
15568
|
+
sessionId,
|
|
15569
|
+
...engine ? { engine } : {}
|
|
15570
|
+
}),
|
|
15571
|
+
BENCH_RUN_TIMEOUT_MS,
|
|
15572
|
+
"runPipelineBatchTest"
|
|
15573
|
+
);
|
|
15385
15574
|
const callMs = Date.now() - callStart;
|
|
15386
15575
|
const inferMs = out.results.reduce((acc, r) => Math.max(acc, r.debug?.totalInferenceMs ?? 0), 0);
|
|
15387
15576
|
return {
|
|
@@ -15526,6 +15715,133 @@ var BenchmarkAddon = class extends import_types4.BaseAddon {
|
|
|
15526
15715
|
errors
|
|
15527
15716
|
};
|
|
15528
15717
|
}
|
|
15718
|
+
// ── Sustained throughput (root-detection e2e) ────────────────────────────
|
|
15719
|
+
/**
|
|
15720
|
+
* SUSTAINED THROUGHPUT test of ROOT detection.
|
|
15721
|
+
*
|
|
15722
|
+
* 1. Pin ONE frame in memory: resolve `frameId` (from `cacheBenchFrame`)
|
|
15723
|
+
* or the one-shot `imageBase64`, decode + square-resize ONCE to raw
|
|
15724
|
+
* RGB. Every node + every iteration consumes the IDENTICAL buffer — no
|
|
15725
|
+
* per-iteration decode or upload.
|
|
15726
|
+
* 2. For each target node, run a `concurrency`-bounded loop of
|
|
15727
|
+
* `runPipelineBatch` (batchSize frames/call) on the node's ROOT-ONLY
|
|
15728
|
+
* steps until `durationSec` elapses (or the stop flag fires). Root-only
|
|
15729
|
+
* steps stay on the batch single-root fast path == live `plane:'frame'`
|
|
15730
|
+
* root detection.
|
|
15731
|
+
* 3. Aggregate sustained fps / det/s + latency percentiles per node on the
|
|
15732
|
+
* identical pinned frame for apples-to-apples comparison.
|
|
15733
|
+
*
|
|
15734
|
+
* Non-destructive: targets the benchmark's own cached frame (never a live
|
|
15735
|
+
* camera session), is explicitly started, and is bounded by N + the stop
|
|
15736
|
+
* button (`abortSustainedThroughput`).
|
|
15737
|
+
*/
|
|
15738
|
+
async runSustainedThroughput(input) {
|
|
15739
|
+
const { frameId, imageBase64, durationSec, batchSize, concurrency, sessionId, nodes } = input;
|
|
15740
|
+
const targetSize = input.targetSize ?? 640;
|
|
15741
|
+
const cached2 = frameId ? this.frameCache.get(frameId) : void 0;
|
|
15742
|
+
if (frameId && !cached2)
|
|
15743
|
+
throw new Error(
|
|
15744
|
+
`runSustainedThroughput: frameId ${frameId} not in cache (expired or never cached)`
|
|
15745
|
+
);
|
|
15746
|
+
const jpegSource = cached2 ? cached2.jpeg : imageBase64 ? Buffer.from(imageBase64, "base64") : null;
|
|
15747
|
+
if (!jpegSource)
|
|
15748
|
+
throw new Error("runSustainedThroughput: no frame source (need frameId or imageBase64)");
|
|
15749
|
+
const sharp = (await import("sharp")).default;
|
|
15750
|
+
const rgb = await sharp(jpegSource).resize({ width: targetSize, height: targetSize, fit: "fill" }).raw().toColorspace("srgb").toBuffer();
|
|
15751
|
+
const rgbBytes = new Uint8Array(rgb.buffer, rgb.byteOffset, rgb.byteLength);
|
|
15752
|
+
const batchFrames = Array.from({ length: batchSize }, () => ({
|
|
15753
|
+
data: rgbBytes,
|
|
15754
|
+
format: "rgb",
|
|
15755
|
+
width: targetSize,
|
|
15756
|
+
height: targetSize,
|
|
15757
|
+
timestamp: Date.now()
|
|
15758
|
+
}));
|
|
15759
|
+
const benchFrameId = Math.floor(Math.random() * 4294967294) + 1 >>> 0;
|
|
15760
|
+
const abortFlag = { aborted: false };
|
|
15761
|
+
this.sustainedAbortFlags.set(sessionId, abortFlag);
|
|
15762
|
+
this.ctx.logger.info("runSustainedThroughput start", {
|
|
15763
|
+
meta: {
|
|
15764
|
+
sessionId,
|
|
15765
|
+
durationSec,
|
|
15766
|
+
batchSize,
|
|
15767
|
+
concurrency,
|
|
15768
|
+
targetSize,
|
|
15769
|
+
nodeCount: nodes.length,
|
|
15770
|
+
nodes: nodes.map((n) => n.nodeId ?? "hub")
|
|
15771
|
+
}
|
|
15772
|
+
});
|
|
15773
|
+
try {
|
|
15774
|
+
const perNode = await Promise.all(
|
|
15775
|
+
nodes.map(async (node) => {
|
|
15776
|
+
if (node.steps.length === 0)
|
|
15777
|
+
throw new Error(
|
|
15778
|
+
`runSustainedThroughput: node ${node.nodeId ?? "hub"} has no root steps`
|
|
15779
|
+
);
|
|
15780
|
+
const dispatchBatch = async () => {
|
|
15781
|
+
const out = await this.api.pipelineExecutor.runPipelineBatch.mutate({
|
|
15782
|
+
nodeId: node.nodeId,
|
|
15783
|
+
steps: [...node.steps],
|
|
15784
|
+
frames: [...batchFrames],
|
|
15785
|
+
sessionId,
|
|
15786
|
+
frameId: benchFrameId,
|
|
15787
|
+
...node.engine ? { engine: node.engine } : {}
|
|
15788
|
+
});
|
|
15789
|
+
return {
|
|
15790
|
+
frameCount: out.results.length,
|
|
15791
|
+
detectionCounts: out.results.map((r) => r.detections.length)
|
|
15792
|
+
};
|
|
15793
|
+
};
|
|
15794
|
+
let raw;
|
|
15795
|
+
try {
|
|
15796
|
+
raw = await runSustainedLoop({
|
|
15797
|
+
dispatchBatch,
|
|
15798
|
+
concurrency,
|
|
15799
|
+
durationMs: durationSec * 1e3,
|
|
15800
|
+
now: () => Date.now(),
|
|
15801
|
+
shouldStop: () => abortFlag.aborted
|
|
15802
|
+
});
|
|
15803
|
+
} finally {
|
|
15804
|
+
await this.api.pipelineExecutor.uncacheFrame.mutate({ nodeId: node.nodeId, frameId: benchFrameId }).catch(() => {
|
|
15805
|
+
});
|
|
15806
|
+
}
|
|
15807
|
+
const summary = {
|
|
15808
|
+
nodeId: node.nodeId ?? null,
|
|
15809
|
+
...node.label ? { label: node.label } : {},
|
|
15810
|
+
totalFrames: raw.totalFrames,
|
|
15811
|
+
totalBatches: raw.totalBatches,
|
|
15812
|
+
wallMs: raw.wallMs,
|
|
15813
|
+
fps: computeFps(raw.totalFrames, raw.wallMs),
|
|
15814
|
+
detsPerSec: computeDetsPerSec(raw.totalDetections, raw.wallMs),
|
|
15815
|
+
avgDetections: computeAvgDetections(raw.totalDetections, raw.detectionSamples),
|
|
15816
|
+
latency: computeLatencyStats(raw.callMsList),
|
|
15817
|
+
errors: raw.errors,
|
|
15818
|
+
aborted: raw.aborted
|
|
15819
|
+
};
|
|
15820
|
+
this.ctx.logger.info("runSustainedThroughput node done", {
|
|
15821
|
+
meta: {
|
|
15822
|
+
nodeId: summary.nodeId,
|
|
15823
|
+
fps: Number(summary.fps.toFixed(2)),
|
|
15824
|
+
detsPerSec: Number(summary.detsPerSec.toFixed(2)),
|
|
15825
|
+
p95Ms: Number(summary.latency.p95.toFixed(2)),
|
|
15826
|
+
totalFrames: summary.totalFrames,
|
|
15827
|
+
errors: summary.errors,
|
|
15828
|
+
aborted: summary.aborted
|
|
15829
|
+
}
|
|
15830
|
+
});
|
|
15831
|
+
return summary;
|
|
15832
|
+
})
|
|
15833
|
+
);
|
|
15834
|
+
return { perNode, durationSec, batchSize, concurrency };
|
|
15835
|
+
} finally {
|
|
15836
|
+
this.sustainedAbortFlags.delete(sessionId);
|
|
15837
|
+
}
|
|
15838
|
+
}
|
|
15839
|
+
async abortSustainedThroughput(input) {
|
|
15840
|
+
const flag = this.sustainedAbortFlags.get(input.sessionId);
|
|
15841
|
+
if (!flag) return { aborted: false };
|
|
15842
|
+
flag.aborted = true;
|
|
15843
|
+
return { aborted: true };
|
|
15844
|
+
}
|
|
15529
15845
|
// ── Decoder perf — fixture discovery + run orchestration ────────────────
|
|
15530
15846
|
async listDecoderFixtures() {
|
|
15531
15847
|
const dir = resolveFixturesDir();
|