diff --git a/src/core.js b/src/core.js index 765a239b7..90fcb09d5 100644 --- a/src/core.js +++ b/src/core.js @@ -60,13 +60,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.bench = new Bench(); + this.bench.enabled = !!globalScope.PDFJS.enableBench; this.xref = xref; this.ref = ref; @@ -187,6 +182,8 @@ var Page = (function PageClosure() { return this.IRQueue; } + this.bench.time('Build IR Queue'); + var xref = this.xref; var content = xref.fetchIfRef(this.content); var resources = xref.fetchIfRef(this.resources); @@ -201,11 +198,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.bench.timeEnd('Build IR Queue'); + return this.IRQueue; }, ensureFonts: function pageEnsureFonts(fonts, callback) { + this.bench.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; @@ -215,7 +215,7 @@ var Page = (function PageClosure() { var fontObjs = FontLoader.bind( fonts, function pageEnsureFontsFontObjs(fontObjs) { - this.stats.fonts = Date.now(); + this.bench.timeEnd('Font Loading'); callback.call(this); }.bind(this), @@ -224,6 +224,8 @@ var Page = (function PageClosure() { }, display: function pageDisplay(gfx, callback) { + var bench = this.bench; + bench.time('Rendering'); var xref = this.xref; var resources = xref.fetchIfRef(this.resources); var mediaBox = xref.fetchIfRef(this.mediaBox); @@ -244,8 +246,9 @@ var Page = (function PageClosure() { function next() { startIdx = gfx.executeIRQueue(IRQueue, startIdx, next); if (startIdx == length) { - self.stats.render = Date.now(); gfx.endDrawing(); + bench.timeEnd('Rendering'); + bench.timeEnd('Overall'); if (callback) callback(); } } @@ -388,15 +391,14 @@ var Page = (function PageClosure() { return items; }, startRendering: function pageStartRendering(ctx, callback, textLayer) { - this.startRenderingTime = Date.now(); - + var bench = this.bench; + bench.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() { @@ -677,7 +679,7 @@ var PDFDoc = (function PDFDocClosure() { var pageNum = data.pageNum; var page = this.pageCache[pageNum]; var depFonts = data.depFonts; - + page.bench.timeEnd('Page Request'); page.startRenderingFromIRQueue(data.IRQueue, depFonts); }, this); @@ -786,6 +788,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.bench.time('Page Request'); this.messageHandler.send('page_request', page.pageNumber + 1); }.bind(this)); }, diff --git a/src/util.js b/src/util.js index 99b422296..80cf937c4 100644 --- a/src/util.js +++ b/src/util.js @@ -338,3 +338,55 @@ var Promise = (function PromiseClosure() { return Promise; })(); +var Bench = (function BenchClosure() { + function rpad(str, pad, length) { + while (str.length < length) + str += pad; + return str; + } + function Bench() { + this.started = {}; + this.times = []; + this.enabled = true; + } + Bench.prototype = { + time: function benchTime(name) { + if (!this.enabled) + return; + if (name in this.started) + throw 'Timer is already running for ' + name; + this.started[name] = Date.now(); + }, + timeEnd: function benchTimeEnd(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 benchToString() { + 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 Bench; +})(); diff --git a/web/viewer.css b/web/viewer.css index e355f7fc2..d5c18504c 100644 --- a/web/viewer.css +++ b/web/viewer.css @@ -67,6 +67,12 @@ body { span#info { display: none; + position: fixed; + top: 32px; + right: 0px; + font-size: 10px; + white-space: pre; + font-family: courier; } @-moz-document regexp("http:.*debug=1.*") { diff --git a/web/viewer.js b/web/viewer.js index ac3fbff0c..fc7d99cdd 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -222,6 +222,7 @@ var PDFView = { return; } + pages[val - 1].updateStats(); currentPageNumber = val; var event = document.createEvent('UIEvents'); event.initUIEvent('pagechange', false, false, window, 0); @@ -421,7 +422,7 @@ var PDFView = { for (var i = 1; i <= pagesCount; i++) { var page = pdf.getPage(i); var pageView = new PageView(container, page, i, page.width, page.height, - page.stats, this.navigateTo.bind(this)); + page.bench, this.navigateTo.bind(this)); var thumbnailView = new ThumbnailView(sidebar, page, i, page.width / page.height); bindOnAfterDraw(pageView, thumbnailView); @@ -581,7 +582,7 @@ var PDFView = { }; var PageView = function pageView(container, content, id, pageWidth, pageHeight, - stats, navigateTo) { + bench, navigateTo) { this.id = id; this.content = content; @@ -800,11 +801,11 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight, ctx.restore(); ctx.translate(-this.x * scale, -this.y * scale); - stats.begin = Date.now(); this.content.startRendering(ctx, (function pageViewDrawCallback(error) { if (error) PDFView.error('An error occurred while rendering the page.', error); + this.stats = content.bench; this.updateStats(); if (this.onAfterDraw) this.onAfterDraw(); @@ -819,10 +820,12 @@ 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').innerHTML = str; + if (!PDFJS.enableBench || !this.stats || PDFView.page != this.id) + return; + var stats = this.stats; + var statsHtml = 'Page ' + this.id + '\n'; + statsHtml += stats.toString().replace(/\n/g, '
'); + document.getElementById('info').innerHTML = statsHtml; }; }; @@ -1016,6 +1019,11 @@ window.addEventListener('load', function webViewerLoad(evt) { if ('disableTextLayer' in params) PDFJS.disableTextLayer = (params['disableTextLayer'] === 'true'); + if ('enableBench' in params) + PDFJS.enableBench = (params['enableBench'] === 'true'); + if (PDFJS.enableBench) + document.getElementById('info').style.display = 'block'; + var sidebarScrollView = document.getElementById('sidebarScrollView'); sidebarScrollView.addEventListener('scroll', updateThumbViewArea, true); }, true);