Skip to content

Commit 13eb112

Browse files
authored
perf(schema-compiler): Improve yaml compilation speed (#9926)
* add convertedToJs flag * pass toCompile to compileCubeFiles() * refactored compileYamlFile() to not call compileJsFile directly * moved yaml transpilation to transpile phase * removed dataSchemaCompiler from YamlCompiler's deps * renaming * simplified code * add PerfTracker * inject perTracker measures in dataschemacompiler * update CubePropContextTranspiler to support string keys * replace original content with js on first phase * update arrow func expression instead of blind copying + correct path collection * fix grammar * more types * Preload Jinja templates to the engine * remove perf logging * simplified compileFile * avoid mutating original repository files * linter fix * filter out files we don't transpile/compile * move PerfTracker to backend-shared * Refactor doCompile() to avoid content mutation * refactor: split first and later compile/transpile stages * improve transpilationNative
1 parent b78ae0a commit 13eb112

File tree

6 files changed

+335
-139
lines changed

6 files changed

+335
-139
lines changed
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { performance, PerformanceObserver } from 'perf_hooks';
2+
3+
interface PerfMetric {
4+
count: number;
5+
totalTime: number;
6+
avgTime: number;
7+
}
8+
9+
interface PerfStats {
10+
[key: string]: PerfMetric;
11+
}
12+
13+
class PerfTracker {
14+
private metrics: PerfStats = {};
15+
16+
private globalMetric: string | null = null;
17+
18+
public constructor() {
19+
const obs = new PerformanceObserver((items) => {
20+
for (const entry of items.getEntries()) {
21+
const { name } = entry;
22+
if (!this.metrics[name]) {
23+
this.metrics[name] = { count: 0, totalTime: 0, avgTime: 0 };
24+
}
25+
const m = this.metrics[name];
26+
m.count++;
27+
m.totalTime += entry.duration;
28+
m.avgTime = m.totalTime / m.count;
29+
}
30+
});
31+
obs.observe({ entryTypes: ['measure'] });
32+
}
33+
34+
public start(name: string, global: boolean = false): { end: () => void } {
35+
const uid = `${name}-${performance.now()}`;
36+
const startMark = `${uid}-start`;
37+
const endMark = `${uid}-end`;
38+
performance.mark(startMark);
39+
40+
if (global && !this.globalMetric) {
41+
this.globalMetric = name;
42+
}
43+
44+
let ended = false;
45+
46+
return {
47+
end: () => {
48+
if (ended) return;
49+
performance.mark(endMark);
50+
performance.measure(name, startMark, endMark);
51+
ended = true;
52+
}
53+
};
54+
}
55+
56+
public printReport() {
57+
console.log('\n🚀 PERFORMANCE REPORT 🚀\n');
58+
console.log('═'.repeat(90));
59+
60+
const sorted = Object.entries(this.metrics)
61+
.sort(([, a], [, b]) => b.totalTime - a.totalTime);
62+
63+
if (!sorted.length) {
64+
console.log('No performance data collected.');
65+
return;
66+
}
67+
68+
let totalTime: number = 0;
69+
70+
if (this.globalMetric) {
71+
totalTime = this.metrics[this.globalMetric]?.totalTime;
72+
} else {
73+
totalTime = sorted.reduce((sum, [, m]) => sum + m.totalTime, 0);
74+
}
75+
76+
console.log(`⏱️ TOTAL TIME: ${totalTime.toFixed(2)}ms\n`);
77+
78+
sorted.forEach(([name, m]) => {
79+
const pct = totalTime > 0 ? (m.totalTime / totalTime * 100) : 0;
80+
console.log(` ${name.padEnd(40)}${m.totalTime.toFixed(2).padStart(8)}ms │ ${m.avgTime.toFixed(2).padStart(7)}ms avg │ ${pct.toFixed(1).padStart(5)}% │ ${m.count.toString().padStart(4)} calls`);
81+
});
82+
83+
console.log('═'.repeat(90));
84+
console.log('🎯 End of Performance Report\n');
85+
}
86+
}
87+
88+
export const perfTracker = new PerfTracker();

packages/cubejs-backend-shared/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,4 @@ export * from './process';
2121
export * from './platform';
2222
export * from './FileRepository';
2323
export * from './decorators';
24+
export * from './PerfTracker';

0 commit comments

Comments
 (0)