@adaas/a-concept 0.3.6 → 0.3.7
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/README.md +116 -78
- package/benchmarks/feature-lifecycle.bench.ts +245 -0
- package/benchmarks/feature-profiling.bench.ts +415 -0
- package/benchmarks/feature-template.bench.ts +211 -0
- package/benchmarks/helpers.ts +97 -0
- package/benchmarks/run-all.ts +58 -0
- package/benchmarks/scope-resolve.bench.ts +245 -0
- package/benchmarks/step-manager.bench.ts +146 -0
- package/dist/browser/index.d.mts +70 -2
- package/dist/browser/index.mjs +2 -2
- package/dist/browser/index.mjs.map +1 -1
- package/dist/node/index.cjs +209 -53
- package/dist/node/index.cjs.map +1 -1
- package/dist/node/index.d.mts +70 -2
- package/dist/node/index.d.ts +70 -2
- package/dist/node/index.mjs +209 -53
- package/dist/node/index.mjs.map +1 -1
- package/package.json +9 -1
- package/src/lib/A-Context/A-Context.class.ts +155 -60
- package/src/lib/A-Feature/A-Feature.types.ts +6 -10
- package/src/lib/A-Meta/A-Meta.class.ts +18 -3
- package/src/lib/A-Scope/A-Scope.class.ts +98 -5
- package/tsconfig.json +1 -0
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feature Lifecycle Profiling Benchmark
|
|
3
|
+
*
|
|
4
|
+
* Realistic profiling of the A_Feature lifecycle using the actual production path:
|
|
5
|
+
* new A_Feature({ name, component, scope }) → feature.process(scope)
|
|
6
|
+
*
|
|
7
|
+
* Uses 25 extension components (≈26 stages total) to simulate a real-world
|
|
8
|
+
* scenario where many components extend the same feature.
|
|
9
|
+
*
|
|
10
|
+
* Two profiling modes:
|
|
11
|
+
* 1. Black-box: times `new A_Feature(...)` and `.process()` as the user sees them
|
|
12
|
+
* 2. Internals breakdown: manually instruments fromComponent() sub-steps
|
|
13
|
+
*/
|
|
14
|
+
import { A_Context } from '@adaas/a-concept/a-context';
|
|
15
|
+
import { A_Component } from '@adaas/a-concept/a-component';
|
|
16
|
+
import { A_Container } from '@adaas/a-concept/a-container';
|
|
17
|
+
import { A_Feature } from '@adaas/a-concept/a-feature';
|
|
18
|
+
import { A_Scope } from '@adaas/a-concept/a-scope';
|
|
19
|
+
import { A_Inject } from '@adaas/a-concept/a-inject';
|
|
20
|
+
import { A_Caller } from '@adaas/a-concept/a-caller';
|
|
21
|
+
import { A_StepsManager } from '@adaas/a-concept/a-step-manager';
|
|
22
|
+
import { A_TypeGuards } from '@adaas/a-concept/helpers/A_TypeGuards.helper';
|
|
23
|
+
|
|
24
|
+
// ── helpers ──────────────────────────────────────────────────────
|
|
25
|
+
function hrMs(start: [number, number]): number {
|
|
26
|
+
const diff = process.hrtime(start);
|
|
27
|
+
return diff[0] * 1000 + diff[1] / 1e6;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function median(arr: number[]): number {
|
|
31
|
+
const s = [...arr].sort((a, b) => a - b);
|
|
32
|
+
const mid = Math.floor(s.length / 2);
|
|
33
|
+
return s.length % 2 ? s[mid] : (s[mid - 1] + s[mid]) / 2;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function p99(arr: number[]): number {
|
|
37
|
+
const s = [...arr].sort((a, b) => a - b);
|
|
38
|
+
return s[Math.floor(s.length * 0.99)];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function avg(arr: number[]): number {
|
|
42
|
+
return arr.reduce((a, b) => a + b, 0) / arr.length;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ── dynamic component generation ─────────────────────────────────
|
|
46
|
+
const NUM_EXTENSIONS = 25;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Dynamically generates N extension component classes that extend the same feature.
|
|
50
|
+
* Each component has a single handler with @A_Inject(A_Caller).
|
|
51
|
+
*/
|
|
52
|
+
function generateExtensionComponents(count: number): Array<typeof A_Component> {
|
|
53
|
+
const components: Array<typeof A_Component> = [];
|
|
54
|
+
|
|
55
|
+
for (let i = 0; i < count; i++) {
|
|
56
|
+
// Create a unique class per extension
|
|
57
|
+
const ExtComp = class extends A_Component {
|
|
58
|
+
handler(caller: any) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Give it a unique name for debugging
|
|
64
|
+
Object.defineProperty(ExtComp, 'name', { value: `Ext_${i}` });
|
|
65
|
+
|
|
66
|
+
// Apply decorators programmatically (same as @A_Feature.Extend + @A_Inject)
|
|
67
|
+
A_Feature.Extend({ name: 'profileFeature' })(
|
|
68
|
+
ExtComp.prototype,
|
|
69
|
+
'handler',
|
|
70
|
+
Object.getOwnPropertyDescriptor(ExtComp.prototype, 'handler')!
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
A_Inject(A_Caller)(ExtComp.prototype, 'handler', 0);
|
|
74
|
+
|
|
75
|
+
components.push(ExtComp);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return components;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ── setup ────────────────────────────────────────────────────────
|
|
82
|
+
function setupEnvironment() {
|
|
83
|
+
A_Context.reset();
|
|
84
|
+
|
|
85
|
+
class ProfilingComponent extends A_Component {
|
|
86
|
+
@A_Feature.Define({ name: 'profileFeature', invoke: true })
|
|
87
|
+
profileHandler(
|
|
88
|
+
@A_Inject(A_Caller) caller: any
|
|
89
|
+
) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const extensionComponents = generateExtensionComponents(NUM_EXTENSIONS);
|
|
95
|
+
|
|
96
|
+
const container = new A_Container({
|
|
97
|
+
name: 'ProfilingContainer',
|
|
98
|
+
components: [ProfilingComponent, ...extensionComponents]
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const component = container.scope.resolve(ProfilingComponent)!;
|
|
102
|
+
const stageCount = 1 + NUM_EXTENSIONS; // 1 define + N extensions
|
|
103
|
+
|
|
104
|
+
return { container, component, ProfilingComponent, extensionComponents, stageCount };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ── profiling run ────────────────────────────────────────────────
|
|
108
|
+
export async function runFeatureProfilingBenchmarks() {
|
|
109
|
+
const ITERATIONS = 2000;
|
|
110
|
+
const WARMUP = 200;
|
|
111
|
+
|
|
112
|
+
const { container, component, stageCount } = setupEnvironment();
|
|
113
|
+
|
|
114
|
+
// ═══════════════════════════════════════════════════════════════
|
|
115
|
+
// PART 1: Black-box profiling (realistic production path)
|
|
116
|
+
// ═══════════════════════════════════════════════════════════════
|
|
117
|
+
const blackBox = {
|
|
118
|
+
construct: [] as number[],
|
|
119
|
+
process: [] as number[],
|
|
120
|
+
total: [] as number[],
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
for (let i = 0; i < ITERATIONS + WARMUP; i++) {
|
|
124
|
+
const isWarmup = i < WARMUP;
|
|
125
|
+
|
|
126
|
+
const tTotal = process.hrtime();
|
|
127
|
+
|
|
128
|
+
// This is exactly how features are called in production:
|
|
129
|
+
// const newFeature = new A_Feature({ name, component, scope });
|
|
130
|
+
// return await newFeature.process(scope);
|
|
131
|
+
const tConstruct = process.hrtime();
|
|
132
|
+
const feature = new A_Feature({
|
|
133
|
+
name: 'profileFeature',
|
|
134
|
+
component: component,
|
|
135
|
+
});
|
|
136
|
+
const constructTime = hrMs(tConstruct);
|
|
137
|
+
|
|
138
|
+
const tProcess = process.hrtime();
|
|
139
|
+
feature.process();
|
|
140
|
+
const processTime = hrMs(tProcess);
|
|
141
|
+
|
|
142
|
+
const totalTime = hrMs(tTotal);
|
|
143
|
+
|
|
144
|
+
if (!isWarmup) {
|
|
145
|
+
blackBox.construct.push(constructTime);
|
|
146
|
+
blackBox.process.push(processTime);
|
|
147
|
+
blackBox.total.push(totalTime);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ═══════════════════════════════════════════════════════════════
|
|
152
|
+
// PART 2: Internals breakdown (mirrors fromComponent sub-steps)
|
|
153
|
+
// ═══════════════════════════════════════════════════════════════
|
|
154
|
+
const internals = {
|
|
155
|
+
scopeGet: [] as number[],
|
|
156
|
+
callerCreate: [] as number[],
|
|
157
|
+
scopeAllocate: [] as number[],
|
|
158
|
+
scopeInherit: [] as number[],
|
|
159
|
+
featureTemplate: [] as number[],
|
|
160
|
+
stepsManagerCreate: [] as number[],
|
|
161
|
+
toStages: [] as number[],
|
|
162
|
+
totalConstruct: [] as number[],
|
|
163
|
+
// per-stage breakdown (aggregated across all stages in each iteration)
|
|
164
|
+
stageProcess: [] as number[],
|
|
165
|
+
stageGetComponent: [] as number[],
|
|
166
|
+
stageGetArgs: [] as number[],
|
|
167
|
+
stageCallHandler: [] as number[],
|
|
168
|
+
totalProcess: [] as number[],
|
|
169
|
+
scopeDestroy: [] as number[],
|
|
170
|
+
totalLifecycle: [] as number[],
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
for (let i = 0; i < ITERATIONS + WARMUP; i++) {
|
|
174
|
+
const isWarmup = i < WARMUP;
|
|
175
|
+
|
|
176
|
+
// ─── CONSTRUCTION PHASE (mirrors fromComponent) ──────────
|
|
177
|
+
const tTotalStart = process.hrtime();
|
|
178
|
+
|
|
179
|
+
// 1) Get component scope
|
|
180
|
+
const t1 = process.hrtime();
|
|
181
|
+
const componentScope = A_Context.scope(component);
|
|
182
|
+
const scopeGetTime = hrMs(t1);
|
|
183
|
+
|
|
184
|
+
// 2) Create caller
|
|
185
|
+
const t2 = process.hrtime();
|
|
186
|
+
const caller = new A_Caller(component);
|
|
187
|
+
const callerCreateTime = hrMs(t2);
|
|
188
|
+
|
|
189
|
+
// 3) Allocate scope
|
|
190
|
+
const t3 = process.hrtime();
|
|
191
|
+
const featurePlaceholder = Object.create(A_Feature.prototype);
|
|
192
|
+
featurePlaceholder._name = 'profileFeature';
|
|
193
|
+
featurePlaceholder._caller = caller;
|
|
194
|
+
featurePlaceholder._state = 0;
|
|
195
|
+
featurePlaceholder._stages = [];
|
|
196
|
+
featurePlaceholder._index = 0;
|
|
197
|
+
const scope = A_Context.allocate(featurePlaceholder);
|
|
198
|
+
const scopeAllocateTime = hrMs(t3);
|
|
199
|
+
|
|
200
|
+
// 4) Inherit scope
|
|
201
|
+
const t4 = process.hrtime();
|
|
202
|
+
scope.inherit(componentScope);
|
|
203
|
+
const scopeInheritTime = hrMs(t4);
|
|
204
|
+
|
|
205
|
+
// 5) Get feature template (this is the key step — same as fromComponent step 6)
|
|
206
|
+
const t5 = process.hrtime();
|
|
207
|
+
const template = A_Context.featureTemplate('profileFeature', caller.component, scope);
|
|
208
|
+
const featureTemplateTime = hrMs(t5);
|
|
209
|
+
|
|
210
|
+
// 6) Create StepsManager
|
|
211
|
+
const t6 = process.hrtime();
|
|
212
|
+
const sm = new A_StepsManager(template as any[]);
|
|
213
|
+
const stepsManagerCreateTime = hrMs(t6);
|
|
214
|
+
|
|
215
|
+
// 7) Create stages
|
|
216
|
+
const t7 = process.hrtime();
|
|
217
|
+
const stages = sm.toStages(featurePlaceholder);
|
|
218
|
+
const toStagesTime = hrMs(t7);
|
|
219
|
+
|
|
220
|
+
const constructTime = hrMs(tTotalStart);
|
|
221
|
+
|
|
222
|
+
// ─── PROCESSING PHASE (mirrors stage.process internals) ──
|
|
223
|
+
const tProcessStart = process.hrtime();
|
|
224
|
+
|
|
225
|
+
for (const stage of stages) {
|
|
226
|
+
const tStage = process.hrtime();
|
|
227
|
+
|
|
228
|
+
// a) getStepComponent — resolve component instance
|
|
229
|
+
const tComp = process.hrtime();
|
|
230
|
+
const dep = stage.definition.dependency;
|
|
231
|
+
const instance = scope.resolve(dep) || scope.resolve(dep);
|
|
232
|
+
const getComponentTime = hrMs(tComp);
|
|
233
|
+
|
|
234
|
+
// b) getStepArgs — DI resolution
|
|
235
|
+
const tArgs = process.hrtime();
|
|
236
|
+
let resolverConstructor =
|
|
237
|
+
(stage.definition.dependency.target as any)
|
|
238
|
+
|| scope.resolveConstructor(stage.definition.dependency.name);
|
|
239
|
+
const injections = A_Context
|
|
240
|
+
.meta(resolverConstructor)
|
|
241
|
+
.injections(stage.definition.handler);
|
|
242
|
+
const resolvedArgs = injections.map((dependency: any) => {
|
|
243
|
+
if (A_TypeGuards.isCallerConstructor(dependency.target)) {
|
|
244
|
+
return caller.component;
|
|
245
|
+
}
|
|
246
|
+
if (A_TypeGuards.isFeatureConstructor(dependency.target)) {
|
|
247
|
+
return featurePlaceholder;
|
|
248
|
+
}
|
|
249
|
+
return scope.resolve(dependency);
|
|
250
|
+
});
|
|
251
|
+
const getArgsTime = hrMs(tArgs);
|
|
252
|
+
|
|
253
|
+
// c) actual handler call
|
|
254
|
+
const tCall = process.hrtime();
|
|
255
|
+
if (instance && (instance as any)[stage.definition.handler]) {
|
|
256
|
+
(instance as any)[stage.definition.handler].call(instance, ...resolvedArgs);
|
|
257
|
+
}
|
|
258
|
+
const callHandlerTime = hrMs(tCall);
|
|
259
|
+
|
|
260
|
+
const stageTime = hrMs(tStage);
|
|
261
|
+
|
|
262
|
+
if (!isWarmup) {
|
|
263
|
+
internals.stageProcess.push(stageTime);
|
|
264
|
+
internals.stageGetComponent.push(getComponentTime);
|
|
265
|
+
internals.stageGetArgs.push(getArgsTime);
|
|
266
|
+
internals.stageCallHandler.push(callHandlerTime);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const processTime = hrMs(tProcessStart);
|
|
271
|
+
|
|
272
|
+
// CLEANUP
|
|
273
|
+
const tDestroy = process.hrtime();
|
|
274
|
+
scope.destroy();
|
|
275
|
+
const destroyTime = hrMs(tDestroy);
|
|
276
|
+
|
|
277
|
+
try { A_Context.deallocate(featurePlaceholder); } catch (e) { }
|
|
278
|
+
|
|
279
|
+
const totalTime = hrMs(tTotalStart);
|
|
280
|
+
|
|
281
|
+
if (!isWarmup) {
|
|
282
|
+
internals.scopeGet.push(scopeGetTime);
|
|
283
|
+
internals.callerCreate.push(callerCreateTime);
|
|
284
|
+
internals.scopeAllocate.push(scopeAllocateTime);
|
|
285
|
+
internals.scopeInherit.push(scopeInheritTime);
|
|
286
|
+
internals.featureTemplate.push(featureTemplateTime);
|
|
287
|
+
internals.stepsManagerCreate.push(stepsManagerCreateTime);
|
|
288
|
+
internals.toStages.push(toStagesTime);
|
|
289
|
+
internals.scopeDestroy.push(destroyTime);
|
|
290
|
+
internals.totalConstruct.push(constructTime);
|
|
291
|
+
internals.totalProcess.push(processTime);
|
|
292
|
+
internals.totalLifecycle.push(totalTime);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// ── Print results ────────────────────────────────────────
|
|
297
|
+
console.log('\n══════════════════════════════════════════════════════════════════════════════');
|
|
298
|
+
console.log(` 📊 A_Feature Lifecycle — Realistic Profiling (${stageCount} stages: 1 define + ${NUM_EXTENSIONS} extensions)`);
|
|
299
|
+
console.log('══════════════════════════════════════════════════════════════════════════════\n');
|
|
300
|
+
|
|
301
|
+
// ── Part 1: Black-box results ────────────────────────────
|
|
302
|
+
console.log(' ┌─────────────────────────────────────────────────────────────────────────┐');
|
|
303
|
+
console.log(' │ PART 1: Production Path (new A_Feature + .process()) │');
|
|
304
|
+
console.log(' └─────────────────────────────────────────────────────────────────────────┘\n');
|
|
305
|
+
|
|
306
|
+
const bbPhases: [string, keyof typeof blackBox][] = [
|
|
307
|
+
[' new A_Feature({ component })', 'construct'],
|
|
308
|
+
[' feature.process()', 'process'],
|
|
309
|
+
[' ═══ TOTAL', 'total'],
|
|
310
|
+
];
|
|
311
|
+
|
|
312
|
+
console.log(
|
|
313
|
+
' Phase'.padEnd(44) +
|
|
314
|
+
'median'.padStart(10) +
|
|
315
|
+
'avg'.padStart(10) +
|
|
316
|
+
'p99'.padStart(10) +
|
|
317
|
+
'min'.padStart(10) +
|
|
318
|
+
'max'.padStart(10)
|
|
319
|
+
);
|
|
320
|
+
console.log(' ' + '─'.repeat(90));
|
|
321
|
+
|
|
322
|
+
for (const [label, key] of bbPhases) {
|
|
323
|
+
const arr = blackBox[key];
|
|
324
|
+
const med = median(arr);
|
|
325
|
+
const a = avg(arr);
|
|
326
|
+
const p = p99(arr);
|
|
327
|
+
const mn = Math.min(...arr);
|
|
328
|
+
const mx = Math.max(...arr);
|
|
329
|
+
const pct = (a / avg(blackBox.total) * 100).toFixed(1);
|
|
330
|
+
|
|
331
|
+
console.log(
|
|
332
|
+
` ${label.padEnd(42)}` +
|
|
333
|
+
`${med.toFixed(4).padStart(10)}` +
|
|
334
|
+
`${a.toFixed(4).padStart(10)}` +
|
|
335
|
+
`${p.toFixed(4).padStart(10)}` +
|
|
336
|
+
`${mn.toFixed(4).padStart(10)}` +
|
|
337
|
+
`${mx.toFixed(4).padStart(10)}` +
|
|
338
|
+
` (${pct}%)`
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// ── Part 2: Internals breakdown ──────────────────────────
|
|
343
|
+
console.log('\n\n ┌─────────────────────────────────────────────────────────────────────────┐');
|
|
344
|
+
console.log(' │ PART 2: Internals Breakdown (fromComponent sub-steps) │');
|
|
345
|
+
console.log(' └─────────────────────────────────────────────────────────────────────────┘\n');
|
|
346
|
+
|
|
347
|
+
const phases: [string, string][] = [
|
|
348
|
+
['CONSTRUCTION PHASE', ''],
|
|
349
|
+
[' A_Context.scope(component)', 'scopeGet'],
|
|
350
|
+
[' new A_Caller()', 'callerCreate'],
|
|
351
|
+
[' A_Context.allocate()', 'scopeAllocate'],
|
|
352
|
+
[' scope.inherit()', 'scopeInherit'],
|
|
353
|
+
[' A_Context.featureTemplate()', 'featureTemplate'],
|
|
354
|
+
[' new A_StepsManager()', 'stepsManagerCreate'],
|
|
355
|
+
[' SM.toStages()', 'toStages'],
|
|
356
|
+
[' ─── Construction Total', 'totalConstruct'],
|
|
357
|
+
['', ''],
|
|
358
|
+
[`PROCESSING PHASE (per-stage avg, ${stageCount} stages)`, ''],
|
|
359
|
+
[' getStepComponent (resolve)', 'stageGetComponent'],
|
|
360
|
+
[' getStepArgs (DI injection)', 'stageGetArgs'],
|
|
361
|
+
[' handler call', 'stageCallHandler'],
|
|
362
|
+
[' ─── Stage Total (per-stage)', 'stageProcess'],
|
|
363
|
+
[' ─── All Stages Total', 'totalProcess'],
|
|
364
|
+
['', ''],
|
|
365
|
+
['CLEANUP PHASE', ''],
|
|
366
|
+
[' scope.destroy()', 'scopeDestroy'],
|
|
367
|
+
['', ''],
|
|
368
|
+
['═══ TOTAL LIFECYCLE', 'totalLifecycle'],
|
|
369
|
+
];
|
|
370
|
+
|
|
371
|
+
console.log(
|
|
372
|
+
' Phase'.padEnd(44) +
|
|
373
|
+
'median'.padStart(10) +
|
|
374
|
+
'avg'.padStart(10) +
|
|
375
|
+
'p99'.padStart(10) +
|
|
376
|
+
'min'.padStart(10) +
|
|
377
|
+
'max'.padStart(10)
|
|
378
|
+
);
|
|
379
|
+
console.log(' ' + '─'.repeat(90));
|
|
380
|
+
|
|
381
|
+
for (const [label, key] of phases) {
|
|
382
|
+
if (!key) {
|
|
383
|
+
if (label) console.log(`\n ${label}`);
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
const arr = (internals as any)[key] as number[];
|
|
387
|
+
const med = median(arr);
|
|
388
|
+
const a = avg(arr);
|
|
389
|
+
const p = p99(arr);
|
|
390
|
+
const mn = Math.min(...arr);
|
|
391
|
+
const mx = Math.max(...arr);
|
|
392
|
+
|
|
393
|
+
const pctOfTotal = (a / avg(internals.totalLifecycle) * 100).toFixed(1);
|
|
394
|
+
|
|
395
|
+
console.log(
|
|
396
|
+
` ${label.padEnd(42)}` +
|
|
397
|
+
`${med.toFixed(4).padStart(10)}` +
|
|
398
|
+
`${a.toFixed(4).padStart(10)}` +
|
|
399
|
+
`${p.toFixed(4).padStart(10)}` +
|
|
400
|
+
`${mn.toFixed(4).padStart(10)}` +
|
|
401
|
+
`${mx.toFixed(4).padStart(10)}` +
|
|
402
|
+
` (${pctOfTotal}%)`
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
console.log('\n ' + '─'.repeat(90));
|
|
407
|
+
console.log(` Iterations: ${ITERATIONS} (after ${WARMUP} warmup)`);
|
|
408
|
+
console.log(` Stages per feature: ${stageCount} (1 define + ${NUM_EXTENSIONS} extensions)`);
|
|
409
|
+
console.log(` Production path: new A_Feature({ name, component }) → feature.process()\n`);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Run standalone
|
|
413
|
+
if (require.main === module) {
|
|
414
|
+
runFeatureProfilingBenchmarks().catch(console.error);
|
|
415
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================
|
|
3
|
+
* A_Context.featureTemplate Performance Benchmarks
|
|
4
|
+
* ============================================================
|
|
5
|
+
*
|
|
6
|
+
* Measures:
|
|
7
|
+
* - featureTemplate() — full template assembly
|
|
8
|
+
* - featureDefinition() — base definition lookup
|
|
9
|
+
* - featureExtensions() — extension collection from scope
|
|
10
|
+
* - Scaling with number of components in scope
|
|
11
|
+
*/
|
|
12
|
+
import { A_Component } from "@adaas/a-concept/a-component";
|
|
13
|
+
import { A_Feature } from "@adaas/a-concept/a-feature";
|
|
14
|
+
import { A_Scope } from "@adaas/a-concept/a-scope";
|
|
15
|
+
import { A_Context } from "@adaas/a-concept/a-context";
|
|
16
|
+
import { A_Inject } from "@adaas/a-concept/a-inject";
|
|
17
|
+
import { A_Caller } from "@adaas/a-concept/a-caller";
|
|
18
|
+
import { createSuite, BenchResult } from './helpers';
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
// ──────────────────────────────────────────────────────────────
|
|
22
|
+
// Fixture: components with feature definitions & extensions
|
|
23
|
+
// ──────────────────────────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
class CoreComponent extends A_Component {
|
|
26
|
+
@A_Feature.Define({ invoke: true })
|
|
27
|
+
@A_Feature.Extend({ name: 'coreFeature' })
|
|
28
|
+
async coreMethod() { }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
class ExtensionComponent_A extends A_Component {
|
|
32
|
+
@A_Feature.Extend({ name: 'coreFeature' })
|
|
33
|
+
async extensionA() { }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
class ExtensionComponent_B extends A_Component {
|
|
37
|
+
@A_Feature.Extend({ name: 'coreFeature' })
|
|
38
|
+
async extensionB() { }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
class ExtensionComponent_C extends A_Component {
|
|
42
|
+
@A_Feature.Extend({ name: 'coreFeature' })
|
|
43
|
+
async extensionC() { }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
class ExtensionComponent_D extends A_Component {
|
|
47
|
+
@A_Feature.Extend({ name: 'coreFeature' })
|
|
48
|
+
async extensionD() { }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
class ExtensionComponent_E extends A_Component {
|
|
52
|
+
@A_Feature.Extend({ name: 'coreFeature' })
|
|
53
|
+
async extensionE() { }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
// --- For inheritance tests ---
|
|
58
|
+
|
|
59
|
+
class ParentComponent extends A_Component {
|
|
60
|
+
@A_Feature.Define({ invoke: false })
|
|
61
|
+
async parentFeature() { }
|
|
62
|
+
|
|
63
|
+
@A_Feature.Extend({ name: 'parentFeature' })
|
|
64
|
+
async parentStep() { }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
class ChildComponent extends ParentComponent {
|
|
68
|
+
@A_Feature.Extend({ name: 'parentFeature' })
|
|
69
|
+
async childStep() { }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
class GrandchildComponent extends ChildComponent { }
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
// --- For multi-feature tests ---
|
|
76
|
+
|
|
77
|
+
class MultiFeatureComponent extends A_Component {
|
|
78
|
+
@A_Feature.Define({ invoke: true })
|
|
79
|
+
@A_Feature.Extend({ name: 'feature1' })
|
|
80
|
+
async feature1() { }
|
|
81
|
+
|
|
82
|
+
@A_Feature.Define({ invoke: true })
|
|
83
|
+
@A_Feature.Extend({ name: 'feature2' })
|
|
84
|
+
async feature2() { }
|
|
85
|
+
|
|
86
|
+
@A_Feature.Define({ invoke: true })
|
|
87
|
+
@A_Feature.Extend({ name: 'feature3' })
|
|
88
|
+
async feature3() { }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
// ──────────────────────────────────────────────────────────────
|
|
93
|
+
// Benchmark Suites
|
|
94
|
+
// ──────────────────────────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
export async function runFeatureTemplateBenchmarks(): Promise<BenchResult[]> {
|
|
97
|
+
const allResults: BenchResult[] = [];
|
|
98
|
+
|
|
99
|
+
// Suite 1: featureTemplate with varying scope sizes
|
|
100
|
+
const templateResults = await createSuite('A_Context.featureTemplate — Scope Size', (suite) => {
|
|
101
|
+
// Small scope: 2 components
|
|
102
|
+
const scopeSmall = new A_Scope({
|
|
103
|
+
name: 'SmallScope',
|
|
104
|
+
components: [CoreComponent, ExtensionComponent_A]
|
|
105
|
+
});
|
|
106
|
+
const compSmall = scopeSmall.resolve(CoreComponent)!;
|
|
107
|
+
|
|
108
|
+
// Medium scope: 4 components
|
|
109
|
+
const scopeMedium = new A_Scope({
|
|
110
|
+
name: 'MediumScope',
|
|
111
|
+
components: [CoreComponent, ExtensionComponent_A, ExtensionComponent_B, ExtensionComponent_C]
|
|
112
|
+
});
|
|
113
|
+
const compMedium = scopeMedium.resolve(CoreComponent)!;
|
|
114
|
+
|
|
115
|
+
// Large scope: 6 components
|
|
116
|
+
const scopeLarge = new A_Scope({
|
|
117
|
+
name: 'LargeScope',
|
|
118
|
+
components: [
|
|
119
|
+
CoreComponent,
|
|
120
|
+
ExtensionComponent_A,
|
|
121
|
+
ExtensionComponent_B,
|
|
122
|
+
ExtensionComponent_C,
|
|
123
|
+
ExtensionComponent_D,
|
|
124
|
+
ExtensionComponent_E
|
|
125
|
+
]
|
|
126
|
+
});
|
|
127
|
+
const compLarge = scopeLarge.resolve(CoreComponent)!;
|
|
128
|
+
|
|
129
|
+
suite
|
|
130
|
+
.add('featureTemplate (2 components)', () => {
|
|
131
|
+
A_Context.featureTemplate('coreFeature', compSmall, scopeSmall);
|
|
132
|
+
})
|
|
133
|
+
.add('featureTemplate (4 components)', () => {
|
|
134
|
+
A_Context.featureTemplate('coreFeature', compMedium, scopeMedium);
|
|
135
|
+
})
|
|
136
|
+
.add('featureTemplate (6 components)', () => {
|
|
137
|
+
A_Context.featureTemplate('coreFeature', compLarge, scopeLarge);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
allResults.push(...templateResults);
|
|
141
|
+
|
|
142
|
+
// Suite 2: featureDefinition vs featureExtensions breakdown
|
|
143
|
+
const breakdownResults = await createSuite('A_Context — Definition vs Extensions breakdown', (suite) => {
|
|
144
|
+
const scope = new A_Scope({
|
|
145
|
+
name: 'BreakdownScope',
|
|
146
|
+
components: [
|
|
147
|
+
CoreComponent,
|
|
148
|
+
ExtensionComponent_A,
|
|
149
|
+
ExtensionComponent_B,
|
|
150
|
+
ExtensionComponent_C,
|
|
151
|
+
]
|
|
152
|
+
});
|
|
153
|
+
const comp = scope.resolve(CoreComponent)!;
|
|
154
|
+
|
|
155
|
+
suite
|
|
156
|
+
.add('featureDefinition only', () => {
|
|
157
|
+
A_Context.featureDefinition('coreFeature', comp);
|
|
158
|
+
})
|
|
159
|
+
.add('featureExtensions only', () => {
|
|
160
|
+
A_Context.featureExtensions('coreFeature', comp, scope);
|
|
161
|
+
})
|
|
162
|
+
.add('featureTemplate (combined)', () => {
|
|
163
|
+
A_Context.featureTemplate('coreFeature', comp, scope);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
allResults.push(...breakdownResults);
|
|
167
|
+
|
|
168
|
+
// Suite 3: Inheritance depth impact
|
|
169
|
+
const inheritanceResults = await createSuite('A_Context.featureTemplate — Inheritance Depth', (suite) => {
|
|
170
|
+
// Direct component
|
|
171
|
+
const scopeDirect = new A_Scope({
|
|
172
|
+
name: 'DirectScope',
|
|
173
|
+
components: [ParentComponent]
|
|
174
|
+
});
|
|
175
|
+
const compDirect = scopeDirect.resolve(ParentComponent)!;
|
|
176
|
+
|
|
177
|
+
// 1-level inheritance
|
|
178
|
+
const scopeChild = new A_Scope({
|
|
179
|
+
name: 'ChildScope',
|
|
180
|
+
components: [ChildComponent]
|
|
181
|
+
});
|
|
182
|
+
const compChild = scopeChild.resolve(ChildComponent)!;
|
|
183
|
+
|
|
184
|
+
// 2-level inheritance
|
|
185
|
+
const scopeGrandchild = new A_Scope({
|
|
186
|
+
name: 'GrandchildScope',
|
|
187
|
+
components: [GrandchildComponent]
|
|
188
|
+
});
|
|
189
|
+
const compGrandchild = scopeGrandchild.resolve(GrandchildComponent)!;
|
|
190
|
+
|
|
191
|
+
suite
|
|
192
|
+
.add('featureTemplate (no inheritance)', () => {
|
|
193
|
+
A_Context.featureTemplate('parentFeature', compDirect, scopeDirect);
|
|
194
|
+
})
|
|
195
|
+
.add('featureTemplate (1-level inherit)', () => {
|
|
196
|
+
A_Context.featureTemplate('parentFeature', compChild, scopeChild);
|
|
197
|
+
})
|
|
198
|
+
.add('featureTemplate (2-level inherit)', () => {
|
|
199
|
+
A_Context.featureTemplate('parentFeature', compGrandchild, scopeGrandchild);
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
allResults.push(...inheritanceResults);
|
|
203
|
+
|
|
204
|
+
return allResults;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
// Run standalone
|
|
209
|
+
if (require.main === module) {
|
|
210
|
+
runFeatureTemplateBenchmarks().catch(console.error);
|
|
211
|
+
}
|