From 8d72b0d291e603c3b62b637a314a5ee35ba95fdc Mon Sep 17 00:00:00 2001 From: Evan Lucas Date: Mon, 22 Feb 2016 02:55:55 -0600 Subject: [PATCH 1/2] util: improve util.format performance By manually copying arguments and breaking the try/catch out, we are able to improve the performance of util.format by 20-100% (depending on the types). PR-URL: https://github.com/nodejs/node/pull/5360 Reviewed-By: James M Snell --- lib/util.js | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/lib/util.js b/lib/util.js index 1425cc6b504c15..a971c103d4ecd7 100644 --- a/lib/util.js +++ b/lib/util.js @@ -9,39 +9,48 @@ const isError = internalUtil.isError; var Debug; +function tryStringify(arg) { + try { + return JSON.stringify(arg); + } catch (_) { + return '[Circular]'; + } +} + const formatRegExp = /%[sdj%]/g; exports.format = function(f) { if (typeof f !== 'string') { - var objects = []; + const objects = new Array(arguments.length); for (var index = 0; index < arguments.length; index++) { - objects.push(inspect(arguments[index])); + objects[index] = inspect(arguments[index]); } return objects.join(' '); } if (arguments.length === 1) return f; - var i = 1; - var args = arguments; - var len = args.length; - var str = String(f).replace(formatRegExp, function(x) { + const len = arguments.length; + const args = new Array(len); + var i; + for (i = 0; i < len; i++) { + args[i] = arguments[i]; + } + + i = 1; + var str = f.replace(formatRegExp, function(x) { if (x === '%%') return '%'; if (i >= len) return x; switch (x) { case '%s': return String(args[i++]); case '%d': return Number(args[i++]); - case '%j': - try { - return JSON.stringify(args[i++]); - } catch (_) { - return '[Circular]'; - } + case '%j': return tryStringify(args[i++]); // falls through default: return x; } }); - for (var x = args[i]; i < len; x = args[++i]) { + while (i < len) { + const x = args[i++]; if (x === null || (typeof x !== 'object' && typeof x !== 'symbol')) { str += ' ' + x; } else { From c490b8ba54df6c730f7bfd02aaafe7c34f571f9e Mon Sep 17 00:00:00 2001 From: Brian White Date: Mon, 22 Feb 2016 20:47:20 -0500 Subject: [PATCH 2/2] util: improve format() performance further Replacing the regexp and replace function with a loop improves performance by ~60-200%. PR-URL: https://github.com/nodejs/node/pull/5360 Reviewed-By: James M Snell --- lib/util.js | 73 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 24 deletions(-) diff --git a/lib/util.js b/lib/util.js index a971c103d4ecd7..42661608ddf7f5 100644 --- a/lib/util.js +++ b/lib/util.js @@ -17,7 +17,6 @@ function tryStringify(arg) { } } -const formatRegExp = /%[sdj%]/g; exports.format = function(f) { if (typeof f !== 'string') { const objects = new Array(arguments.length); @@ -27,30 +26,56 @@ exports.format = function(f) { return objects.join(' '); } - if (arguments.length === 1) return f; - - const len = arguments.length; - const args = new Array(len); - var i; - for (i = 0; i < len; i++) { - args[i] = arguments[i]; - } - - i = 1; - var str = f.replace(formatRegExp, function(x) { - if (x === '%%') return '%'; - if (i >= len) return x; - switch (x) { - case '%s': return String(args[i++]); - case '%d': return Number(args[i++]); - case '%j': return tryStringify(args[i++]); - // falls through - default: - return x; + var argLen = arguments.length; + + if (argLen === 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) { + switch (f.charCodeAt(i + 1)) { + case 100: // 'd' + if (a >= argLen) + break; + if (lastPos < i) + str += f.slice(lastPos, i); + str += Number(arguments[a++]); + lastPos = i = i + 2; + continue; + case 106: // 'j' + if (a >= argLen) + break; + if (lastPos < i) + str += f.slice(lastPos, i); + str += tryStringify(arguments[a++]); + lastPos = i = i + 2; + continue; + case 115: // 's' + if (a >= argLen) + break; + if (lastPos < i) + str += f.slice(lastPos, i); + str += String(arguments[a++]); + lastPos = i = i + 2; + continue; + case 37: // '%' + if (lastPos < i) + str += f.slice(lastPos, i); + str += '%'; + lastPos = i = i + 2; + continue; + } } - }); - while (i < len) { - const x = args[i++]; + ++i; + } + if (lastPos === 0) + str = f; + else if (lastPos < f.length) + str += f.slice(lastPos); + while (a < argLen) { + const x = arguments[a++]; if (x === null || (typeof x !== 'object' && typeof x !== 'symbol')) { str += ' ' + x; } else {