Browse Source

Add performance labels

gh-953
Bogdan Chadkin 8 years ago
parent
commit
8197977050
  1. 27
      src/Bundle.js
  2. 11
      src/Module.js
  3. 11
      src/rollup.js
  4. 35
      src/utils/flushTime.js

27
src/Bundle.js

@ -1,3 +1,4 @@
import { timeStart, timeEnd } from './utils/flushTime.js';
import { decode } from 'sourcemap-codec'; import { decode } from 'sourcemap-codec';
import { Bundle as MagicStringBundle } from 'magic-string'; import { Bundle as MagicStringBundle } from 'magic-string';
import first from './utils/first.js'; import first from './utils/first.js';
@ -102,12 +103,19 @@ export default class Bundle {
// Phase 2 – binding. We link references to their declarations // Phase 2 – binding. We link references to their declarations
// to generate a complete picture of the bundle // to generate a complete picture of the bundle
timeStart( 'phase 2' );
this.modules.forEach( module => module.bindImportSpecifiers() ); this.modules.forEach( module => module.bindImportSpecifiers() );
this.modules.forEach( module => module.bindReferences() ); this.modules.forEach( module => module.bindReferences() );
timeEnd( 'phase 2' );
// Phase 3 – marking. We 'run' each statement to see which ones // Phase 3 – marking. We 'run' each statement to see which ones
// need to be included in the generated bundle // need to be included in the generated bundle
timeStart( 'phase 3' );
// mark all export statements // mark all export statements
entryModule.getExports().forEach( name => { entryModule.getExports().forEach( name => {
const declaration = entryModule.traceExport( name ); const declaration = entryModule.traceExport( name );
@ -146,11 +154,18 @@ export default class Bundle {
} }
} }
timeEnd( 'phase 3' );
// Phase 4 – final preparation. We order the modules with an // Phase 4 – final preparation. We order the modules with an
// enhanced topological sort that accounts for cycles, then // enhanced topological sort that accounts for cycles, then
// ensure that names are deconflicted throughout the bundle // ensure that names are deconflicted throughout the bundle
timeStart( 'phase 4' );
this.orderedModules = this.sort(); this.orderedModules = this.sort();
this.deconflict(); this.deconflict();
timeEnd( 'phase 4' );
}); });
} }
@ -332,6 +347,8 @@ export default class Bundle {
let magicString = new MagicStringBundle({ separator: '\n\n' }); let magicString = new MagicStringBundle({ separator: '\n\n' });
const usedModules = []; const usedModules = [];
timeStart( 'render modules' );
this.orderedModules.forEach( module => { this.orderedModules.forEach( module => {
const source = module.render( format === 'es' ); const source = module.render( format === 'es' );
@ -341,6 +358,8 @@ export default class Bundle {
} }
}); });
timeEnd( 'render modules' );
let intro = [ options.intro ] let intro = [ options.intro ]
.concat( .concat(
this.plugins.map( plugin => plugin.intro && plugin.intro() ) this.plugins.map( plugin => plugin.intro && plugin.intro() )
@ -355,8 +374,12 @@ export default class Bundle {
const finalise = finalisers[ format ]; const finalise = finalisers[ format ];
if ( !finalise ) throw new Error( `You must specify an output type - valid options are ${keys( finalisers ).join( ', ' )}` ); if ( !finalise ) throw new Error( `You must specify an output type - valid options are ${keys( finalisers ).join( ', ' )}` );
timeStart( 'render format' );
magicString = finalise( this, magicString.trim(), { exportMode, indentString, intro }, options ); magicString = finalise( this, magicString.trim(), { exportMode, indentString, intro }, options );
timeEnd( 'render format' );
const banner = [ options.banner ] const banner = [ options.banner ]
.concat( this.plugins.map( plugin => plugin.banner ) ) .concat( this.plugins.map( plugin => plugin.banner ) )
.map( callIfFunction ) .map( callIfFunction )
@ -380,6 +403,8 @@ export default class Bundle {
.replace( new RegExp( `\\/\\/#\\s+${SOURCEMAPPING_URL}=.+\\n?`, 'g' ), '' ); .replace( new RegExp( `\\/\\/#\\s+${SOURCEMAPPING_URL}=.+\\n?`, 'g' ), '' );
if ( options.sourceMap ) { if ( options.sourceMap ) {
timeStart( 'sourceMap' );
let file = options.sourceMapFile || options.dest; let file = options.sourceMapFile || options.dest;
if ( file ) file = resolve( typeof process !== 'undefined' ? process.cwd() : '', file ); if ( file ) file = resolve( typeof process !== 'undefined' ? process.cwd() : '', file );
@ -394,6 +419,8 @@ export default class Bundle {
} }
map.sources = map.sources.map( normalize ); map.sources = map.sources.map( normalize );
timeEnd( 'sourceMap' );
} }
return { code, map }; return { code, map };

11
src/Module.js

@ -1,3 +1,4 @@
import { timeStart, timeEnd } from './utils/flushTime.js';
import { parse } from 'acorn/src/index.js'; import { parse } from 'acorn/src/index.js';
import MagicString from 'magic-string'; import MagicString from 'magic-string';
import { assign, blank, deepClone, keys } from './utils/object.js'; import { assign, blank, deepClone, keys } from './utils/object.js';
@ -34,9 +35,14 @@ export default class Module {
this.sourceMapChain = sourceMapChain; this.sourceMapChain = sourceMapChain;
this.comments = []; this.comments = [];
timeStart( 'ast' );
this.ast = ast || tryParse( code, this.comments, bundle.acornOptions, id ); // TODO what happens to comments if AST is provided? this.ast = ast || tryParse( code, this.comments, bundle.acornOptions, id ); // TODO what happens to comments if AST is provided?
this.astClone = deepClone( this.ast ); this.astClone = deepClone( this.ast );
timeEnd( 'ast' );
this.bundle = bundle; this.bundle = bundle;
this.id = id; this.id = id;
this.excludeFromSourcemap = /\0/.test( id ); this.excludeFromSourcemap = /\0/.test( id );
@ -72,8 +78,13 @@ export default class Module {
this.declarations = blank(); this.declarations = blank();
this.type = 'Module'; // TODO only necessary so that Scope knows this should be treated as a function scope... messy this.type = 'Module'; // TODO only necessary so that Scope knows this should be treated as a function scope... messy
this.scope = new ModuleScope( this ); this.scope = new ModuleScope( this );
timeStart( 'analyse' );
this.analyse(); this.analyse();
timeEnd( 'analyse' );
this.strongDependencies = []; this.strongDependencies = [];
} }

11
src/rollup.js

@ -1,3 +1,4 @@
import { timeStart, timeEnd, flushTime } from './utils/flushTime.js';
import { basename } from './utils/path.js'; import { basename } from './utils/path.js';
import { writeFile } from './utils/fs.js'; import { writeFile } from './utils/fs.js';
import { assign, keys } from './utils/object.js'; import { assign, keys } from './utils/object.js';
@ -54,10 +55,18 @@ export function rollup ( options ) {
const bundle = new Bundle( options ); const bundle = new Bundle( options );
timeStart( '--BUILD--' );
return bundle.build().then( () => { return bundle.build().then( () => {
timeEnd( '--BUILD--' );
function generate ( options ) { function generate ( options ) {
timeStart( '--GENERATE--' )
const rendered = bundle.render( options ); const rendered = bundle.render( options );
timeEnd( '--GENERATE--' );
bundle.plugins.forEach( plugin => { bundle.plugins.forEach( plugin => {
if ( plugin.ongenerate ) { if ( plugin.ongenerate ) {
plugin.ongenerate( assign({ plugin.ongenerate( assign({
@ -66,6 +75,8 @@ export function rollup ( options ) {
} }
}); });
flushTime();
return rendered; return rendered;
} }

35
src/utils/flushTime.js

@ -0,0 +1,35 @@
const DEBUG = false;
const map = new Map;
export function timeStart( label ) {
if ( !map.has( label ) ) {
map.set( label, {
time: 0
});
}
map.get( label ).start = process.hrtime();
}
export function timeEnd( label ) {
if ( map.has( label ) ) {
const item = map.get( label );
item.time += toMilliseconds( process.hrtime( item.start ) );
}
}
export function flushTime( log = defaultLog ) {
for ( const item of map.entries() ) {
log( item[0], item[1].time );
}
map.clear();
}
function toMilliseconds( time ) {
return time[0] * 1e+3 + Math.floor( time[1] * 1e-6 );
}
function defaultLog( label, time ) {
if ( DEBUG ) {
console.info( '%dms: %s', time, label );
}
}
Loading…
Cancel
Save