Created
June 5, 2012 23:24
-
-
Save simonlindholm/2878765 to your computer and use it in GitHub Desktop.
Firebug profiling code (for issue 1309)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Time execution of a piece of code, with a user-provided number 'iterations' | |
* of samples (>= 0), or if 'iterations' is null, just one. | |
*/ | |
timeExecution = function(context, code, iterations) | |
{ | |
var userGiven = true; | |
if (iterations === null) | |
{ | |
userGiven = false; | |
iterations = 1; | |
} | |
// In FF15 and up, performance.now() provides submillisecond precision; | |
// for older versions we will have to fall back to Date.now(). | |
var precise = ("performance" in window && "now" in window.performance); | |
// Build up JavaScript code that evaluates and times 'code'. | |
var getTimingCode = function(code) | |
{ | |
var nowF = (precise ? "performance.now()" : "Date.now()"); | |
var ret = "{ "; | |
var timingOverhead = "0"; | |
if (precise) | |
{ | |
// Warm up timer/JavaScript engine. (This helps a tiny bit, I | |
// think. Can't hurt, at least.) | |
ret += [nowF, nowF].join("; ") + "; "; | |
ret += "document.getElementById('dummy'); "; | |
// performance.now() takes some time to run (~0.02ms on my machine); | |
// thus, save some consecutive timings so we can account for that. | |
// The actual overhead fluctuates, so use the most reasonable value. | |
ret += "let __fbt0, __fbt1, __fbt2, __fbt3; "; | |
ret += "__fbt0 = " + nowF + "; try {} finally { if (__fbt1 = " + nowF + ") {} } "; | |
ret += "__fbt2 = " + nowF + "; try {} finally { if (__fbt3 = " + nowF + ") {} } "; | |
timingOverhead = "Math.min(__fbt3 - __fbt2, __fbt1 - __fbt0)"; | |
} | |
ret += "let __fbT0, __fbT1; __fbT0 = " + nowF + "; "; | |
// Add a default return value, to avoid printing a random float if | |
// the evaluated code lacks statements. | |
ret += "void 0; "; | |
// Actually evaluate the code. | |
ret += "try {\n" + code + "\n} finally {\n"; | |
// Save the time taken in window.__fbTiming. To retain the last | |
// evaluated value for display, wrap the timing calculation/ | |
// assignment in an expression. | |
ret += "if (__fbT1 = " + nowF + ", window.__fbTiming = (__fbT1 - __fbT0) - " + timingOverhead + ") {} "; | |
ret += "} }"; | |
return ret; | |
}; | |
var win = (context.baseWindow ? context.baseWindow : context.window); | |
var timeCode = getTimingCode(code); | |
var times = [], resArgs; | |
for (var i = 0; i < iterations; ++i) | |
{ | |
var save = function() { resArgs = arguments; }; | |
Firebug.CommandLine.evaluate(timeCode, context, context.thisValue, | |
win, save, save); | |
// Get the timing out from the global. | |
var timing = NaN; | |
try | |
{ | |
timing = +win.wrappedJSObject.__fbTiming; | |
delete win.wrappedJSObject.__fbTiming; | |
} | |
catch (e) {} | |
times.push(timing); | |
// In case of e.g. syntax errors, we have no data to display. Just | |
// print the last result/error. | |
if (isNaN(timing)) | |
{ | |
Firebug.Console.log.apply(Firebug.Console, resArgs); | |
return; | |
} | |
} | |
// Print the result of the last iteration. | |
Firebug.Console.log.apply(Firebug.Console, resArgs); | |
if (precise && ((times.length >= 5 && times[0] < 0.2) || | |
(times.length >= 10 && times[0] < 1.0))) | |
{ | |
// The first ~3 results are slightly too high, on my machine. Throw | |
// them away. | |
times.splice(0, 3); | |
} | |
if (times.length >= 10) | |
{ | |
// We have enough data to be able to throw out outliers. | |
var edge = Math.floor(times.length * 0.2); | |
times.sort(function(a, b) { return a - b; }); | |
times = times.slice(edge, -edge); | |
} | |
// Calculate a simple average. If it is negative (from incorrect timing | |
// overhead correction - most commonly from timing something empty), | |
// show it as 0 instead. | |
var average = 0; | |
for (var i = 0; i < times.length; ++i) | |
average += times[i]; | |
average /= times.length; | |
if (average < 0) | |
average = 0; | |
// Truncate the result to 2 decimals, or more for more iterations. | |
var decimals = (precise ? 2 : 0); | |
for (var order = 2; order <= iterations; order *= 10) | |
++decimals; | |
var str = average.toFixed(decimals); | |
if (userGiven) | |
str = Locale.$STRF("commandline.TimeUsageAverage", [str, iterations]); | |
else | |
str = Locale.$STRF("commandline.TimeUsage", [str]); | |
Firebug.Console.logFormatted([str], context, "log"); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment