@bldrs-ai/conway 1.323.1029 → 1.325.1034
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 +22 -24
- package/compiled/examples/browser-bundled.cjs +1 -1
- package/compiled/examples/cli-bundled.cjs +1 -1
- package/compiled/examples/validator-bundled.cjs +1 -1
- package/compiled/src/ifc/ifc_regression_batch_main.js +100 -9
- package/compiled/src/ifc/ifc_regression_main.js +61 -0
- package/compiled/src/version/version.js +1 -1
- package/compiled/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -178,7 +178,7 @@ Every PR is gated on two checks defined in `.github/workflows/build.yml`:
|
|
|
178
178
|
| Job | What it does |
|
|
179
179
|
|---|---|
|
|
180
180
|
| `build` | `yarn install`, WASM + TS compile (WASM cached on the `conway-geom` submodule SHA), `yarn test`, `yarn lint`. |
|
|
181
|
-
| `run-ifc-regression` | `needs: build`. Reuses the same WASM cache. Runs the regression batch against the pinned `test-models` tag, posts a per-PR comment with `failed.csv` / `errors.csv` summaries, and uploads the candidate npm tarball as
|
|
181
|
+
| `run-ifc-regression` | `needs: build`. Reuses the same WASM cache. Runs the regression batch against the pinned `test-models` tag, posts a per-PR comment with `failed.csv` / `errors.csv` / perf summaries, and uploads the candidate npm tarball + `perf.csv` as workflow artifacts. |
|
|
182
182
|
|
|
183
183
|
A merge to `main` re-runs those two jobs and then chains into `auto-publish` (see [Releases](#releases) below).
|
|
184
184
|
|
|
@@ -186,9 +186,11 @@ A merge to `main` re-runs those two jobs and then chains into `auto-publish` (se
|
|
|
186
186
|
|
|
187
187
|
The same batch the regression CI job runs can be invoked locally — see [regression/README.md](regression/README.md) for digest / verbose / batch modes and the catalog of model fixtures. The CI pinned tag is `TEST_MODELS_TAG` near the top of `build.yml`.
|
|
188
188
|
|
|
189
|
-
## Performance benchmarks
|
|
189
|
+
## Performance benchmarks
|
|
190
190
|
|
|
191
|
-
|
|
191
|
+
**Tier 1 — Conway-only perf in CI (live).** Every regression run emits a `perf.csv` of `parseTimeMs / geometryTimeMs / totalTimeMs / rssMb / heapUsedMb / heapTotalMb` per model. The top-10 slowest are posted in the PR comment; the full CSV is uploaded as a workflow artifact. This piggybacks on the existing regression batch so cost is ~0 extra runner minutes.
|
|
192
|
+
|
|
193
|
+
**Tier 2 — full headless-three perf (planned, #314).** A `perf-three` job on `push: main` that runs `scripts/benchmark.cjs` against a clone of [headless-three](https://github.com/bldrs-ai/headless-three), installing the candidate Conway tarball as H3's `@bldrs-ai/conway` dep. Captures PNG renders + per-model timings via H3's rendering server, including a parallel job for `test-models-private`. Tracked in #314.
|
|
192
194
|
|
|
193
195
|
|
|
194
196
|
# Releases
|
|
@@ -278,27 +280,23 @@ follow-ups to deepen its coverage. Big items first.
|
|
|
278
280
|
|
|
279
281
|
### #314 — Performance benchmarks in CI
|
|
280
282
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
Once perf runs in CI, the comment posted on each PR should include a
|
|
300
|
-
delta vs. main so regressions show up alongside the regression-batch
|
|
301
|
-
results.
|
|
283
|
+
**Tier 1 — Conway-only perf (done):** the regression batch now emits a
|
|
284
|
+
per-model `perf.csv` (parse/geometry/total time + RSS/heap MB),
|
|
285
|
+
piggybacking on the existing run so there's no extra runner cost. Top-10
|
|
286
|
+
slowest models appear in each PR comment; the full CSV uploads as an
|
|
287
|
+
artifact. See "Performance benchmarks" above.
|
|
288
|
+
|
|
289
|
+
**Tier 2 — full headless-three perf (next):** an end-to-end render-and-time
|
|
290
|
+
job that runs `scripts/benchmark.cjs` against a clone of
|
|
291
|
+
[headless-three](https://github.com/bldrs-ai/headless-three). To avoid the
|
|
292
|
+
local `yarn link` toolchain, the job installs the candidate Conway tarball
|
|
293
|
+
(produced by `run-ifc-regression`) as H3's `@bldrs-ai/conway` dep and pins
|
|
294
|
+
`H3_TAG` in `build.yml`. Gated to `push: main` to keep PR runtime down
|
|
295
|
+
while still exercising every release. A sibling job runs against
|
|
296
|
+
`test-models-private` using `secrets.TEST_MODELS_PRIVATE_TOKEN`, never on
|
|
297
|
+
PR events (so forks can't leak the secret), and uploads results as
|
|
298
|
+
org-scoped artifacts. Delta vs. the previous release lands in the
|
|
299
|
+
auto-publish summary.
|
|
302
300
|
|
|
303
301
|
### #315 — Bump pinned `TEST_MODELS_TAG`
|
|
304
302
|
|
|
@@ -69608,7 +69608,7 @@ var IfcStepParser = class extends StepParser {
|
|
|
69608
69608
|
IfcStepParser.Instance = new IfcStepParser();
|
|
69609
69609
|
|
|
69610
69610
|
// compiled/src/version/version.js
|
|
69611
|
-
var versionString = "Conway Web-Ifc Shim v1.
|
|
69611
|
+
var versionString = "Conway Web-Ifc Shim v1.325.1034";
|
|
69612
69612
|
|
|
69613
69613
|
// compiled/src/statistics/statistics.js
|
|
69614
69614
|
var Statistics = class {
|
|
@@ -85869,7 +85869,7 @@ var IfcSceneBuilder = class {
|
|
|
85869
85869
|
};
|
|
85870
85870
|
|
|
85871
85871
|
// compiled/src/version/version.js
|
|
85872
|
-
var versionString = "Conway Web-Ifc Shim v1.
|
|
85872
|
+
var versionString = "Conway Web-Ifc Shim v1.325.1034";
|
|
85873
85873
|
|
|
85874
85874
|
// compiled/src/statistics/statistics.js
|
|
85875
85875
|
var Statistics = class {
|
|
@@ -69606,7 +69606,7 @@ var IfcStepParser = class extends StepParser {
|
|
|
69606
69606
|
IfcStepParser.Instance = new IfcStepParser();
|
|
69607
69607
|
|
|
69608
69608
|
// compiled/src/version/version.js
|
|
69609
|
-
var versionString = "Conway Web-Ifc Shim v1.
|
|
69609
|
+
var versionString = "Conway Web-Ifc Shim v1.325.1034";
|
|
69610
69610
|
|
|
69611
69611
|
// compiled/src/statistics/statistics.js
|
|
69612
69612
|
var Statistics = class {
|
|
@@ -126,6 +126,56 @@ async function runDiff(ifcFolder, outputFolder, target, diffOutputPath, isDryRun
|
|
|
126
126
|
await exec(`git checkout -- "${outputFolder}"`, { cwd: ifcFolder });
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Aggregate all per-file `*.perf.csv` rows in perfDir into a single CSV at
|
|
131
|
+
* outputCsvPath, sorted by file name. Files are written by individual child
|
|
132
|
+
* regression runs (one row + one header per file); we keep the header from
|
|
133
|
+
* the first file we read and concatenate the data rows from the rest. The
|
|
134
|
+
* input directory is removed afterwards.
|
|
135
|
+
*
|
|
136
|
+
* @param perfDir Directory the children wrote their per-file perf CSVs to.
|
|
137
|
+
* @param outputCsvPath Aggregate perf CSV destination.
|
|
138
|
+
*/
|
|
139
|
+
async function aggregatePerfCsvs(perfDir, outputCsvPath) {
|
|
140
|
+
const entries = await fsPromises.readdir(perfDir, { withFileTypes: true });
|
|
141
|
+
const perfFiles = entries
|
|
142
|
+
.filter((d) => d.isFile() && d.name.endsWith('.perf.csv'))
|
|
143
|
+
.map((d) => path.join(perfDir, d.name));
|
|
144
|
+
if (perfFiles.length === 0) {
|
|
145
|
+
console.warn(`No per-file perf CSVs found in ${perfDir}; skipping aggregate.`);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const rows = [];
|
|
149
|
+
let header;
|
|
150
|
+
for (const perfFile of perfFiles) {
|
|
151
|
+
const contents = await fsPromises.readFile(perfFile, 'utf8');
|
|
152
|
+
const lines = contents.split(/\r?\n/).filter((l) => l.length > 0);
|
|
153
|
+
if (lines.length < 2) {
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (header === undefined) {
|
|
157
|
+
header = lines[0];
|
|
158
|
+
}
|
|
159
|
+
// Each per-file CSV has exactly one data row (lines[1]); the first column
|
|
160
|
+
// is the file name which we use for stable sorting.
|
|
161
|
+
const dataLine = lines[1];
|
|
162
|
+
const firstComma = dataLine.indexOf(',');
|
|
163
|
+
const fileKey = firstComma >= 0 ? dataLine.slice(0, firstComma) : dataLine;
|
|
164
|
+
rows.push({ file: fileKey, line: dataLine });
|
|
165
|
+
}
|
|
166
|
+
rows.sort((a, b) => a.file.localeCompare(b.file));
|
|
167
|
+
// Defensive: if every child wrote a malformed CSV (lines.length < 2 for all),
|
|
168
|
+
// header stays undefined and rows stays empty. Bail rather than write the
|
|
169
|
+
// literal "undefined" as a CSV header.
|
|
170
|
+
if (rows.length === 0 || header === undefined) {
|
|
171
|
+
console.warn(`No usable perf rows in ${perfDir} (${perfFiles.length} file(s) checked); ` +
|
|
172
|
+
`skipping aggregate write.`);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const body = rows.map((r) => r.line).join('\n');
|
|
176
|
+
await fsPromises.writeFile(outputCsvPath, `${header}\n${body}\n`);
|
|
177
|
+
console.log(`Wrote aggregate perf CSV: ${outputCsvPath} (${rows.length} rows)`);
|
|
178
|
+
}
|
|
129
179
|
let totalTime = 0; // To keep track of the running total time
|
|
130
180
|
/**
|
|
131
181
|
* Run a regression test digest for a file.
|
|
@@ -133,11 +183,13 @@ let totalTime = 0; // To keep track of the running total time
|
|
|
133
183
|
* @param filePath
|
|
134
184
|
* @param outputPath
|
|
135
185
|
* @param maxTimeout
|
|
186
|
+
* @param perfPath Optional path the child should write a one-row perf CSV to.
|
|
136
187
|
*/
|
|
137
|
-
async function runForFile(filePath, outputPath, maxTimeout) {
|
|
188
|
+
async function runForFile(filePath, outputPath, maxTimeout, perfPath) {
|
|
138
189
|
const MAX_TIMEOUT_MS = maxTimeout;
|
|
139
190
|
const startTime = Date.now(); // Start time
|
|
140
|
-
const
|
|
191
|
+
const perfFlag = perfPath ? ` --perf "${perfPath}"` : '';
|
|
192
|
+
const safeExecCommand = `node --experimental-specifier-resolution=node ./compiled/src/ifc/ifc_regression_main.js -d${perfFlag} "${filePath}" "${outputPath}"`;
|
|
141
193
|
console.log(`Current File: ${filePath}`);
|
|
142
194
|
// Use safeExecWithCancellation, will kill the process if it takes longer than MAX_TIMEOUT_MS.
|
|
143
195
|
const process = await safeExecWithCancellation(safeExecCommand, MAX_TIMEOUT_MS);
|
|
@@ -235,8 +287,9 @@ function getSystemMemoryUsagePercent() {
|
|
|
235
287
|
* @param failedLines
|
|
236
288
|
* @param memUtilization
|
|
237
289
|
* @param maxTimeout
|
|
290
|
+
* @param perfDir If set, the child writes its perf CSV here as <basename>.perf.csv.
|
|
238
291
|
*/
|
|
239
|
-
async function processIFCFilesInParallel(ifcFiles, outputPath, errorLines, fileLines, failedLines, memUtilization, maxTimeout) {
|
|
292
|
+
async function processIFCFilesInParallel(ifcFiles, outputPath, errorLines, fileLines, failedLines, memUtilization, maxTimeout, perfDir) {
|
|
240
293
|
const concurrencyLimit = os.cpus().length;
|
|
241
294
|
console.log(`Concurrency: ${concurrencyLimit} threads - Max Timeout: ${maxTimeout} ms`);
|
|
242
295
|
const limit = pLimit(concurrencyLimit);
|
|
@@ -251,7 +304,10 @@ async function processIFCFilesInParallel(ifcFiles, outputPath, errorLines, fileL
|
|
|
251
304
|
}
|
|
252
305
|
activeTasks++;
|
|
253
306
|
console.log(`Starting task for "${path.basename(ifcPath)}". Active tasks: ${activeTasks}`);
|
|
254
|
-
const
|
|
307
|
+
const perfChildPath = perfDir ?
|
|
308
|
+
path.join(perfDir, `${path.basename(ifcPath, '.ifc')}.perf.csv`) :
|
|
309
|
+
undefined;
|
|
310
|
+
const fileResults = await runForFile(ifcPath, path.join(outputPath, path.basename(ifcPath, '.ifc')), maxTimeout, perfChildPath);
|
|
255
311
|
activeTasks--;
|
|
256
312
|
console.log(`Completed task for "${path.basename(ifcPath)}". Active tasks: ${activeTasks}`);
|
|
257
313
|
return { ifcPath, fileResults };
|
|
@@ -286,8 +342,9 @@ async function processIFCFilesInParallel(ifcFiles, outputPath, errorLines, fileL
|
|
|
286
342
|
* @param fileLines
|
|
287
343
|
* @param failedLines
|
|
288
344
|
* @param maxTimeout
|
|
345
|
+
* @param perfDir If set, the child writes its perf CSV here as <basename>.perf.csv.
|
|
289
346
|
*/
|
|
290
|
-
async function recursiveWalk(parentPath, excludeRegex, outputPath, errorLines, fileLines, failedLines, maxTimeout) {
|
|
347
|
+
async function recursiveWalk(parentPath, excludeRegex, outputPath, errorLines, fileLines, failedLines, maxTimeout, perfDir) {
|
|
291
348
|
const items = await fsPromises.readdir(parentPath, { withFileTypes: true });
|
|
292
349
|
items.sort((a, b) => (a.name > b.name ? 1 : -1));
|
|
293
350
|
for (const item of items) {
|
|
@@ -296,10 +353,13 @@ async function recursiveWalk(parentPath, excludeRegex, outputPath, errorLines, f
|
|
|
296
353
|
continue;
|
|
297
354
|
}
|
|
298
355
|
if (item.isDirectory()) {
|
|
299
|
-
await recursiveWalk(resolved, excludeRegex, outputPath, errorLines, fileLines, failedLines, maxTimeout);
|
|
356
|
+
await recursiveWalk(resolved, excludeRegex, outputPath, errorLines, fileLines, failedLines, maxTimeout, perfDir);
|
|
300
357
|
}
|
|
301
358
|
else if (path.extname(resolved).toLowerCase() === '.ifc') {
|
|
302
|
-
const
|
|
359
|
+
const perfChildPath = perfDir ?
|
|
360
|
+
path.join(perfDir, `${path.basename(resolved, '.ifc')}.perf.csv`) :
|
|
361
|
+
undefined;
|
|
362
|
+
const fileResults = await runForFile(resolved, path.join(outputPath, path.basename(resolved, '.ifc')), maxTimeout, perfChildPath);
|
|
303
363
|
if (fileResults.type === 'Run') {
|
|
304
364
|
if (fileResults.errorLines !== void 0) {
|
|
305
365
|
errorLines.push(...fileResults.errorLines);
|
|
@@ -368,6 +428,13 @@ const args = yargs(process.argv.slice(SKIP_PARAMS))
|
|
|
368
428
|
alias: 'timeout',
|
|
369
429
|
default: 150000,
|
|
370
430
|
});
|
|
431
|
+
yargs2.option('perf', {
|
|
432
|
+
describe: 'Output path for aggregate perf CSV. Each child writes a one-row ' +
|
|
433
|
+
'<basename>.perf.csv to a temp dir; on completion they are merged ' +
|
|
434
|
+
'into this file, sorted by file name. Disabled when unset.',
|
|
435
|
+
type: 'string',
|
|
436
|
+
default: '',
|
|
437
|
+
});
|
|
371
438
|
yargs2.positional('model_folder', {
|
|
372
439
|
describe: 'Folder containing IFC files, recursively walked',
|
|
373
440
|
type: 'string',
|
|
@@ -388,10 +455,20 @@ const args = yargs(process.argv.slice(SKIP_PARAMS))
|
|
|
388
455
|
const doParallel = argv['parallel'] ?? false; // <--- read the parallel flag
|
|
389
456
|
const memUtilization = argv['mem-utilization'];
|
|
390
457
|
const maxTimeout = argv['timeout'];
|
|
458
|
+
const perfOutputPath = (argv['perf'] ?? '').trim();
|
|
391
459
|
if (changes.length === 0) {
|
|
392
460
|
changes = path.join(outputPath, 'changes');
|
|
393
461
|
}
|
|
394
462
|
await fsPromises.mkdir(outputPath, { recursive: true });
|
|
463
|
+
// When perf is requested, children write their one-row CSVs into a
|
|
464
|
+
// throwaway temp dir; we aggregate and clean up afterwards. Keeping
|
|
465
|
+
// them out of outputPath avoids the runDiff step picking up
|
|
466
|
+
// machine-specific timings as "changes" against the test-models
|
|
467
|
+
// checked-in baselines.
|
|
468
|
+
let perfTmpDir;
|
|
469
|
+
if (perfOutputPath.length > 0) {
|
|
470
|
+
perfTmpDir = await fsPromises.mkdtemp(path.join(os.tmpdir(), 'conway-perf-'));
|
|
471
|
+
}
|
|
395
472
|
const mainPath = path.join(outputPath, 'main.csv');
|
|
396
473
|
const errorPath = path.join(outputPath, 'errors.csv');
|
|
397
474
|
const failedPath = path.join(outputPath, 'failed.csv');
|
|
@@ -404,16 +481,30 @@ const args = yargs(process.argv.slice(SKIP_PARAMS))
|
|
|
404
481
|
// 1) Collect all IFC files first
|
|
405
482
|
const allIFCFiles = await collectIFCFiles(ifcFolder, excludeRegex);
|
|
406
483
|
// 2) Process them in parallel
|
|
407
|
-
await processIFCFilesInParallel(allIFCFiles, outputPath, errorLines, fileLines, failedLines, memUtilization, maxTimeout);
|
|
484
|
+
await processIFCFilesInParallel(allIFCFiles, outputPath, errorLines, fileLines, failedLines, memUtilization, maxTimeout, perfTmpDir);
|
|
408
485
|
}
|
|
409
486
|
else {
|
|
410
487
|
console.log('Processing in serial mode...');
|
|
411
|
-
await recursiveWalk(ifcFolder, excludeRegex, outputPath, errorLines, fileLines, failedLines, maxTimeout);
|
|
488
|
+
await recursiveWalk(ifcFolder, excludeRegex, outputPath, errorLines, fileLines, failedLines, maxTimeout, perfTmpDir);
|
|
412
489
|
}
|
|
413
490
|
// Write out results
|
|
414
491
|
await fsPromises.writeFile(mainPath, `file,hash,errors\n${fileLines.join('')}`);
|
|
415
492
|
await fsPromises.writeFile(errorPath, `${errorCSVHeader}\n${errorLines.join('')}`);
|
|
416
493
|
await fsPromises.writeFile(failedPath, `file,code,signal\n${failedLines.join('')}`);
|
|
494
|
+
// Aggregate per-child perf rows (if requested) before runDiff so the
|
|
495
|
+
// run completes deterministically even when the git diff step is
|
|
496
|
+
// skipped or fails.
|
|
497
|
+
if (perfTmpDir) {
|
|
498
|
+
try {
|
|
499
|
+
await aggregatePerfCsvs(perfTmpDir, perfOutputPath);
|
|
500
|
+
}
|
|
501
|
+
catch (e) {
|
|
502
|
+
console.error('Failed to aggregate perf CSVs:', e);
|
|
503
|
+
}
|
|
504
|
+
finally {
|
|
505
|
+
await fsPromises.rm(perfTmpDir, { recursive: true, force: true });
|
|
506
|
+
}
|
|
507
|
+
}
|
|
417
508
|
// If user wants a git diff
|
|
418
509
|
await runDiff(ifcFolder, outputPath, target, changes, dryRun);
|
|
419
510
|
// ---- New: log total runtime
|
|
@@ -36,6 +36,50 @@ function csvSafeString(from) {
|
|
|
36
36
|
}
|
|
37
37
|
return from;
|
|
38
38
|
}
|
|
39
|
+
// Bytes per megabyte for memory-stat formatting in the perf CSV.
|
|
40
|
+
// eslint-disable-next-line no-magic-numbers
|
|
41
|
+
const BYTES_PER_MB = 1024 * 1024;
|
|
42
|
+
// Fixed-point precision for perf MB values.
|
|
43
|
+
// eslint-disable-next-line no-magic-numbers
|
|
44
|
+
const PERF_MB_PRECISION = 2;
|
|
45
|
+
/**
|
|
46
|
+
* Write a single-row per-file perf CSV at the given path. Memory snapshot is
|
|
47
|
+
* taken at call time; for the OK path this is right after geometry extraction
|
|
48
|
+
* — close to peak. No-op when perfPath is empty.
|
|
49
|
+
*
|
|
50
|
+
* Memory semantics: `rssMb` includes the conway-geom WASM heap (mmap'd into
|
|
51
|
+
* the process) and is the load-bearing memory metric for geometry work.
|
|
52
|
+
* `heapUsedMb` / `heapTotalMb` are V8-only — useful for tracking JS-side
|
|
53
|
+
* allocation pressure but they exclude WASM-side buffers. Expect a large
|
|
54
|
+
* gap between rss and heap on geometry-heavy models.
|
|
55
|
+
*
|
|
56
|
+
* @param perfPath Path to write the CSV to. Empty string disables.
|
|
57
|
+
* @param ifcFile Source IFC file path (basename used as the row key).
|
|
58
|
+
* @param status OK or FAIL.
|
|
59
|
+
* @param parseTimeMs Parse stage duration in ms.
|
|
60
|
+
* @param geometryTimeMs Geometry extraction duration in ms.
|
|
61
|
+
* @param totalTimeMs Sum of parse + geometry in ms.
|
|
62
|
+
*/
|
|
63
|
+
async function writePerfCsvIfRequested(perfPath, ifcFile, status, parseTimeMs, geometryTimeMs, totalTimeMs) {
|
|
64
|
+
if (perfPath.length === 0) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const mem = process.memoryUsage();
|
|
68
|
+
const rssMb = (mem.rss / BYTES_PER_MB).toFixed(PERF_MB_PRECISION);
|
|
69
|
+
const heapUsedMb = (mem.heapUsed / BYTES_PER_MB).toFixed(PERF_MB_PRECISION);
|
|
70
|
+
const heapTotalMb = (mem.heapTotal / BYTES_PER_MB).toFixed(PERF_MB_PRECISION);
|
|
71
|
+
const fileName = csvSafeString(path.basename(ifcFile));
|
|
72
|
+
const header = "file,status,parseTimeMs,geometryTimeMs,totalTimeMs,rssMb,heapUsedMb,heapTotalMb\n";
|
|
73
|
+
const row = `${fileName},${status},${parseTimeMs},${geometryTimeMs},${totalTimeMs},` +
|
|
74
|
+
`${rssMb},${heapUsedMb},${heapTotalMb}\n`;
|
|
75
|
+
try {
|
|
76
|
+
await fsPromises.writeFile(perfPath, header + row);
|
|
77
|
+
}
|
|
78
|
+
catch (e) {
|
|
79
|
+
// Perf is best-effort; never fail the regression run because of a perf write.
|
|
80
|
+
console.error(`Failed to write perf CSV at ${perfPath}:`, e);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
39
83
|
/**
|
|
40
84
|
* Display errors and dump errors errors to stderr
|
|
41
85
|
*
|
|
@@ -94,6 +138,12 @@ function doWork() {
|
|
|
94
138
|
alias: "v",
|
|
95
139
|
default: false,
|
|
96
140
|
});
|
|
141
|
+
yargs2.option("perf", {
|
|
142
|
+
describe: "Write a single-row perf CSV (parse/geometry/total time + memory) at this path",
|
|
143
|
+
type: "string",
|
|
144
|
+
alias: "p",
|
|
145
|
+
default: "",
|
|
146
|
+
});
|
|
97
147
|
yargs2.positional("filename", { describe: "IFC File Paths", type: "string" });
|
|
98
148
|
yargs2.positional("output", { describe: "Output path", type: "string" });
|
|
99
149
|
}, async (argv) => {
|
|
@@ -104,6 +154,7 @@ function doWork() {
|
|
|
104
154
|
const strict = argv["strict"] ?? false;
|
|
105
155
|
const digest = argv["digest"] ?? false;
|
|
106
156
|
const verbose = argv["verbose"] ?? false;
|
|
157
|
+
const perfPath = argv["perf"] ?? "";
|
|
107
158
|
try {
|
|
108
159
|
indexIfcBuffer = fs.readFileSync(ifcFile);
|
|
109
160
|
}
|
|
@@ -119,6 +170,7 @@ function doWork() {
|
|
|
119
170
|
}
|
|
120
171
|
const parser = IfcStepParser.Instance;
|
|
121
172
|
const bufferInput = new ParsingBuffer(indexIfcBuffer);
|
|
173
|
+
const parseStartMs = Date.now();
|
|
122
174
|
const result0 = parser.parseHeader(bufferInput)[1];
|
|
123
175
|
switch (result0) {
|
|
124
176
|
case ParseResult.COMPLETE:
|
|
@@ -155,11 +207,20 @@ function doWork() {
|
|
|
155
207
|
break;
|
|
156
208
|
default:
|
|
157
209
|
}
|
|
210
|
+
const parseEndMs = Date.now();
|
|
211
|
+
const parseTimeMs = parseEndMs - parseStartMs;
|
|
158
212
|
if (model === void 0) {
|
|
213
|
+
await writePerfCsvIfRequested(perfPath, ifcFile, "FAIL", parseTimeMs, 0, parseTimeMs);
|
|
159
214
|
return;
|
|
160
215
|
}
|
|
161
216
|
model.nullOnErrors = !strict;
|
|
217
|
+
const geomStartMs = Date.now();
|
|
162
218
|
const result = await geometryExtraction(model);
|
|
219
|
+
const geomEndMs = Date.now();
|
|
220
|
+
const geometryTimeMs = geomEndMs - geomStartMs;
|
|
221
|
+
const totalTimeMs = geomEndMs - parseStartMs;
|
|
222
|
+
const perfStatus = result === void 0 ? "FAIL" : "OK";
|
|
223
|
+
await writePerfCsvIfRequested(perfPath, ifcFile, perfStatus, parseTimeMs, geometryTimeMs, totalTimeMs);
|
|
163
224
|
if (result === void 0) {
|
|
164
225
|
Logger.error("Couldn't extract geometry");
|
|
165
226
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const versionString = 'Conway Web-Ifc Shim v1.
|
|
1
|
+
const versionString = 'Conway Web-Ifc Shim v1.325.1034';
|
|
2
2
|
export { versionString };
|