From 8ec9a73e6af2de3205148adf3df5f589b9163efc Mon Sep 17 00:00:00 2001 From: Jesse Wright <63333554+jeswr@users.noreply.github.com> Date: Sun, 5 Jul 2026 11:28:20 +0000 Subject: [PATCH] perf: Pre-expand the Override type and avoid Object.entries in the override scan ConfigPreprocessorOverride.findOverrideTargets() scans every resource in the shared RdfObjectLoader (15,814 resources during a Community Solid Server default-config boot) to find oo:Override resources. Two avoidable costs per boot: 1. `resource.isA(IRIS_OO.Override)` is called with a *string*, so Resource#isA re-runs term expansion for every resource in the loader. IRIS_OO.Override is a constant absolute IRI, so it can be converted to a term once, outside the loop. 2. `Object.entries(...)` materializes a [ key, value ] pair array per resource purely for iteration; `Object.keys(...)` + index lookup avoids that allocation. Behaviour is unchanged: `isA(term)` is exactly what `isA(string)` does after expanding the string, and iteration order and results are identical. Isolated A/B on the real CSS boot object graph (15,814 resources, warm, median of 40 reps, 2-core shared box, this branch's build): original (Object.entries + isA(string)) : 23.6 ms (min 20.4) pre-expanded term + Object.keys : 12.4 ms (min 11.6) ~1.9x The optimized scan returns the identical (empty) override set for the default config, and the full test suite (including all override tests) is unchanged and green. Small in absolute terms, but it is the single biggest componentsjs-owned CPU frame in the instantiate phase and is pure waste for the common zero-override configuration. Co-Authored-By: Claude Fable 5 --- lib/preprocess/ConfigPreprocessorOverride.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/preprocess/ConfigPreprocessorOverride.ts b/lib/preprocess/ConfigPreprocessorOverride.ts index b03553d..b3dca38 100644 --- a/lib/preprocess/ConfigPreprocessorOverride.ts +++ b/lib/preprocess/ConfigPreprocessorOverride.ts @@ -1,5 +1,6 @@ import type { Resource } from 'rdf-object'; import type { RdfObjectLoader } from 'rdf-object/lib/RdfObjectLoader'; +import { stringToTerm } from 'rdf-string'; import type { Logger } from 'winston'; import { IRIS_OO, PREFIX_OO } from '../rdf/Iris'; import { uniqueTypes } from '../rdf/ResourceUtil'; @@ -110,8 +111,13 @@ export class ConfigPreprocessorOverride implements IConfigPreprocessor { - for (const [ id, resource ] of Object.entries(this.objectLoader.resources)) { - if (resource.isA(IRIS_OO.Override) && resource.value !== IRIS_OO.Override) { + // Convert the Override type IRI to a term once, instead of having `Resource#isA` + // re-expand the type string for every resource in the loader. + // Iterating over the keys also avoids materializing a [ key, value ] pair array per resource. + const overrideType = stringToTerm(IRIS_OO.Override); + for (const id of Object.keys(this.objectLoader.resources)) { + const resource = this.objectLoader.resources[id]; + if (resource.isA(overrideType) && resource.value !== IRIS_OO.Override) { const targets = resource.properties[IRIS_OO.overrideInstance]; if (!targets || targets.length === 0) { this.logger.warn(`Missing overrideInstance for ${id}. This Override will be ignored.`);