Merge pull request #1067 from brendandahl/stats
Start of the benchmark recording framework.
This commit is contained in:
commit
6833f64db3
33
src/core.js
33
src/core.js
@ -63,13 +63,8 @@ var Page = (function PageClosure() {
|
||||
function Page(xref, pageNumber, pageDict, ref) {
|
||||
this.pageNumber = pageNumber;
|
||||
this.pageDict = pageDict;
|
||||
this.stats = {
|
||||
create: Date.now(),
|
||||
compile: 0.0,
|
||||
fonts: 0.0,
|
||||
images: 0.0,
|
||||
render: 0.0
|
||||
};
|
||||
this.stats = new StatTimer();
|
||||
this.stats.enabled = !!globalScope.PDFJS.enableStats;
|
||||
this.xref = xref;
|
||||
this.ref = ref;
|
||||
|
||||
@ -200,6 +195,8 @@ var Page = (function PageClosure() {
|
||||
return this.IRQueue;
|
||||
}
|
||||
|
||||
this.stats.time('Build IR Queue');
|
||||
|
||||
var xref = this.xref;
|
||||
var content = xref.fetchIfRef(this.content);
|
||||
var resources = xref.fetchIfRef(this.resources);
|
||||
@ -217,11 +214,14 @@ var Page = (function PageClosure() {
|
||||
var pe = this.pe = new PartialEvaluator(
|
||||
xref, handler, 'p' + this.pageNumber + '_');
|
||||
var IRQueue = {};
|
||||
return (this.IRQueue = pe.getIRQueue(content, resources, IRQueue,
|
||||
dependency));
|
||||
this.IRQueue = pe.getIRQueue(content, resources, IRQueue, dependency);
|
||||
|
||||
this.stats.timeEnd('Build IR Queue');
|
||||
return this.IRQueue;
|
||||
},
|
||||
|
||||
ensureFonts: function pageEnsureFonts(fonts, callback) {
|
||||
this.stats.time('Font Loading');
|
||||
// Convert the font names to the corresponding font obj.
|
||||
for (var i = 0, ii = fonts.length; i < ii; i++) {
|
||||
fonts[i] = this.objs.objs[fonts[i]].data;
|
||||
@ -231,7 +231,7 @@ var Page = (function PageClosure() {
|
||||
var fontObjs = FontLoader.bind(
|
||||
fonts,
|
||||
function pageEnsureFontsFontObjs(fontObjs) {
|
||||
this.stats.fonts = Date.now();
|
||||
this.stats.timeEnd('Font Loading');
|
||||
|
||||
callback.call(this);
|
||||
}.bind(this),
|
||||
@ -240,6 +240,8 @@ var Page = (function PageClosure() {
|
||||
},
|
||||
|
||||
display: function pageDisplay(gfx, callback) {
|
||||
var stats = this.stats;
|
||||
stats.time('Rendering');
|
||||
var xref = this.xref;
|
||||
var resources = xref.fetchIfRef(this.resources);
|
||||
var mediaBox = xref.fetchIfRef(this.mediaBox);
|
||||
@ -266,8 +268,9 @@ var Page = (function PageClosure() {
|
||||
function next() {
|
||||
startIdx = gfx.executeIRQueue(IRQueue, startIdx, next, stepper);
|
||||
if (startIdx == length) {
|
||||
self.stats.render = Date.now();
|
||||
gfx.endDrawing();
|
||||
stats.timeEnd('Rendering');
|
||||
stats.timeEnd('Overall');
|
||||
if (callback) callback();
|
||||
}
|
||||
}
|
||||
@ -431,15 +434,14 @@ var Page = (function PageClosure() {
|
||||
return items;
|
||||
},
|
||||
startRendering: function pageStartRendering(ctx, callback, textLayer) {
|
||||
this.startRenderingTime = Date.now();
|
||||
|
||||
var stats = this.stats;
|
||||
stats.time('Overall');
|
||||
// If there is no displayReadyPromise yet, then the IRQueue was never
|
||||
// requested before. Make the request and create the promise.
|
||||
if (!this.displayReadyPromise) {
|
||||
this.pdf.startRendering(this);
|
||||
this.displayReadyPromise = new Promise();
|
||||
}
|
||||
|
||||
// Once the IRQueue and fonts are loaded, perform the actual rendering.
|
||||
this.displayReadyPromise.then(
|
||||
function pageDisplayReadyPromise() {
|
||||
@ -731,7 +733,7 @@ var PDFDoc = (function PDFDocClosure() {
|
||||
var pageNum = data.pageNum;
|
||||
var page = this.pageCache[pageNum];
|
||||
var depFonts = data.depFonts;
|
||||
|
||||
page.stats.timeEnd('Page Request');
|
||||
page.startRenderingFromIRQueue(data.IRQueue, depFonts);
|
||||
}, this);
|
||||
|
||||
@ -840,6 +842,7 @@ var PDFDoc = (function PDFDocClosure() {
|
||||
startRendering: function pdfDocStartRendering(page) {
|
||||
// The worker might not be ready to receive the page request yet.
|
||||
this.workerReadyPromise.then(function pdfDocStartRenderingThen() {
|
||||
page.stats.time('Page Request');
|
||||
this.messageHandler.send('page_request', page.pageNumber + 1);
|
||||
}.bind(this));
|
||||
},
|
||||
|
52
src/util.js
52
src/util.js
@ -416,3 +416,55 @@ var Promise = (function PromiseClosure() {
|
||||
return Promise;
|
||||
})();
|
||||
|
||||
var StatTimer = (function StatTimerClosure() {
|
||||
function rpad(str, pad, length) {
|
||||
while (str.length < length)
|
||||
str += pad;
|
||||
return str;
|
||||
}
|
||||
function StatTimer() {
|
||||
this.started = {};
|
||||
this.times = [];
|
||||
this.enabled = true;
|
||||
}
|
||||
StatTimer.prototype = {
|
||||
time: function statTimerTime(name) {
|
||||
if (!this.enabled)
|
||||
return;
|
||||
if (name in this.started)
|
||||
throw 'Timer is already running for ' + name;
|
||||
this.started[name] = Date.now();
|
||||
},
|
||||
timeEnd: function statTimerTimeEnd(name) {
|
||||
if (!this.enabled)
|
||||
return;
|
||||
if (!(name in this.started))
|
||||
throw 'Timer has not been started for ' + name;
|
||||
this.times.push({
|
||||
'name': name,
|
||||
'start': this.started[name],
|
||||
'end': Date.now()
|
||||
});
|
||||
// Remove timer from started so it can be called again.
|
||||
delete this.started[name];
|
||||
},
|
||||
toString: function statTimerToString() {
|
||||
var times = this.times;
|
||||
var out = '';
|
||||
// Find the longest name for padding purposes.
|
||||
var longest = 0;
|
||||
for (var i = 0, ii = times.length; i < ii; ++i) {
|
||||
var name = times[i]['name'];
|
||||
if (name.length > longest)
|
||||
longest = name.length;
|
||||
}
|
||||
for (var i = 0, ii = times.length; i < ii; ++i) {
|
||||
var span = times[i];
|
||||
var duration = span.end - span.start;
|
||||
out += rpad(span['name'], ' ', longest) + ' ' + duration + 'ms\n';
|
||||
}
|
||||
return out;
|
||||
}
|
||||
};
|
||||
return StatTimer;
|
||||
})();
|
||||
|
@ -318,6 +318,58 @@ var Stepper = (function StepperClosure() {
|
||||
return Stepper;
|
||||
})();
|
||||
|
||||
var Stats = (function Stats() {
|
||||
var stats = [];
|
||||
function clear(node) {
|
||||
while (node.hasChildNodes())
|
||||
node.removeChild(node.lastChild);
|
||||
}
|
||||
function getStatIndex(pageNumber) {
|
||||
for (var i = 0, ii = stats.length; i < ii; ++i)
|
||||
if (stats[i].pageNumber === pageNumber)
|
||||
return i;
|
||||
return false;
|
||||
}
|
||||
return {
|
||||
// Poperties/functions needed by PDFBug.
|
||||
id: 'Stats',
|
||||
name: 'Stats',
|
||||
panel: null,
|
||||
manager: null,
|
||||
init: function init() {
|
||||
this.panel.setAttribute('style', 'padding: 5px;');
|
||||
PDFJS.enableStats = true;
|
||||
},
|
||||
enabled: false,
|
||||
active: false,
|
||||
// Stats specific functions.
|
||||
add: function(pageNumber, stat) {
|
||||
if (!stat)
|
||||
return;
|
||||
var statsIndex = getStatIndex(pageNumber);
|
||||
if (statsIndex !== false) {
|
||||
var b = stats[statsIndex];
|
||||
this.panel.removeChild(b.div);
|
||||
stats.splice(statsIndex, 1);
|
||||
}
|
||||
var wrapper = document.createElement('div');
|
||||
wrapper.className = 'stats';
|
||||
var title = document.createElement('div');
|
||||
title.className = 'title';
|
||||
title.textContent = 'Page: ' + pageNumber;
|
||||
var statsDiv = document.createElement('div');
|
||||
statsDiv.textContent = stat.toString();
|
||||
wrapper.appendChild(title);
|
||||
wrapper.appendChild(statsDiv);
|
||||
stats.push({ pageNumber: pageNumber, div: wrapper });
|
||||
stats.sort(function(a, b) { return a.pageNumber - b.pageNumber});
|
||||
clear(this.panel);
|
||||
for (var i = 0, ii = stats.length; i < ii; ++i)
|
||||
this.panel.appendChild(stats[i].div);
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
// Manages all the debugging tools.
|
||||
var PDFBug = (function PDFBugClosure() {
|
||||
var panelWidth = 300;
|
||||
@ -327,8 +379,29 @@ var PDFBug = (function PDFBugClosure() {
|
||||
return {
|
||||
tools: [
|
||||
FontInspector,
|
||||
StepperManager
|
||||
StepperManager,
|
||||
Stats
|
||||
],
|
||||
enable: function(ids) {
|
||||
var all = false, tools = this.tools;
|
||||
if (ids.length === 1 && ids[0] === 'all')
|
||||
all = true;
|
||||
for (var i = 0; i < tools.length; ++i) {
|
||||
var tool = tools[i];
|
||||
if (all || ids.indexOf(tool.id) !== -1)
|
||||
tool.enabled = true;
|
||||
}
|
||||
if (!all) {
|
||||
// Sort the tools by the order they are enabled.
|
||||
tools.sort(function(a, b) {
|
||||
var indexA = ids.indexOf(a.id);
|
||||
indexA = indexA < 0 ? tools.length : indexA;
|
||||
var indexB = ids.indexOf(b.id);
|
||||
indexB = indexB < 0 ? tools.length : indexB;
|
||||
return indexA - indexB;
|
||||
});
|
||||
}
|
||||
},
|
||||
init: function init() {
|
||||
/*
|
||||
* Basic Layout:
|
||||
|
@ -65,16 +65,6 @@ body {
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
span#info {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@-moz-document regexp("http:.*debug=1.*") {
|
||||
span#info {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
/* === Sidebar === */
|
||||
#sidebar {
|
||||
position: fixed;
|
||||
@ -442,3 +432,11 @@ canvas {
|
||||
background: yellow;
|
||||
opacity: 0.3;
|
||||
}
|
||||
#PDFBug .stats {
|
||||
font-size: 10px;
|
||||
white-space: pre;
|
||||
font-family: courier;
|
||||
}
|
||||
#PDFBug .stats .title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
@ -102,7 +102,6 @@
|
||||
<img src="images/bookmark.svg" alt="Bookmark" align="top" height="16"/>
|
||||
</a>
|
||||
|
||||
<span id="info">--</span>
|
||||
</div>
|
||||
<div id="errorWrapper" hidden='true'>
|
||||
<div id="errorMessageLeft">
|
||||
|
@ -234,6 +234,7 @@ var PDFView = {
|
||||
return;
|
||||
}
|
||||
|
||||
pages[val - 1].updateStats();
|
||||
currentPageNumber = val;
|
||||
var event = document.createEvent('UIEvents');
|
||||
event.initUIEvent('pagechange', false, false, window, 0);
|
||||
@ -870,7 +871,6 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
|
||||
// Rendering area
|
||||
|
||||
var self = this;
|
||||
stats.begin = Date.now();
|
||||
this.content.startRendering(ctx, function pageViewDrawCallback(error) {
|
||||
if (self.loadingIconDiv) {
|
||||
div.removeChild(self.loadingIconDiv);
|
||||
@ -880,6 +880,7 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
|
||||
if (error)
|
||||
PDFView.error('An error occurred while rendering the page.', error);
|
||||
|
||||
self.stats = content.stats;
|
||||
self.updateStats();
|
||||
if (self.onAfterDraw)
|
||||
self.onAfterDraw();
|
||||
@ -893,10 +894,10 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
|
||||
};
|
||||
|
||||
this.updateStats = function pageViewUpdateStats() {
|
||||
var t1 = stats.compile, t2 = stats.fonts, t3 = stats.render;
|
||||
var str = 'Time to compile/fonts/render: ' +
|
||||
(t1 - stats.begin) + '/' + (t2 - t1) + '/' + (t3 - t2) + ' ms';
|
||||
document.getElementById('info').textContent = str;
|
||||
if (PDFJS.pdfBug && Stats.enabled) {
|
||||
var stats = this.stats;
|
||||
Stats.add(this.id, stats);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@ -1184,17 +1185,8 @@ window.addEventListener('load', function webViewerLoad(evt) {
|
||||
if ('pdfBug' in hashParams) {
|
||||
PDFJS.pdfBug = true;
|
||||
var pdfBug = hashParams['pdfBug'];
|
||||
var all = false, enabled = [];
|
||||
if (pdfBug === 'all')
|
||||
all = true;
|
||||
else
|
||||
enabled = pdfBug.split(',');
|
||||
var debugTools = PDFBug.tools;
|
||||
for (var i = 0; i < debugTools.length; ++i) {
|
||||
var tool = debugTools[i];
|
||||
if (all || enabled.indexOf(tool.id) !== -1)
|
||||
tool.enabled = true;
|
||||
}
|
||||
var enabled = pdfBug.split(',');
|
||||
PDFBug.enable(enabled);
|
||||
PDFBug.init();
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user