I wanted to extend String object prototype with some utility method. It worked, but the performance was surprisingly low. Passing a string to a function is 10x times faster than overriding the String.prototype
method that is doing the same thing. To make sure this really happens I created a very simple count()
function and the corresponding methods.
(I was experimenting, and created three different versions of the method.)
function count(str, char) {
var n = 0;
for (var i = 0; i < str.length; i++) if (str[i] == char) n++;
return n;
}
String.prototype.count = function (char) {
var n = 0;
for (var i = 0; i < this.length; i++) if (this[i] == char) n++;
return n;
}
String.prototype.count_reuse = function (char) {
return count(this, char)
}
String.prototype.count_var = function (char) {
var str = this;
var n = 0;
for (var i = 0; i < str.length; i++) if (str[i] == char) n++;
return n;
}
// Here is how I measued speed, using Node.js 6.1.0
var STR ='0110101110010110100111010011101010101111110001010110010101011101101010101010111111000';
var REP = 1e3//6;
console.time('func')
for (var i = 0; i < REP; i++) count(STR,'1')
console.timeEnd('func')
console.time('proto')
for (var i = 0; i < REP; i++) STR.count('1')
console.timeEnd('proto')
console.time('proto-reuse')
for (var i = 0; i < REP; i++) STR.count_reuse('1')
console.timeEnd('proto-reuse')
console.time('proto-var')
for (var i = 0; i < REP; i++) STR.count_var('1')
console.timeEnd('proto-var')
Results:
func: 705 ms
proto: 10011 ms
proto-reuse: 10366 ms
proto-var: 9703 ms
As you can see the difference is dramatic.
The below proves that performance of method calls is neglectably slower, and that the function code it self is slower for methods.
function count_dummy(str, char) {
return 1234;
}
String.prototype.count_dummy = function (char) {
return 1234; // Just to prove that accessing the method is not the bottle-neck.
}
console.time('func-dummy')
for (var i = 0; i < REP; i++) count_dummy(STR,'1')
console.timeEnd('func-dummy')
console.time('proto-dummy')
for (var i = 0; i < REP; i++) STR.count_dummy('1')
console.timeEnd('proto-dummy')
console.time('func-dummy')
for (var i = 0; i < REP; i++) count_dummy(STR,'1')
console.timeEnd('func-dummy')
Results:
func-dummy: 0.165ms
proto-dummy: 0.247ms
Although on huge repetitions (like 1e8) prototyped methods proves to be 10x times slower than functions, this can be ignored for this case.
All this may be related only to a String object, because simple generic objects perform about the same when you pass them to functions or call their methods:
var A = { count: 1234 };
function getCount(obj) { return obj.count }
A.getCount = function() { return this.count }
console.time('func')
for (var i = 0; i < 1e9; i++) getCount(A)
console.timeEnd('func')
console.time('method')
for (var i = 0; i < 1e9; i++) A.getCount()
console.timeEnd('method')
Results:
func: 1689.942ms
method: 1674.639ms
I've been searching on Stackoverflow and binging, but other that the recommendation "do not extend String or Array because you pollute the name space" (which is not a problem for my particular project), I cannot find anything related to performance of methods compared to functions. So should I simply forget about extending the String object due to performance drop of added methods or there is more about it?
This is most likely because you are not using strict mode, and the
this
value inside your method needs to be cast to aString
instance instead of being a primitive string.You can confirm this by repeating your measurement on
var STR = new String('01101011…')
.Then fix your implementation: