Javascript: Is the length method efficient? - javascript

i'm doing some javascript coding and I was wondering if the length method is "precomputed", or remembered by the JS engine.
So, the question is:
If I'm checking really often for an array length, and supposing that i'm not changing it (making it immutable through a closure), should I precompute the length method and store it in some variable?
Thanks!

As always, the answer is "it depends".
Let's test native arrays with a million-element array:
for (var i = 0; i < arr.length; i++);
var len=arr.length;
for (var i = 0; i < len; i++);
http://josh3736.net/images/arrlen.png
Chrome and Firefox optimize the property accessor to be as efficient as copying the length to a local variable. IE and Opera do not, and are 50%+ slower.
However, keep in mind that the test results' "ops/second" means number of complete iterations through an array of one million elements per second.
To put this in perspective, even in IE8 (the worst performer in this bunch)—which scored .44 and 3.9 on property access and local variable (respectively)—the per-iteration penalty was a scant 2 µs. Iterating over a thousand items, using array.length will only cost you an extra 2 ms. In other words: beware premature optimization.

The length of an actual array is not computed on the fly. It's stored as part of the array data structure so accessing it involves no more work than just fetching the value (there is no computation). As such, it will generally be as fast as retrieving any fixed property of an object. As you can see in this performance test, there is basically no difference between retrieving the length of an array and retrieving a property of an object:
http://jsperf.com/length-comparisons
An exception to this is the nodeList objects that the DOM returns from functions like getElementsByTagName() or getElementsByClassName(). In these, it is often much slower to access the length property. This is probably because these nodeList objects are not true javascript objects and there may be a bridge between Javascript and native code that must be crossed each time something is accessed from these objects. In this case, it would be a LOT faster (10-100x faster) to cache the length into a local variable rather than use it repeatedly in a loop off the nodeList. I've added that to the length-comparison and you can see how much slower it is.
In some browsers, it is meaningfully faster to put the length into a local variable and use it from there if you will be referring to it over and over again (like in a loop). Here's the performance graph from the above jsperf test:

All major interpreters provide efficient accessors for the lengths of native arrays, but not for array-like objects like NodeLists.
"Efficient looping in Javascript"
Test / Browser Firefox 2.0 Opera 9.1 Internet Explorer 6
Native For-Loop 155 (ms) 121 (ms) 160 (ms)
...
Improved Native While-Loop 120 (ms) 100 (ms) 110 (ms)
"Efficient JavaScript code" suggests
for( var i = 0; i < document.getElementsByTagName('tr').length; i++ ) {
document.getElementsByTagName('tr')[i].className = 'newclass';
document.getElementsByTagName('tr')[i].style.color = 'red';
...
}
var rows = document.getElementsByTagName('tr');
for( var i = 0; i < rows.length; i++ ) {
rows[i].className = 'newclass';
rows[i].style.color = 'red';
...
}
Neither of these are efficient. getElementsByTagName returns a dynamic object, not a static array. Every time the loop condition is checked, Opera has to reassess the object, and work out how many elements it references, in order to work out the length property. This takes a little more time than checking against a static number.

There's probably a modest speed boost attainable by caching the length in a local variable due to attribute lookup speed. This may or may not be negligible, depending on how the JS engine JITs the code.
See http://jsperf.com/for-loop-caching for a rudimentary JSperf testcase.

For any collection-type object whose length you will not be manipulating (e.g. any immutable collection), it's always a good idea to cache its length for better performance.
var elems = document.getElementsByName("tst");
var elemsLen = elems.length;
var i;
for(i = 0; i < elemsLen; ++i)
{
// work with elems... example:
// elems[i].selected = false;
}
elems = [10,20,30,40,50,60,70,80,90,100];
elemsLen = elems.length;
for(i = 0; i < elemsLen; ++i)
{
// work with elems... example:
// elems[i] = elems[i] / 10;
}

Related

JS array length - when is it updated, when is it calculated? [duplicate]

What is the time complexity of a call to array.length in JavaScript? I think it would constant since it seems that property is set automatically on all arrays and you're just looking it up?
I think it would be constant since it seems that property is set automatically on all arrays and you're just looking it up?
Right. It's a property which is stored (not calculated) and automatically updated as necessary. The specification is explicit about that here and here amongst other places.
In theory, a JavaScript engine would be free to calculate length on access as though it were an accessor property as long as you couldn't tell (which would mean it couldn't literally be an accessor property, because you can detect that in code), but given that length is used repeatedly a lot (for (let n = 0; n < array.length; ++n) springs to mind), I think we can assume that all JavaScript engines in widespread use do what the spec says or at least something that's constant time access.
Just FWIW: Remember that JavaScript's standard arrays are, in theory, just objects with special behavior. And in theory, JavaScript objects are property bags. So looking up a property in a property bag could, in theory, depend on how many other properties are there, if the object is implemented as some kind of name->value hashmap (and they used to be, back in the bad old days). Modern engines optimize objects (Chrome's V8 famously creates dynamic classes on the fly and compiles them), but operations on those objects can still change property lookup performance. Adding a property can cause V8 to create a subclass, for instance. Deleting a property (actually using delete) can make V8 throw up its hands and fall back into "dictionary mode," which substantially degrades property access on the object.
In other words: It may vary, engine to engine, even object to object. But if you use arrays purely as arrays (not storing other non-array properties on them), odds are you'll get constant-time lookup.
It doesn't seem like a bottleneck but if you want to be sure use var len = arr.length and check that. It doesn't hurt and seems to be a tad faster on my machine albeit not a significant difference.
var arr = [];
for (var i = 0; i < 1000000; i++) {
arr[i] = Math.random();
}
var start = new Date();
for (var i = 0; i < arr.length; i++) {
arr[i] = Math.random();
}
var time1 = new Date() - start;
var start = new Date();
for (var i = 0, len = arr.length; i < len; i++) {
arr[i] = Math.random();
}
var time2 = new Date() - start;
document.getElementById("output").innerHTML = ".length: " + time1 + "<br/>\nvar len: " + time2;
<div id="output"></div>

Performance of array includes vs mapping to an Object and accessing it in JavaScript

According to the fundamentals of CS
the search functionality of an unsorted list has to occur in O(n) time where as direct access into an array will occur in O(1) time for HashMaps.
So is it more performant to map an array into a dictionary and then access the element directly or should I just use includes? This question is specifically for JavaScript because I believe this would come down to core implementation details of how includes() and {} is implemented.
let y = [1,2,3,4,5]
y.includes(3)
or...
let y = {
1: true,
2: true
3: true
4: true
5: true
}
5 in y
It's true that object lookup occurs in constant time - O(1) - so using object properties instead of an array is one option, but if you're just trying to check whether a value is included in a collection, it would be more appropriate to use a Set, which is a (generally unordered) collection of values, which can also be looked up in linear time. (Using a plain object instead would require you to have values in addition to your keys, which you don't care about - so, use a Set instead.)
const set = new Set(['foo', 'bar']);
console.log(set.has('foo'));
console.log(set.has('baz'));
This will be useful when you have to look up multiple values for the same Set. But, adding items to the Set (just like adding properties to an object) is O(N), so if you're just going to look up a single value, once, there's no benefit to this nor the object technique, and you may as well just use an array includes test.
Updated 04/29/2020
As the commenter rightly pointed out it would seem V8 was optimizing out the array includes calls. An updated version that assigns to a var and uses it produces more expected results. In that case Object address is fastest, followed by Set has and in a distant third is Array includes (on my system / browser).
All the same, I do stand by my original point, that if making micro-optimizations it is worth testing assumptions. Just make sure your tests are valid ;)
Original
Well. Despite the obvious expectation that Object address and Set has should outperform Array includes, benchmarks against Chrome indicate that implementation trumps expectation.
In the benches I ran against Chrome Array includes was far and away the best performer.
I also tested locally with Node and got more expected results. In that Object address wins, followed closely by Set has, then Array includes was marginally slower than both.
Bottom line is, if you're making micro-optimizations (not recommending that) it's worth benchmarking rather than assuming which might be best for your particular case. Ultimately it comes down to the implementation, as your question implies. So optimizing for the target platform is key.
Here's the results I got:
Node (12.6.0):
ops for Object address 7804199
ops for Array includes 5200197
ops for Set has 7178483
Chrome (75.0):
https://jsbench.me/myjyq4ixs1/1
This isn't necessarily a direct answer to the question but here is a related performance test I ran real quick in my chrome dev tools
function getRandomInt(max) {
return Math.floor(Math.random() * max);
}
var arr = [1,2,3];
var t = performance.now();
for (var i = 0; i < 100000; i++) {
var x = arr.includes(getRandomInt(3));
}
console.log(performance.now() - t);
var t = performance.now();
for (var i = 0; i < 100000; i++) {
var n = getRandomInt(3);
var x = n == 1 || n == 2 || n == 3;
}
console.log(performance.now() - t);
VM44:9 9.100000001490116
VM44:16 5.699999995529652
I find the array includes syntax nice to look at, so I wanted to know if the performance was likely to be an issue the way I use it, for checking if a variable is one of a set of enums for instance. It doesn't seem to be much of an impact for situations like this with a short list. Then I ran this.
function getRandomInt(max) {
return Math.floor(Math.random() * max);
}
var t = performance.now();
for (var i = 0; i < 100000; i++) {
var x = [1,2,3].includes(getRandomInt(3));
}
console.log(performance.now() - t);
var t = performance.now();
for (var i = 0; i < 100000; i++) {
var n = getRandomInt(3);
var x = n == 1 || n == 2 || n == 3;
}
console.log(performance.now() - t);
VM83:8 12.600000001490116
VM83:15 4.399999998509884
and so the way I actually use it and like lookin at it is quite worse with performance, despite still not being very significant unless run a few million times, so using it inside of an Array.filter that may run a lot as a react redux selector may not be a great idea like I was about to do when I decided to test this.

Remove element of array without splice()

I'm developing a JavaScript game and I want to keep my memory usage as low as possible.
Therefore I set some objects to null again, so they can get garbage collected.
I read an article which recommends avoiding functions like Array.splice(), because this creates a new array, which allocates new memory.
So I've implemented a JSFiddle with an own function, that deletes an element at a specific index and shifts all elements behind, so the length will be set to length -= 1. This only affects the existing array instead of creating a new one:
Function to use instead of splice:
deleteElem = function(arr, el) {
var index = arr.indexOf(el);
if (index > -1) {
var len = arr.length - 1;
for (var i = index; i < len; i++) {
arr[i] = arr[i + 1];
}
arr.length = len;
}
}
The JSFiddle for my function is sometimes faster, sometimes slower...
Should I pay more attention to better performance and worse memory, or better memory and worse performance?
What other ways exist to avoid using Array.splice?
You need to realize how jsperf runs your code. It doesn't run setup for each time it runs your code - the code runs hundreds or thousands of times for each setup.
That means you are using an empty array for 99.999999% of the calls and thus not measuring anything useful.
You could at least get some sense out of it by measuring it like this http://jsperf.com/splice-vs-own-function/2 however you need to note that the allocation of array of 50 times might blunt the differences and that your method is therefore actually much faster than the benchmark can show.

Do loops check the array.length every time when comparing i against array.length?

I was browsing around and I found this:
var i, len;
for(i = 0, len = array.length; i < len; i++) {
//...
}
My first thoughts are:
Why he did that? (it must be better for some reason)
Is it worth it? (I assume yes, why else he will do it this way?)
Do normal loops (the ones that don't cache the length) check the array.length each time?
A loop consisting of three parts is executed as follows:
for (A; B; C)
A - Executed before the enumeration
B - condition to test
C - expression after each enumeration (so, not if B evaluated to false)
So, yes: The .length property of an array is checked at each enumeration if it's constructed as for(var i=0; i<array.length; i++). For micro-optimisation, it's efficient to store the length of an array in a temporary variable (see also: What's the fastest way to loop through an array in JavaScript?).
Equivalent to for (var i=0; i<array.length; i++) { ... }:
var i = 0;
while (i < array.length) {
...
i++;
}
Is it worth it? (obviously yes, why else he will do it this way?)
Absolutely yes. Because, as you say, loop will calculate array length each time. So this will cause an enormous overhead. Run the following code snippets in your firebug or chrome dev tool vs.
// create an array with 50.000 items
(function(){
window.items = [];
for (var i = 0; i < 50000; i++) {
items.push(i);
}
})();
// a profiler function that will return given function's execution time in milliseconds
var getExecutionTime = function(fn) {
var start = new Date().getTime();
fn();
var end = new Date().getTime();
console.log(end - start);
}
var optimized = function() {
var newItems = [];
for (var i = 0, len = items.length; i < len; i++) {
newItems.push(items[i]);
}
};
var unOptimized = function() {
var newItems= [];
for (var i = 0; i < items.length; i++) {
newItems.push(items[i]);
}
};
getExecutionTime(optimized);
getExecutionTime(unOptimized);
Here is the approximate results in various browsers
Browser optimized unOptimized
Firefox 14 26
Chrome 15 32
IE9 22 40
IE8 82 157
IE7 76 148
So consider it again, and use optimized way :)
Note: I tried to work this code on jsPerf but I couldn't access jsPerf now. I guess, it is down when I tried.
I always thought in JavaScript length was just a property of the array object, pre-calculated by previous array operations - creation, addition, removal - or overridden by the user, so you're just looking up a variable anyway? I must admit I had just assumed that because of the lack of parenthesis, but looking at the MDN page for array.length, it seems to say the same thing.
In languages where length is a method or length is calculated by a standard library function, then you should pre-calculate the length before running the loop so The array isn't calculated every iteration, particularly for large datasets. Even then, in modern high level languages like Python, len() just returns the length property of the array object anyway.
So unless I'm mistaken, the complexity is just O(1), and from that standpoint, even if the variable were slightly faster than a property to lookup each pass, it wouldn't be worth the potential trouble of creating/reusing additional variables outside of the protective for loop scope.
However, I suspect that in this case the reason the example's programmer chose this approach is simply just a habit they picked up in another language and carried forwards JavaScript.
One reason to do this is say, if you're adding elements to the array during the loop but do not want to iterate over them. Say you want to turn [1, 2, 3] into [1, 2, 3, 1, 2, 3]. You could to that with:
var initialLength = items.length;
for(var i=0; i<initialLength; ++i) {
items.push(items[i]);
}
If you don't save the length before the loop, then array.length will keep increasing and the loop will run until the browser crashes / kills it.
Other than that, as the others said, it mildly affects performance. I wouldn't make a habit out of doing this because "premature optimization is the root of all evil". Plus, if you change the size of the array during the loop, doing this could break your code. For instance, if you remove elements from the array during the loop but keep comparing i to the previous array size, then the loop will try to access elements beyond the new size.
Yes
Its check the array.length every time, but if we don't want to run our loop for increased array then, in that case, we can use forEach method.
forEach is a javascript in build method which is does like for loop, but forEach only iterative the elements in an array which are before the loop start.
Let's understand this from below snippet:
array = [1,2,3,4]
for(i=0;i<=array.length;i++){
console.log(array[i]);
array.push(array[i]+1);
}
output like
1
2
3
4
5
6
...so on (infinite) while its checking array.length each time
let's check with forEach method
array = [1,2,3,4]
array.forEach((element) => {
console.log(element);
array.push(element+1);
})
console.log("array elements after loop",array);
it only processes for 4 elements which are present in array before iteration start.
**but in forEach case, it will affect on array.length if we pop out the element from array.
lets see this by an example:
array = [1,2,3,4]
array.forEach((element) => {
console.log(element);
array.pop()
})
console.log("array elements after loop",array);
Here are a number of performance tests for different approaches
http://www.websiteoptimization.com/speed/10/

Optimise a simple double for loop for IE

array.sort(function(left, right) {
return index(otherArray, left) < index(otherArray, right);
});
This is O(len(array) ^ 2) so for a reasonable size array of len = 1000 this takes constant * 1 million operations which easily overshoots the IE 5 million operators cap.
Thus IE throws a script is taking too long even though this is fast.
The problem is that IE does not have it's own Array.prototype.indexOf so I can't reduce the operation count down to O(len(array) and rely instead end up using a double for loop instead of a single for loop.
I considered array.join and using String.prototype.indexOf but the objects in the arrays are DOM elements and you can't convert them to a string (easily).
Telling IE users to remove this default cap is not an option.
I can think of two possible solutions to this problem: one of which will work everywhere, the other which is entirely IE-proprietary (and I expect doesn't work in IE9, but that supports Array.prototype.indexOf, so that's a non-issue).
The first, simpler, solution is to just set a property on each HTMLElement of the desired order and sort by that. If you care about the desired order persisting, you'll have to make sure the HTMLElement objects don't get garbage collected, so you'll have to keep references to them around (it's probably simplest to just create an array in the global scope for it).
The IE-only solution is to do something similar to what #maclema was proposing, using a lookup object, and HTMLElement.uniqueID:
var otherArrayLookup = {};
for (var i=0; i < otherArray.length; i++) {
otherArrayLookup[otherArray[i].uniqueID] = i;
}
array.sort(function(left, right) {
return otherArrayLookup[left.uniqueID] < otherArrayLookup[right.uniqueID];
});
You'll want to add some branches in there (don't put any within the callback function, but use different callback functions) for the Array.prototype.indexOf supported case, the HTMLElement.uniqueID supported case, and the none-of-the-above case.
You could try making an index lookup object. This should greatly increase performance too.
var otherArrayLookup = {};
for ( var i=0; i<otherArray.length; i++ ) {
otherArrayLookup[otherArray[i]] = i;
}
array.sort(function(left, right) {
return otherArrayLookup[left] < otherArrayLookup[right];
});

Categories

Resources