Avoid unnecessary array allocations in EvaluatorPreprocessor_read().

EvaluatorPreprocessor_read() is called in two cases. For the normal
layer, the args array it produces is used beyond the bounds of the loop
in which EvaluatorPreprocessor_read() is called.

But for the text layer, the args array is used in a very short-term
fashion. This change reworks things so that a single array is repeatedly
used for the text layer. This reduces total JS allocations for the
Spoorkaart map by 11%, and has similar effects on many other PDFs.
This commit is contained in:
Nicholas Nethercote 2014-08-10 17:23:23 -07:00
parent 0e4d9061b2
commit 7cbd057deb

View File

@ -642,8 +642,15 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
return new Promise(function next(resolve, reject) {
timeSlotManager.reset();
var stop, operation = {}, i, ii, cs;
while (!(stop = timeSlotManager.check()) &&
preprocessor.read(operation)) {
while (!(stop = timeSlotManager.check())) {
// The arguments parsed by read() are used beyond this loop, so we
// cannot reuse the same array on each iteration. Therefore we pass
// in |null| as the initial value (see the comment on
// EvaluatorPreprocessor_read() for why).
operation.args = null;
if (!(preprocessor.read(operation))) {
break;
}
var args = operation.args;
var fn = operation.fn;
@ -1037,12 +1044,20 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
return new Promise(function next(resolve, reject) {
timeSlotManager.reset();
var stop, operation = {};
while (!(stop = timeSlotManager.check()) &&
(preprocessor.read(operation))) {
var stop, operation = {}, args = [];
while (!(stop = timeSlotManager.check())) {
// The arguments parsed by read() are not used beyond this loop, so
// we can reuse the same array on every iteration, thus avoiding
// unnecessary allocations.
args.length = 0;
operation.args = args;
if (!(preprocessor.read(operation))) {
break;
}
textState = stateManager.state;
var fn = operation.fn;
var args = operation.args;
args = operation.args;
switch (fn | 0) {
case OPS.setFont:
textState.fontSize = args[1];
@ -2112,10 +2127,29 @@ var EvaluatorPreprocessor = (function EvaluatorPreprocessorClosure() {
return this.stateManager.stateStack.length;
},
// |operation| is an object with two fields:
//
// - |fn| is an out param.
//
// - |args| is an inout param. On entry, it should have one of two values.
//
// - An empty array. This indicates that the caller is providing the
// array in which the args will be stored in. The caller should use
// this value if it can reuse a single array for each call to read().
//
// - |null|. This indicates that the caller needs this function to create
// the array in which any args are stored in. If there are zero args,
// this function will leave |operation.args| as |null| (thus avoiding
// allocations that would occur if we used an empty array to represent
// zero arguments). Otherwise, it will replace |null| with a new array
// containing the arguments. The caller should use this value if it
// cannot reuse an array for each call to read().
//
// These two modes are present because this function is very hot and so
// avoiding allocations where possible is worthwhile.
//
read: function EvaluatorPreprocessor_read(operation) {
// We use an array to represent args, except we use |null| to represent
// no args because it's more compact than an empty array.
var args = null;
var args = operation.args;
while (true) {
var obj = this.parser.getObj();
if (isCmd(obj)) {