@@ -22,6 +22,7 @@ import { CompilerInterface } from './PrepareCompiler';
2222import { YamlCompiler } from './YamlCompiler' ;
2323import { CubeDictionary } from './CubeDictionary' ;
2424import { CompilerCache } from './CompilerCache' ;
25+ import { perfTracker } from './PerfTracker' ;
2526
2627const NATIVE_IS_SUPPORTED = isNativeSupported ( ) ;
2728
@@ -206,127 +207,157 @@ export class DataSchemaCompiler {
206207 }
207208
208209 protected async doCompile ( ) {
209- const files = await this . repository . dataSchemaFiles ( ) ;
210+ // Start overall compilation timing
211+ const compileTimer = perfTracker . start ( 'doCompile' , true ) ;
210212
211- this . pythonContext = await this . loadPythonContext ( files , 'globals.py' ) ;
212- this . yamlCompiler . initFromPythonContext ( this . pythonContext ) ;
213+ try {
214+ const files = await this . repository . dataSchemaFiles ( ) ;
213215
214- const toCompile = files . filter ( ( f ) => ! this . filesToCompile || ! this . filesToCompile . length || this . filesToCompile . indexOf ( f . fileName ) !== - 1 ) ;
216+ console . log ( `Compiling ${ files . length } files...` ) ;
215217
216- const errorsReport = new ErrorReporter ( null , [ ] , this . errorReportOptions ) ;
217- this . errorsReporter = errorsReport ;
218+ this . pythonContext = await this . loadPythonContext ( files , 'globals.py' ) ;
219+ this . yamlCompiler . initFromPythonContext ( this . pythonContext ) ;
218220
219- const transpilationWorkerThreads = getEnv ( 'transpilationWorkerThreads' ) ;
220- const transpilationNative = getEnv ( 'transpilationNative' ) ;
221- const transpilationNativeThreadsCount = getThreadsCount ( ) ;
222- const { compilerId } = this ;
221+ const toCompile = files . filter ( ( f ) => ! this . filesToCompile || ! this . filesToCompile . length || this . filesToCompile . indexOf ( f . fileName ) !== - 1 ) ;
223222
224- if ( ! transpilationNative && transpilationWorkerThreads ) {
225- const wc = getEnv ( 'transpilationWorkerThreadsCount' ) ;
226- this . workerPool = workerpool . pool (
227- path . join ( __dirname , 'transpilers/transpiler_worker' ) ,
228- wc > 0 ? { maxWorkers : wc } : undefined ,
229- ) ;
230- }
223+ const errorsReport = new ErrorReporter ( null , [ ] , this . errorReportOptions ) ;
224+ this . errorsReporter = errorsReport ;
231225
232- const transpile = async ( stage : CompileStage ) => {
233- let cubeNames : string [ ] = [ ] ;
234- let cubeSymbols : Record < string , Record < string , boolean > > = { } ;
235- let transpilerNames : string [ ] = [ ] ;
236- let results ;
237-
238- if ( transpilationNative || transpilationWorkerThreads ) {
239- cubeNames = Object . keys ( this . cubeDictionary . byId ) ;
240- // We need only cubes and all its member names for transpiling.
241- // Cubes doesn't change during transpiling, but are changed during compilation phase,
242- // so we can prepare them once for every phase.
243- // Communication between main and worker threads uses
244- // The structured clone algorithm (@see https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm)
245- // which doesn't allow passing any function objects, so we need to sanitize the symbols.
246- // Communication with native backend also involves deserialization.
247- cubeSymbols = Object . fromEntries (
248- Object . entries ( this . cubeSymbols . symbols as Record < string , Record < string , any > > )
249- . map (
250- ( [ key , value ] : [ string , Record < string , any > ] ) => [ key , Object . fromEntries (
251- Object . keys ( value ) . map ( ( k ) => [ k , true ] ) ,
252- ) ] ,
253- ) ,
254- ) ;
226+ const transpilationWorkerThreads = getEnv ( 'transpilationWorkerThreads' ) ;
227+ const transpilationNative = getEnv ( 'transpilationNative' ) ;
228+ const transpilationNativeThreadsCount = getThreadsCount ( ) ;
229+ const { compilerId } = this ;
255230
256- // Transpilers are the same for all files within phase.
257- transpilerNames = this . transpilers . map ( t => t . constructor . name ) ;
231+ if ( ! transpilationNative && transpilationWorkerThreads ) {
232+ const wc = getEnv ( 'transpilationWorkerThreadsCount' ) ;
233+ this . workerPool = workerpool . pool (
234+ path . join ( __dirname , 'transpilers/transpiler_worker' ) ,
235+ wc > 0 ? { maxWorkers : wc } : undefined ,
236+ ) ;
258237 }
259238
260- if ( transpilationNative ) {
239+ const transpile = async ( stage : CompileStage ) => {
240+ // Time transpilation stage
241+ const transpileTimer = perfTracker . start ( `transpilation-stage-${ stage } ` ) ;
242+
243+ let cubeNames : string [ ] = [ ] ;
244+ let cubeSymbols : Record < string , Record < string , boolean > > = { } ;
245+ let transpilerNames : string [ ] = [ ] ;
246+ let results ;
247+
248+ if ( transpilationNative || transpilationWorkerThreads ) {
249+ cubeNames = Object . keys ( this . cubeDictionary . byId ) ;
250+ // We need only cubes and all its member names for transpiling.
251+ // Cubes doesn't change during transpiling, but are changed during compilation phase,
252+ // so we can prepare them once for every phase.
253+ // Communication between main and worker threads uses
254+ // The structured clone algorithm (@see https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm)
255+ // which doesn't allow passing any function objects, so we need to sanitize the symbols.
256+ // Communication with native backend also involves deserialization.
257+ cubeSymbols = Object . fromEntries (
258+ Object . entries ( this . cubeSymbols . symbols as Record < string , Record < string , any > > )
259+ . map (
260+ ( [ key , value ] : [ string , Record < string , any > ] ) => [ key , Object . fromEntries (
261+ Object . keys ( value ) . map ( ( k ) => [ k , true ] ) ,
262+ ) ] ,
263+ ) ,
264+ ) ;
265+
266+ // Transpilers are the same for all files within phase.
267+ transpilerNames = this . transpilers . map ( t => t . constructor . name ) ;
268+ }
269+
270+ if ( transpilationNative ) {
261271 // Warming up swc compiler cache
262- const dummyFile = {
263- fileName : 'dummy.js' ,
264- content : ';' ,
265- } ;
272+ const dummyFile = {
273+ fileName : 'dummy.js' ,
274+ content : ';' ,
275+ } ;
266276
267- await this . transpileJsFile ( dummyFile , errorsReport , { cubeNames, cubeSymbols, transpilerNames, contextSymbols : CONTEXT_SYMBOLS , compilerId, stage } ) ;
277+ await this . transpileJsFile ( dummyFile , errorsReport , { cubeNames, cubeSymbols, transpilerNames, contextSymbols : CONTEXT_SYMBOLS , compilerId, stage } ) ;
268278
269- const nonJsFilesTasks = toCompile . filter ( file => ! file . fileName . endsWith ( '.js' ) )
270- . map ( f => this . transpileFile ( f , errorsReport , { transpilerNames, compilerId } ) ) ;
279+ const nonJsFilesTasks = toCompile . filter ( file => ! file . fileName . endsWith ( '.js' ) )
280+ . map ( f => this . transpileFile ( f , errorsReport , { transpilerNames, compilerId } ) ) ;
271281
272- const jsFiles = toCompile . filter ( file => file . fileName . endsWith ( '.js' ) ) ;
273- let JsFilesTasks = [ ] ;
282+ const jsFiles = toCompile . filter ( file => file . fileName . endsWith ( '.js' ) ) ;
283+ let JsFilesTasks = [ ] ;
274284
275- if ( jsFiles . length > 0 ) {
276- let jsChunks ;
277- if ( jsFiles . length < transpilationNativeThreadsCount * transpilationNativeThreadsCount ) {
278- jsChunks = [ jsFiles ] ;
279- } else {
280- const baseSize = Math . floor ( jsFiles . length / transpilationNativeThreadsCount ) ;
281- jsChunks = [ ] ;
282- for ( let i = 0 ; i < transpilationNativeThreadsCount ; i ++ ) {
285+ if ( jsFiles . length > 0 ) {
286+ let jsChunks ;
287+ if ( jsFiles . length < transpilationNativeThreadsCount * transpilationNativeThreadsCount ) {
288+ jsChunks = [ jsFiles ] ;
289+ } else {
290+ const baseSize = Math . floor ( jsFiles . length / transpilationNativeThreadsCount ) ;
291+ jsChunks = [ ] ;
292+ for ( let i = 0 ; i < transpilationNativeThreadsCount ; i ++ ) {
283293 // For the last part, we take the remaining files so we don't lose the extra ones.
284- const start = i * baseSize ;
285- const end = ( i === transpilationNativeThreadsCount - 1 ) ? jsFiles . length : start + baseSize ;
286- jsChunks . push ( jsFiles . slice ( start , end ) ) ;
294+ const start = i * baseSize ;
295+ const end = ( i === transpilationNativeThreadsCount - 1 ) ? jsFiles . length : start + baseSize ;
296+ jsChunks . push ( jsFiles . slice ( start , end ) ) ;
297+ }
287298 }
299+ JsFilesTasks = jsChunks . map ( chunk => this . transpileJsFilesBulk ( chunk , errorsReport , { transpilerNames, compilerId } ) ) ;
288300 }
289- JsFilesTasks = jsChunks . map ( chunk => this . transpileJsFilesBulk ( chunk , errorsReport , { transpilerNames, compilerId } ) ) ;
301+
302+ results = ( await Promise . all ( [ ...nonJsFilesTasks , ...JsFilesTasks ] ) ) . flat ( ) ;
303+ } else if ( transpilationWorkerThreads ) {
304+ results = await Promise . all ( toCompile . map ( f => this . transpileFile ( f , errorsReport , { cubeNames, cubeSymbols, transpilerNames } ) ) ) ;
305+ } else {
306+ results = await Promise . all ( toCompile . map ( f => this . transpileFile ( f , errorsReport , { } ) ) ) ;
290307 }
291308
292- results = ( await Promise . all ( [ ...nonJsFilesTasks , ...JsFilesTasks ] ) ) . flat ( ) ;
293- } else if ( transpilationWorkerThreads ) {
294- results = await Promise . all ( toCompile . map ( f => this . transpileFile ( f , errorsReport , { cubeNames, cubeSymbols, transpilerNames } ) ) ) ;
295- } else {
296- results = await Promise . all ( toCompile . map ( f => this . transpileFile ( f , errorsReport , { } ) ) ) ;
297- }
309+ // End transpilation stage timing
310+ transpileTimer . end ( ) ;
298311
299- return results . filter ( f => ! ! f ) ;
300- } ;
301-
302- const compilePhase = async ( compilers : CompileCubeFilesCompilers , stage : 0 | 1 | 2 | 3 ) => this . compileCubeFiles ( compilers , await transpile ( stage ) , errorsReport ) ;
303-
304- return compilePhase ( { cubeCompilers : this . cubeNameCompilers } , 0 )
305- . then ( ( ) => compilePhase ( { cubeCompilers : this . preTranspileCubeCompilers . concat ( [ this . viewCompilationGate ] ) } , 1 ) )
306- . then ( ( ) => ( this . viewCompilationGate . shouldCompileViews ( ) ?
307- compilePhase ( { cubeCompilers : this . viewCompilers } , 2 )
308- : Promise . resolve ( ) ) )
309- . then ( ( ) => compilePhase ( {
310- cubeCompilers : this . cubeCompilers ,
311- contextCompilers : this . contextCompilers ,
312- } , 3 ) )
313- . then ( ( ) => {
314- if ( transpilationNative ) {
315- // Clean up cache
316- const dummyFile = {
317- fileName : 'terminate.js' ,
318- content : ';' ,
319- } ;
312+ return results . filter ( f => ! ! f ) ;
313+ } ;
320314
321- this . transpileJsFile (
322- dummyFile ,
323- errorsReport ,
324- { cubeNames : [ ] , cubeSymbols : { } , transpilerNames : [ ] , contextSymbols : { } , compilerId : this . compilerId , stage : 0 }
325- ) . then ( ( ) => undefined ) ;
326- } else if ( transpilationWorkerThreads && this . workerPool ) {
327- this . workerPool . terminate ( ) ;
315+ const compilePhase = async ( compilers : CompileCubeFilesCompilers , stage : 0 | 1 | 2 | 3 ) => {
316+ const compilePhaseTimer = perfTracker . start ( `compilation-phase-${ stage } ` ) ;
317+ try {
318+ return await this . compileCubeFiles ( compilers , await transpile ( stage ) , errorsReport ) ;
319+ } finally {
320+ compilePhaseTimer . end ( ) ;
328321 }
329- } ) ;
322+ } ;
323+
324+ return compilePhase ( { cubeCompilers : this . cubeNameCompilers } , 0 )
325+ . then ( ( ) => compilePhase ( { cubeCompilers : this . preTranspileCubeCompilers . concat ( [ this . viewCompilationGate ] ) } , 1 ) )
326+ . then ( ( ) => ( this . viewCompilationGate . shouldCompileViews ( ) ?
327+ compilePhase ( { cubeCompilers : this . viewCompilers } , 2 )
328+ : Promise . resolve ( ) ) )
329+ . then ( ( ) => compilePhase ( {
330+ cubeCompilers : this . cubeCompilers ,
331+ contextCompilers : this . contextCompilers ,
332+ } , 3 ) )
333+ . then ( ( ) => {
334+ if ( transpilationNative ) {
335+ // Clean up cache
336+ const dummyFile = {
337+ fileName : 'terminate.js' ,
338+ content : ';' ,
339+ } ;
340+
341+ this . transpileJsFile (
342+ dummyFile ,
343+ errorsReport ,
344+ { cubeNames : [ ] , cubeSymbols : { } , transpilerNames : [ ] , contextSymbols : { } , compilerId : this . compilerId , stage : 0 }
345+ ) . then ( ( ) => undefined ) ;
346+ } else if ( transpilationWorkerThreads && this . workerPool ) {
347+ this . workerPool . terminate ( ) ;
348+ }
349+
350+ // End overall compilation timing and print performance report
351+ compileTimer . end ( ) ;
352+ setImmediate ( ( ) => {
353+ perfTracker . printReport ( ) ;
354+ } ) ;
355+ } ) ;
356+ } catch ( error ) {
357+ // End timing even on error
358+ compileTimer . end ( ) ;
359+ throw error ;
360+ }
330361 }
331362
332363 public compile ( ) {
@@ -513,7 +544,9 @@ export class DataSchemaCompiler {
513544 asyncModules
514545 ) ;
515546 } ) ;
547+ const asyncModulesTimer = perfTracker . start ( 'asyncModules.reduce (jinja)' ) ;
516548 await asyncModules . reduce ( ( a : Promise < void > , b : CallableFunction ) => a . then ( ( ) => b ( ) ) , Promise . resolve ( ) ) ;
549+ asyncModulesTimer . end ( ) ;
517550 return this . compileObjects ( compilers . cubeCompilers || [ ] , cubes , errorsReport )
518551 . then ( ( ) => this . compileObjects ( compilers . contextCompilers || [ ] , contexts , errorsReport ) ) ;
519552 }
@@ -532,7 +565,9 @@ export class DataSchemaCompiler {
532565 compiledFiles [ file . fileName ] = true ;
533566
534567 if ( file . fileName . endsWith ( '.js' ) ) {
568+ const compileJsFileTimer = perfTracker . start ( 'compileJsFile' ) ;
535569 this . compileJsFile ( file , errorsReport , cubes , contexts , exports , asyncModules , toCompile , compiledFiles , { doSyntaxCheck } ) ;
570+ compileJsFileTimer . end ( ) ;
536571 } else if ( file . fileName . endsWith ( '.yml.jinja' ) || file . fileName . endsWith ( '.yaml.jinja' ) ||
537572 (
538573 file . fileName . endsWith ( '.yml' ) || file . fileName . endsWith ( '.yaml' )
@@ -552,7 +587,9 @@ export class DataSchemaCompiler {
552587 this . pythonContext !
553588 ) ) ;
554589 } else if ( file . fileName . endsWith ( '.yml' ) || file . fileName . endsWith ( '.yaml' ) ) {
590+ const compileYamlFileTimer = perfTracker . start ( 'compileYamlFile' ) ;
555591 this . yamlCompiler . compileYamlFile ( file , errorsReport , cubes , contexts , exports , asyncModules , toCompile , compiledFiles ) ;
592+ compileYamlFileTimer . end ( ) ;
556593 }
557594 }
558595
0 commit comments