| diff --git a/src/jsifier.js b/src/jsifier.js |
| index da8c4db..2d606be 100644 |
| --- a/src/jsifier.js |
| +++ b/src/jsifier.js |
| @@ -432,12 +432,16 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { |
| func.JS = '\nfunction ' + func.ident + '(' + func.paramIdents.join(', ') + ') {\n'; |
| |
| if (PROFILE) { |
| - func.JS += ' if (PROFILING) { ' |
| - + 'var __parentProfilingNode__ = PROFILING_NODE; PROFILING_NODE = PROFILING_NODE.children["' + func.ident + '"]; ' |
| - + 'if (!PROFILING_NODE) __parentProfilingNode__.children["' + func.ident + '"] = PROFILING_NODE = { time: 0, children: {}, calls: 0 };' |
| - + 'PROFILING_NODE.calls++; ' |
| - + 'var __profilingStartTime__ = Date.now() ' |
| - + '}\n'; |
| + if (PROFILE_CALLGRAPH) { |
| + func.JS += ' if (PROFILING) { ' |
| + + 'var __parentProfilingNode__ = PROFILING_NODE; PROFILING_NODE = PROFILING_NODE.children["' + func.ident + '"]; ' |
| + + 'if (!PROFILING_NODE) __parentProfilingNode__.children["' + func.ident + '"] = PROFILING_NODE = { time: 0, children: {}, calls: 0 };' |
| + + 'PROFILING_NODE.calls++; ' |
| + + 'var __profilingStartTime__ = Date.now() ' |
| + + '}\n'; |
| + } else { |
| + func.JS += ' PROFILING_DATA[' + Profiling.getIndex(func.ident) + '] -= Date.now();'; |
| + } |
| } |
| |
| func.JS += ' ' + RuntimeGenerator.stackEnter(func.initialStack) + ';\n'; |
| @@ -733,10 +737,14 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { |
| makeFuncLineActor('return', function(item) { |
| var ret = RuntimeGenerator.stackExit(item.funcData.initialStack) + ';\n'; |
| if (PROFILE) { |
| - ret += 'if (PROFILING) { ' |
| - + 'PROFILING_NODE.time += Date.now() - __profilingStartTime__; ' |
| - + 'PROFILING_NODE = __parentProfilingNode__ ' |
| - + '}\n'; |
| + if (PROFILE_CALLGRAPH) { |
| + ret += 'if (PROFILING) { ' |
| + + 'PROFILING_NODE.time += Date.now() - __profilingStartTime__; ' |
| + + 'PROFILING_NODE = __parentProfilingNode__ ' |
| + + '}\n'; |
| + } else { |
| + ret += 'PROFILING_DATA[' + Profiling.getIndex(item.funcData.ident) + '] += Date.now();' |
| + } |
| } |
| if (LABEL_DEBUG) { |
| ret += "print(INDENT + 'Exiting: " + item.funcData.ident + "');\n" |
| @@ -945,6 +953,9 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { |
| print(shellParts[0]); |
| var preFile = BUILD_AS_SHARED_LIB ? 'preamble_sharedlib.js' : 'preamble.js'; |
| var pre = processMacros(preprocess(read(preFile).replace('{{RUNTIME}}', getRuntime()), CONSTANTS)); |
| + if (PROFILE && !PROFILE_CALLGRAPH) { |
| + pre = pre.replace('{{PROFILING_DATA}}', Profiling.generateIndexing()); |
| + } |
| print(pre); |
| print('Runtime.QUANTUM_SIZE = ' + QUANTUM_SIZE); |
| if (RUNTIME_TYPE_INFO) { |
| diff --git a/src/modules.js b/src/modules.js |
| index 20f42f9..624c5ae 100644 |
| --- a/src/modules.js |
| +++ b/src/modules.js |
| @@ -226,13 +226,15 @@ var Functions = { |
| |
| indexedFunctions: [0, 0], // Start at a non-0 (even, see below) value |
| |
| + skip: true, |
| + |
| // Mark a function as needing indexing, and returns the index |
| getIndex: function(ident) { |
| var key = this.indexedFunctions.indexOf(ident); |
| if (key < 0) { |
| key = this.indexedFunctions.length; |
| this.indexedFunctions[key] = ident; |
| - this.indexedFunctions[key+1] = 0; // Need to have keys be even numbers, see |polymorph| test |
| + if (this.skip) this.indexedFunctions[key+1] = 0; // Need to have keys be even numbers, see |polymorph| test |
| } |
| return key.toString(); |
| }, |
| @@ -281,3 +283,19 @@ var LibraryManager = { |
| } |
| }; |
| |
| +var Profiling = { // We use the same principle of function hashing as in Functions |
| + currFunctions: [], |
| + implementedFunctions: null, |
| + indexedFunctions: [], |
| + getIndex: Functions.getIndex, |
| + |
| + generateIndexing: function() { |
| + var ret = 'var PROFILING_DATA = new Array(' + this.indexedFunctions.length + ');\n' |
| + + 'for (var i = 0; i < ' + this.indexedFunctions.length + '; i++) {\n' |
| + + ' PROFILING_DATA[i] = 0;\n' |
| + + '}\n' |
| + + 'var PROFILING_NAMES = ' + JSON.stringify(this.indexedFunctions) + ';\n'; |
| + return ret; |
| + } |
| +}; |
| + |
| diff --git a/src/preamble.js b/src/preamble.js |
| index 1c1ec91..1bda448 100644 |
| --- a/src/preamble.js |
| +++ b/src/preamble.js |
| @@ -276,22 +276,26 @@ var START_TIME = Date.now(); |
| |
| #if PROFILE |
| var PROFILING = 0; |
| + |
| +#if PROFILE_CALLGRAPH |
| var PROFILING_ROOT = { time: 0, children: {}, calls: 0 }; |
| var PROFILING_NODE; |
| - |
| function startProfiling() { |
| PROFILING_NODE = PROFILING_ROOT; |
| PROFILING = 1; |
| } |
| Module['startProfiling'] = startProfiling; |
| - |
| function stopProfiling() { |
| PROFILING = 0; |
| assert(PROFILING_NODE === PROFILING_ROOT, 'Must have popped all the profiling call stack'); |
| } |
| Module['stopProfiling'] = stopProfiling; |
| +#else |
| +{{PROFILING_DATA}} |
| +#endif |
| |
| function printProfiling() { |
| +#if PROFILE_CALLGRAPH |
| function dumpData(name_, node, indent) { |
| print(indent + ('________' + node.time).substr(-8) + ': ' + name_ + ' (' + node.calls + ')'); |
| var children = []; |
| @@ -303,9 +307,20 @@ function printProfiling() { |
| children.forEach(function(child) { dumpData(child.name_, child, indent + ' ') }); |
| } |
| dumpData('root', PROFILING_ROOT, ' '); |
| +#else |
| + var items = []; |
| + for (var i = 0; i < PROFILING_NAMES.length; i++) { |
| + items.push({ name_: PROFILING_NAMES[i], time: PROFILING_DATA[i] }); |
| + } |
| + items.sort(function(x, y) { return y.time - x.time }); |
| + items.forEach(function(item) { |
| + print(('________' + item.time).substr(-8) + ': ' + item.name_); |
| + }); |
| +#endif |
| } |
| Module['printProfiling'] = printProfiling; |
| |
| +#if PROFILE_CALLGRAPH |
| function printXULProfiling() { |
| function dumpData(name_, node, indent) { |
| var children = []; |
| @@ -357,6 +372,7 @@ function printXULProfiling() { |
| } |
| Module['printXULProfiling'] = printXULProfiling; |
| #endif |
| +#endif |
| |
| //======================================== |
| // Runtime essentials |
| diff --git a/src/settings.js b/src/settings.js |
| index ef8b399..749468b 100644 |
| --- a/src/settings.js |
| +++ b/src/settings.js |
| @@ -114,7 +114,8 @@ AUTO_OPTIMIZE = 0; // When run with the CHECK_* options, will not fail on errors |
| // checking enabled and which do not, that is, this is a way to automate the |
| // generation of line data for CORRECT_*_LINES options |
| |
| -PROFILE = 0; // Enables runtime profiling. See test_profiling for a usage example. |
| +PROFILE = 1; // Enables runtime profiling. As lightweight as possible. |
| +PROFILE_CALLGRAPH = 0; // Much heavier profiling, of entire callgraphs. |
| |
| EXPORTED_FUNCTIONS = ['_main']; // Functions that are explicitly exported, so they are guaranteed to |
| // be accessible outside of the generated code. |
| diff --git a/tests/runner.py b/tests/runner.py |
| index f7ae9b0..3caedad 100644 |
| --- a/tests/runner.py |
| +++ b/tests/runner.py |
| @@ -3525,16 +3525,16 @@ if 'benchmark' not in str(sys.argv): |
| def post(filename): |
| src = open(filename, 'a') |
| src.write(''' |
| - startProfiling(); |
| + //startProfiling(); |
| run(); |
| - stopProfiling(); |
| + //stopProfiling(); |
| printProfiling(); |
| print('*ok*'); |
| ''') |
| src.close() |
| |
| # Using build_ll_hook forces a recompile, which leads to DFE being done even without opts |
| - self.do_test(src, ': __Z6inner1i (5000)\n*ok*', post_build=post) |
| + self.do_test(src, ': __Z6inner2i\n*ok*', post_build=post) |
| |
| ### Integration tests |
| |