exebook
exebook

Reputation: 33900

Why str.charAt(i) is 1.6 times faster than str[i] in Node.js

var s = '', ok = ' h dfb  ds84 78sgf  ydf hjb////**', lc = 0, cc = 0

for (var i = 0; i < 300000; i++) {
    s += ok[Math.floor(Math.random() * ok.length)]
}

console.time('[]')
for (var i = 0; i < s.length; i++) {
    if (s[i] == '/' && s[i+1] == '/') lc++, i++
    if (s[i] == '/' && s[i+1] == '*') cc++, i++
}
console.timeEnd('[]')
/*
console.time('charAt')
for (var i = 0; i < s.length; i++) {
    if (s.charAt(i) == '/' && s.charAt(i+1) == '/') lc++, i++
    if (s.charAt(i) == '/' && s.charAt(i+1) == '*') cc++, i++
}
console.timeEnd('charAt')
*/
console.time('regex')
var rlc = (s.match(/\/\//g) || []).length;
var rcc = (s.match(/\/\*/g) || []).length;
console.timeEnd('regex')

console.log(lc, cc, rlc==lc, rcc==cc)

Why str.charAt() is consistently showing about 1.6 performance improvement over str[]? Aren't they supposed to do the same thing the same way? Does [] do some extra checks or conversions that affect speed? Or is it rather about my code?

Furthermore why str.charCodeAt(i) == 42 is another 10% faster than str.charAt(i) == '/'? From C/C++ point of view it makes no sense at all.

UPDATE: I put whole test code here.

UPDATE2: I must say that this is observed using Node.js ver 0.11.4

http://jsperf.com/brackets-vs-charat3 This test case shows lesser difference. charAt() is slower 6% in chrome 37 and 1% faster in Firefox 33. All Ubuntu 64.

UPDATE 3, since this seems to be Node.js issue I add node.js tag

Upvotes: 4

Views: 1353

Answers (2)

Bergi
Bergi

Reputation: 664297

Aren't they supposed to do the same thing the same way?

No. Apart from obviously different ways, they don't do the same things. Check the spec for charAt and for [] on strings.

Does [] do some extra checks or conversions that affect speed?

Apparently. The differences are that charAt needs to cast its operand to a string, which [] doesn't need to do. Furthermore, charAt will return the empty string for out-of-bounds accesses, while [] will return undefined. And mostly, [] needs to check whether the given property name is really an integer, and whether there is an actual property with that name on the string object.

Or is it rather about my code?

Your code looks fine.

Furthermore why str.charCodeAt(i) == 42 is another 10% faster than str.charAt(i) == '/'? From C/C++ point of view it makes no sense at all.

Notice that JavaScript does not have a char datatype. '/' is a string of length 1. It seems this is not optimized well (or: easily), and an integer1 comparison is just faster than a string comparison.

1: Most numbers, though "per spec" being 64-bit floats, are represented as 31-bit integers in V8.

Upvotes: 4

deitch
deitch

Reputation: 14581

Quote from Crockford's "JavaScript: the Good Parts", opening paragraphs of Chap 6, Arrays:

... Arrays can be very fast data structures. Unfortunately, JavaScript does not have anything like this kind of array. Instead, JavaScript provides an object that has some array-like characteristics. It converts array subscripts into strings that are used to make properties. It is significantly slower than a real array...

So whereas in C or Java arr[2] is a direct pointer to a memory location and thus should have very fast performance, in JS, arr[2] really means, "convert the number 2 into the string '2', then hash it to use as a lookup in an object."

Hashed lookups are still pretty fast, but not as fast as true arrays. Add to it that the number needs to be converted to a string and then hashed, and you see the performance hit.

On the other hand, str.charAt(2) is, I believe, a native function and is probably doing true array-like lookup under the covers.

Upvotes: -2

Related Questions