Skip to content

Commit

Permalink
util: improve format performance
Browse files Browse the repository at this point in the history
PR-URL: nodejs/node#15422
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Refael Ackermann <refack@gmail.com>
  • Loading branch information
BridgeAR authored and Stephen Belanger committed Sep 21, 2017
1 parent f643c59 commit be98bf9
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 65 deletions.
35 changes: 15 additions & 20 deletions benchmark/util/format.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,30 @@

const util = require('util');
const common = require('../common');
const types = [
'string',
'number',
'object',
'unknown',
'no-replace'
];
const bench = common.createBenchmark(main, {
n: [2e6],
type: types
});

const inputs = {
'string': ['Hello, my name is %s', 'fred'],
'number': ['Hi, I was born in %d', 1942],
'object': ['An error occurred %j', { msg: 'This is an error', code: 'ERR' }],
'string': ['Hello, my name is %s', 'Fred'],
'string-2': ['Hello, %s is my name', 'Fred'],
'number': ['Hi, I was born in %d', 1989],
'replace-object': ['An error occurred %j', { msg: 'This is an error' }],
'unknown': ['hello %a', 'test'],
'no-replace': [1, 2]
'no-replace': [1, 2],
'no-replace-2': ['foobar', 'yeah', 'mensch', 5],
'only-objects': [{ msg: 'This is an error' }, { msg: 'This is an error' }],
'many-%': ['replace%%%%s%%%%many%s%s%s', 'percent'],
};

function main(conf) {
const n = conf.n | 0;
const type = conf.type;
const bench = common.createBenchmark(main, {
n: [4e6],
type: Object.keys(inputs)
});

const input = inputs[type];
function main({ n, type }) {
const [first, second] = inputs[type];

bench.start();
for (var i = 0; i < n; i++) {
util.format(input[0], input[1]);
util.format(first, second);
}
bench.end(n);
}
94 changes: 49 additions & 45 deletions lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,70 +177,74 @@ function tryStringify(arg) {
}

function format(f) {
var i, tempStr;
if (typeof f !== 'string') {
const objects = new Array(arguments.length);
for (var index = 0; index < arguments.length; index++) {
objects[index] = inspect(arguments[index]);
if (arguments.length === 0) return '';
var res = '';
for (i = 0; i < arguments.length - 1; i++) {
res += inspect(arguments[i]);
res += ' ';
}
return objects.join(' ');
res += inspect(arguments[i]);
return res;
}

if (arguments.length === 1) return f;

var str = '';
var a = 1;
var lastPos = 0;
for (var i = 0; i < f.length;) {
if (f.charCodeAt(i) === 37/*'%'*/ && i + 1 < f.length) {
if (f.charCodeAt(i + 1) !== 37/*'%'*/ && a >= arguments.length) {
++i;
continue;
}
if (lastPos < i)
for (i = 0; i < f.length - 1; i++) {
if (f.charCodeAt(i) === 37) { // '%'
const nextChar = f.charCodeAt(++i);
if (a !== arguments.length) {
switch (nextChar) {
case 115: // 's'
tempStr = String(arguments[a++]);
break;
case 106: // 'j'
tempStr = tryStringify(arguments[a++]);
break;
case 100: // 'd'
tempStr = `${Number(arguments[a++])}`;
break;
case 79: // 'O'
tempStr = inspect(arguments[a++]);
break;
case 111: // 'o'
tempStr = inspect(arguments[a++],
{ showHidden: true, depth: 4, showProxy: true });
break;
case 105: // 'i'
tempStr = `${parseInt(arguments[a++])}`;
break;
case 102: // 'f'
tempStr = `${parseFloat(arguments[a++])}`;
break;
case 37: // '%'
str += f.slice(lastPos, i);
lastPos = i + 1;
continue;
default: // any other character is not a correct placeholder
continue;
}
if (lastPos !== i - 1)
str += f.slice(lastPos, i - 1);
str += tempStr;
lastPos = i + 1;
} else if (nextChar === 37) {
str += f.slice(lastPos, i);
switch (f.charCodeAt(i + 1)) {
case 100: // 'd'
str += Number(arguments[a++]);
break;
case 105: // 'i'
str += parseInt(arguments[a++]);
break;
case 102: // 'f'
str += parseFloat(arguments[a++]);
break;
case 106: // 'j'
str += tryStringify(arguments[a++]);
break;
case 115: // 's'
str += String(arguments[a++]);
break;
case 79: // 'O'
str += inspect(arguments[a++]);
break;
case 111: // 'o'
str += inspect(arguments[a++],
{ showHidden: true, depth: 4, showProxy: true });
break;
case 37: // '%'
str += '%';
break;
default: // any other character is not a correct placeholder
str += '%';
lastPos = i = i + 1;
continue;
lastPos = i + 1;
}
lastPos = i = i + 2;
continue;
}
++i;
}
if (lastPos === 0)
str = f;
else if (lastPos < f.length)
str += f.slice(lastPos);
while (a < arguments.length) {
const x = arguments[a++];
if (x === null || (typeof x !== 'object' && typeof x !== 'symbol')) {
if ((typeof x !== 'object' && typeof x !== 'symbol') || x === null) {
str += ` ${x}`;
} else {
str += ` ${inspect(x)}`;
Expand Down

0 comments on commit be98bf9

Please sign in to comment.