dom referenceing vs storing dom reference [duplicate] - javascript

This question already has answers here:
performance issue : storing a reference to DOM element vs using selectors
(4 answers)
Closed 8 years ago.
What is the difference between the following statements?
$("#txt").val("123");
and
var txt=$("#txt");
txt.val("123");
Which one is most efficient?

Both version go through the motion of finding a relevant dom element and setting a value
There is no benefit to doing one or the other unless you need to reference it again. Consider this:
if ( $("#txt").val()!='123' )
$("#txt").val("123");
// vs.
var txt=$("#txt");
if ( txt.val()!='123' )
txt.val("123");
The latter is more efficient because you are only looking for the element once.
But if you are simply setting it.. either way in your example is going to be the same amount of work. Slightly more overhead from setting a variable first, but nothing to shake a stick at...but even the above situation isn't likely worth shaking a stick at either, unless you enjoy nickel and diming fractions of microseconds.

In your code, if you need only once to target a desired element selector:
$("#txt").val("123"); // is just fine
But most of the time programmers need to reference multiple times to the same element, and to increase the overall application performance they store the selector into a variable memory slot:
var txt = $("#txt");
var input = $("#someInput");
input.on('input', function() {
txt.val( this.value ); // *
});
txt.addClass('selected'); // JS says "hey I know this guy!"
where at this point JS doesn't need to go all over the document looking for your #txt input on every keyup / keydown or other input events. Can you imagine the benefit?

If you're going to be operating on the value of #txt more than once, it is best to cache the selector as you did in the second version, if you're going to be using the object multiple times. In the end, you probably won't notice any considerable performance increases by using one method v. the other.
As for quantitative data:
versionOne(): 0.0359999998472631
versionTwo(): 0.02500000037252903
This test seems to suggest that the first one is slower, using window.performance. When running this, make sure to empty your browser cache.
(function() {
var v1 = [];
var v2 = [];
function avg(data) {
var sum = 0
for(var i = 0; i < data.length; i++) {
sum += data[i];
}
return sum / data.length;
}
function versionOne() {
$("#txt").val('hello');
}
function versionTwo() {
var txt=$("#txt");
txt.val("123");
}
for(var i = 0; i < 1000; i++) {
var start = window.performance.now();
versionOne();
var end = window.performance.now();
v1.push(end - start);
var start = window.performance.now();
versionTwo();
var end = window.performance.now();
v2.push(end - start);
}
console.log("versionOne(): " + avg(v1));
console.log("versionTwo(): " + avg(v2));
})();
Fiddle

Related

Setting variable to existing value versus return?

In Javascript, I have a function that sets a variable. If the function tries to set the variable to its current value, is it more "efficient" to break out of the function, or let the function re-set the variable's value?
Example
var a;
function setStuff(x) {
if (a == x) { return; }
a = x;
}
versus
var a;
function setStuff(x) {
a = x;
}
This function will be called on page scroll, so it will be called at a high frequency.
I don't think the issue is "efficiency".
I do however think there's a practice at play here, which is to generally not manipulate values outside the scope of the function. Having many functions like these in your application will drive you nuts, wondering which function is changing what.
Instead return a new value.
var setStuff = function() {
return newValue;
}
var a = setStuff();
I wrote a simple test snippet:
var a;
function setStuffCheck(x) {
if (a == x) { return; }
a = x;
}
function setStuff(x) {
a = x;
}
function benchmark(func){
var startTime = Date.now();
var callCount = 1000000;
for(var i = 0; i < callCount; i++){
func(10);
}
console.log((Date.now() - startTime) + "ms for "+callCount+" calls setting always the same value");
startTime = Date.now();
for(var i = 0; i < callCount; i++){
func(i);
}
console.log((Date.now() - startTime) + "ms for "+callCount+" calls setting always different values");
}
benchmark(setStuffCheck);
benchmark(setStuff);
By copying and pasting it in the console (Firefox 46.0.1), I have something like this:
138ms for 1000000 calls setting always the same value
216ms for 1000000 calls setting always different values
77ms for 1000000 calls setting always the same value
78ms for 1000000 calls setting always different values
So the second way seems to be always better. But the results may be different on each computers. However, the difference is noticable only for 1 millions of calls (try changing it to 1000, there'll be no differences).
The second option makes more sense to me and is more viable as there is very less logic written as compared to the first one as it is checking whether two values are equal or not , whereas in the second option its just re-assigning the variable to a new value.
if condition is always slower compared to when just assigning a new value . So i think you should go with second option.
There are potentially two factors that will likely drown out any practical performance difference between the two options. In practice, I would suggest that you use the version that is the easiest to understand and explain to others. I think that would probably be the unconditional update but it would be more up to you.
The two things that are likely going to obfuscate any real differences are:
What else are you doing in the function
What effect branch prediction has on your conditional.
Now, specifically to the question of what version is faster. I have set up the following test with each option executed a million times with 10 runs of each test. The global is set to itself 1 in 10 times but you can change that to some other frequency by setting aNew
var a = 10;
var ittr = 1000 * 1000;
function getRandomIntInclusive(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; }
function set1(x){
if (a === x){ return; }
a = x;
}
function set2(x){
a = x;
}
for (var j = 0; j < 10; j++){
var start = performance.now();
for (var i = 0; i< ittr; i++){
var aNew = a - 10 + getRandomIntInclusive(1,19);
set1(aNew);
}
console.log("conditional : " + (performance.now() - start));
}
for (var j = 0; j < 10; j++){
var start = performance.now();
for (var i = 0; i< ittr; i++){
var aNew = a - 10 + getRandomIntInclusive(1,19);
set2(aNew);
}
console.log("unconditional : " + (performance.now() - start));
}
Your results may vary but the I see conditional set() averages about 18 after settling down. The unconditional about the same, maybe 17.5.
Note that the vast bulk of the time here is taken by the call to random(). If you consistently just set the global to itself both functions time at around 1.8 rather than 18, suggesting that whatever else you might do in your set() is likely to obfuscate any performance difference.
The two do not have necessarily have identical results. Consider this series of calls:
var a;
function setStuff(x) {
if (a == x) { return; }
a = x;
}
setStuffCheck(0) ;
setStuffCheck('0');
console.log(a);
Output is not '0', but 0.
For a good comparison, the setStuffCheck function should use the strict equality operator ===.
In my tests in FireFox I see the performance of both functions show very little difference. setStuffCheck seems to take slightly more time to execute than setStuff when the argument has a different value than a, but it is the opposite (also slightly) when the values are the same. The difference in either way is in the order of 2%, which is the kind of fluctuations you get anyway on a typical device/PC for other causes, that have nothing to do with the code.
Anyway, this also means that this slight performance difference will depend on how often you expect to call the function with an argument that is equal to a.
However, the difference is only noticeable when you would do hundreds of millions of calls. If you don't have that many calls, then don't even bother and choose for setStuff.

How to you use recursion in javascript to create key value objects

I understand how to go about tasks using loops, recursion is kind of a mystery to me, but from what I understand in certain cases it can save a ton of time if looping through a lot of data.
I created the following function to loop through a large(ish) data set.
var quotes = require('./quotes.js');
//Pulls in the exported function from quotes.js
var exportedQuotes = quotes.allQuotes();
var allAuthors = exportedQuotes.author;
//Create an empty key value object, we use these to coerce unique values to an array
var uniqs = {};
//I create this object to hold all the authors and their quotes
var fullQuote = {};
//Create an object with only unique authors
for(var i = 0; i < allAuthors.length ; i++){
fullQuote[allAuthors[i]] = null;
}
//Coerce unique authors from javascript object into an array
var uniqAuthors = Object.keys(uniqs);
var quoteCount = exportedQuotes.author.length;
var iterativeSolution = function(){
for(var i = 0; i < Object.keys(fullQuote).length; i++){
for(var j = 0; j < exportedQuotes.author.length; j++){
//If the author in the unique list is equal to the author in the duplicate list
if(Object.keys(fullQuote)[i] == exportedQuotes.author[j]){
//if an author has not had a quote attributed to its name
if(fullQuote[exportedQuotes.author[j]] == null){
//assign the author an array with the current quote at the 0 index
fullQuote[exportedQuotes.author[j]] = [exportedQuotes.quote[j]]
} else {
//if an author already has a quote assigned to its name then just add the current quote to the authors quote list
fullQuote[exportedQuotes.author[j]].push(exportedQuotes.quote[j])
}
}
}
}
}
I don't currently have the skills to do analyze this, but, I'm wondering if there is a case for recursion to save the time it takes to get through all the loops. And if there is a case for recursion what does it look like for nested loops in javascript, specifically when creating key value objects recursively?
There may be a slight misunderstanding about what recursion is: recursion does not save time. It's just a different way of doing the same traversal. It generally a little easier to read, and depending on the problem, will map to certain algorithms better. However, one of the first things we do when we need to start optimizing code for speed is to remove recursion, turning them back into loops, and then even "unrolling" loops, making code much uglier, but fast, in the process. Recursion vs plain loops is almost always a matter of taste. One looks nicer, but that's hardly the only quality we should judge code on.
And also: just because it sounds like I'm advocating against using it, doesn't mean you shouldn't just try it: take that code, put it in a new file, rewrite that file so that it uses recursion. Doing so lets you compare your code. Which one is faster? Which is easier to read? Now you know something about how (your) code behaves, and you'll have learned something valuable.
Also don't sell yourself short: if you wrote this code, you know how it works, so you know how to analyze it enough to rewrite it.
Algorithms makes code fast or slow, not recursion. Some quite fast algorithms can use recursion, but that's a whole different story. Many algorithms can be written as both with recursion, and without recursion.
However, your code has a big problem. Notice how many times you call this code?
Object.keys(fullQuote)
You are re-computing the value of that many many times in your code. Don't do that. Just call it once and store in a variable, like the following:
var uniqAuthors = Object.keys(uniqs);
var uniqFullQuote = Object.keys(fullQuote);
var quoteCount = exportedQuotes.author.length;
//Loop through all quotes and assign all quotes to a unique author::Each author has many quotes
for(var i = 0; i < uniqFullQuote.length; i++){
for(var j = 0; j < exportedQuotes.author.length; j++){
//If the author in the unique list is equal to the author in the duplicate list
if(uniqFullQuote[i] == exportedQuotes.author[j]){
//if an author has not had a quote attributed to its name
if(fullQuote[exportedQuotes.author[j]] == null){
//assign the author an array with the current quote at the 0 index
fullQuote[exportedQuotes.author[j]] = [exportedQuotes.quote[j]]
} else {
//if an author already has a quote assigned to its name then just add the current quote to the authors quote list
fullQuote[exportedQuotes.author[j]].push(exportedQuotes.quote[j])
}
}
}
}
You don't have to iterate Object.keys(fullQuote).
var quotes = require('./quotes.js'),
exportedQuotes = quotes.allQuotes(),
allAuthors = exportedQuotes.author,
fullQuote = Object.create(null);
for(var i=0; i < allAuthors.length; ++i)
(fullQuote[allAuthors[i]] = fullQuote[allAuthors[i]] || [])
.push(exportedQuotes.quote[i])
I don't recommend recursion. It won't improve the asymptotic cost, and in JS calling functions is a bit expensive.
I got really curious and created a recursive solution just to see how it works. Then timed it, my iterative solution took 53 seconds to run, while my recursive solution took 1 millisecond to run. The iterative approach can obviously be tweaked based on the answers provided below, to run faster, but a recursive approach forced me to think in a "leaner" manner when creating my function.
var exportedQuotes = quotes.allQuotes();
var allAuthors = exportedQuotes.author;
var n = allAuthors.length
var fullQuote = {};
var recursiveSolution = function(arrayLength) {
//base case
if(arrayLength <= 1){
if(fullQuote[exportedQuotes.author[0]] == null){
fullQuote[exportedQuotes.author[0]] = [exportedQuotes.quote[0]];
}else{
fullQuote[exportedQuotes.author[0]].push(exportedQuotes.quote[0])
}
return;
};
//recursive step
if(fullQuote[exportedQuotes.author[arrayLength]] == null){
fullQuote[exportedQuotes.author[arrayLength]] = [exportedQuotes.quote[arrayLength]];
}else{
fullQuote[exportedQuotes.author[arrayLength]].push(exportedQuotes.quote[arrayLength])
}
newLength = arrayLength - 1;
return recursiveSolution(newLength);
}
////////Timing functions
var timeIteration = function(){
console.time(iterativeSolution);
iterativeSolution(); // run whatever needs to be timed in between the statements
return console.timeEnd(iterativeSolution);
}
var timeRecursive = function(){
console.time(recursiveSolution(n));
recursiveSolution(n); // run whatever needs to be timed in between the statements
return console.timeEnd(recursiveSolution(n));
}

Is this as fast as JS array copy can get without loop unrolling?

This is related to this question.
I have heard that the while pattern with a decrement and a greater than test is faster than any other loop pattern. Given that, is this the fastest possible array copy in js?
function arrayCopy(src,sstart,dst,dstart,length) {
length += sstart;
dstart += length;
while(--length >= sstart) {
dst[--dstart] = src[length];
}
}
Other test functions
function slowCopy(src,sstart,dst,dstart,length) {
for(var i = sstart; i < sstart+length;i+=1 ) {
dst[dstart++] = src[i];
}
}
function aCopy(src,sstart,dst,dstart,length) {
Array.prototype.splice.apply(dst,[dstart, length].concat(src.slice(sstart,sstart+length)));
}
Test Results http://jsperf.com/fastest-js-arraycopy
arrayCopy -
2,899
±5.27%
fastest
slowCopy - WINNER
2,977
±4.86%
fastest
aCopy -
2,810
±4.61%
fastest
I want to add some more of the suggested functions below to the jsPerf tests but none of them incorporate source start offset, destination start offset or length of copy. Anyway, I was somewhat surprised by these results which appear to be the opposite of what I expect
Who says you need a loop?
var myArrayCopy = JSON.parse(JSON.stringify(myArray));
This method makes a deep clone of the array. Here it is in a function:
function arrayCopy(src,sstart,dst,dstart,length) {
dst = JSON.parse(JSON.stringify(src));
}
Keep in mind the other variables (besides src and dst) are there just to maintain your original code structure in case you have pre-existing calls to this function. They won't be used and can be removed.
Slow Copy is, surprisingly, the winner. By a narrow margin:
function slowCopy(src,sstart,dst,dstart,length) {
for(var i = sstart; i < sstart+length;i+=1 ) {
dst[dstart++] = src[i];
}
}
I think this is the fastest way:
var original = [1, 2, 3];
var copy = original.slice(0);

alternatives for excessive for() looping in javascript

Situation
I'm currently writing a javascript widget that displays a random quote into a html element. the quotes are stored in a javascript array as well as how many times they've been displayed into the html element. A quote to be displayed cannot be the same quote as was previously displayed. Furthermore the chance for a quote to be selected is based on it's previous occurences in the html element. ( less occurrences should result in a higher chance compared to the other quotes to be selected for display.
Current solution
I've currently made it work ( with my severely lacking javascript knowledge ) by using a lot of looping through various arrays. while this currently works ( !! ) I find this solution rather expensive for what I want to achieve.
What I'm looking for
Alternative methods of removing an array element from an array, currently looping through the entire array to find the element I want removed and copy all other elements into a new array
Alternative method of calculating and selecting a element from an array based on it's occurence
Anything else you notice I should / could do different while still enforcing the stated business rules under Situation
The Code
var quoteElement = $("div#Quotes > q"),
quotes = [[" AAAAAAAAAAAA ", 1],
[" BBBBBBBBBBBB ", 1],
[" CCCCCCCCCCCC ", 1],
[" DDDDDDDDDDDD ", 1]],
fadeTimer = 600,
displayNewQuote = function () {
var currentQuote = quoteElement.text();
var eligibleQuotes = new Array();
var exclusionFound = false;
for (var i = 0; i < quotes.length; i++) {
var iteratedQuote = quotes[i];
if (exclusionFound === false) {
if (currentQuote == iteratedQuote[0].toString())
exclusionFound = true;
else
eligibleQuotes.push(iteratedQuote);
} else
eligibleQuotes.push(iteratedQuote);
}
eligibleQuotes.sort( function (current, next) {
return current[1] - next[1];
} );
var calculatePoint = eligibleQuotes[0][1];
var occurenceRelation = new Array();
var relationSum = 0;
for (var i = 0; i < eligibleQuotes.length; i++) {
if (i == 0)
occurenceRelation[i] = 1 / ((calculatePoint / calculatePoint) + (calculatePoint / eligibleQuotes[i+1][1]));
else
occurenceRelation[i] = occurenceRelation[0] * (calculatePoint / eligibleQuotes[i][1]);
relationSum = relationSum + (occurenceRelation[i] * 100);
}
var generatedNumber = Math.floor(relationSum * Math.random());
var newQuote;
for (var i = 0; i < occurenceRelation.length; i++) {
if (occurenceRelation[i] <= generatedNumber) {
newQuote = eligibleQuotes[i][0].toString();
i = occurenceRelation.length;
}
}
for (var i = 0; i < quotes.length; i++) {
var iteratedQuote = quotes[i][0].toString();
if (iteratedQuote == newQuote) {
quotes[i][1]++;
i = quotes.length;
}
}
quoteElement.stop(true, true)
.fadeOut(fadeTimer);
setTimeout( function () {
quoteElement.html(newQuote)
.fadeIn(fadeTimer);
}, fadeTimer);
}
if (quotes.length > 1)
setInterval(displayNewQuote, 10000);
Alternatives considered
Always chose the array element with the lowest occurence.
Decided against this as this would / could possibly reveal a too obvious pattern in the animation
combine several for loops to reduce the workload
Decided against this as this would make the code to esoteric, I'd probably wouldn't understand the code anymore next week
jsFiddle reference
http://jsfiddle.net/P5rk3/
Update
Rewrote my function with the techniques mentioned, while I fear that these techniques still loop through the entire array to find it's requirements, at least my code looks cleaner : )
References used after reading the answers here:
http://www.tutorialspoint.com/javascript/array_map.htm
http://www.tutorialspoint.com/javascript/array_filter.htm
http://api.jquery.com/jQuery.each/
I suggest array functions that are mostly supported (and easily added if not):
[].splice(index, howManyToDelete); // you can alternatively add extra parameters to slot into the place of deletion
[].indexOf(elementToSearchFor);
[].filter(function(){});
Other useful functions include forEach and map.
I agree that combining all the work into one giant loop is ugly (and not always possible), and you gain little by doing it, so readability is definitely the winner. Although you shouldn't need too many loops with these array functions.
The answer that you want:
Create an integer array that stores the number of uses of every quote. Also, a global variable Tot with the total number of quotes already used (i.e., the sum of that integer array). Find also Mean, as Tot / number of quotes.
Chose a random number between 0 and Tot - 1.
For each quote, add Mean * 2 - the number of uses(*1). When you get that that value has exceeded the random number generated, select that quote.
In case that quote is the one currently displayed, either select the next or the previous quote or just repeat the process.
The real answer:
Use a random quote, at the very maximum repeat if the quote is duplicated. The data usages are going to be lost when the user reloads/leaves the page. And, no matter how cleverly have you chosen them, most users do not care.
(*1) Check for limits, i.e. that the first or last quota will be eligible with this formula.
Alternative methods of removing an array element from an array
With ES5's Array.filter() method:
Array.prototype.without = function(v) {
return this.filter(function(x) {
return v !== x;
});
};
given an array a, a.without(v) will return a copy of a without the element v in it.
less occurrences should result in a higher chance compared to the other quotes to be selected for display
You shouldn't mess with chance - as my mathematician other-half says, "chance doesn't have a memory".
What you're suggesting is akin to the idea that numbers in the lottery that haven't come up yet must be "overdue" and therefore more likely to appear. It simply isn't true.
You can write functions that explicitly define what you're trying to do with the loop.
Your first loop is a filter.
Your second loop is a map + some side effect.
I don't know about the other loops, they're weird :P
A filter is something like:
function filter(array, condition) {
var i = 0, new_array = [];
for (; i < array.length; i += 1) {
if (condition(array[i], i)) {
new_array.push(array[i]);
}
}
return new_array;
}
var numbers = [1,2,3,4,5,6,7,8,9];
var even_numbers = filter(numbers, function (number, index) {
return number % 2 === 0;
});
alert(even_numbers); // [2,4,6,8]
You can't avoid the loop, but you can add more semantics to the code by making a function that explains what you're doing.
If, for some reason, you are not comfortable with splice or filter methods, there is a nice (outdated, but still working) method by John Resig: http://ejohn.org/blog/javascript-array-remove/

Alternatives to javascript function-based iteration (e.g. jQuery.each())

I've been watching Google Tech Talks' Speed Up Your Javascript and in talking about loops, the speaker mentions to stay away from function-based iterations such as jQuery.each() (among others, at about 24:05 in the video). He briefly explains why to avoid them which makes sense, but admittedly I don't quite understand what an alternative would be. Say, in the case I want to iterate through a column of table cells and use the value to manipulate the adjacent cell's value (just a quick example). Can anyone explain and give an example of an alternative to function-based iteration?
Just a simple for loop should be quicker if you need to loop.
var l = collection.length;
for (var i = 0; i<l; i++) {
//do stuff
}
But, just because it's quicker doesn't mean it's always important that it is so.
This runs at the client, not the server, so you don't need to worry about scaling with the number of users, and if it's quick with a .each(), then leave it. But, if that's slow, a for loop could speed it up.
Ye olde for-loop
It seems to me that it would be case that function-based iteration would be slightly slower because of the 1) the overhead of function itself, 2) the overhead of the callback function being created and executed N times, and 3) the extra depth in the scope chain. However, I thought I'd do a quick benchmark just for kicks. Turns out, at least in my simple test-case, that function-based iteration was faster. Here's the code and the findings
Test benchmark code
// Function based iteration method
var forEach = function(_a, callback) {
for ( var _i=0; _i<_a.length; _i++ ) {
callback(_a[_i], _i);
}
}
// Generate a big ass array with numbers 0..N
var a = [], LENGTH = 1024 * 10;
for ( var i=0; i<LENGTH; i++ ) { a.push(i); }
console.log("Array length: %d", LENGTH);
// Test 1: function-based iteration
console.info("function-base iteration");
var end1 = 0, start1 = new Date().getTime();
var sum1 = 0;
forEach(a, function(value, index) { sum1 += value; });
end1 = new Date().getTime();
console.log("Time: %sms; Sum: %d", end1 - start1, sum1);
// Test 2: normal for-loop iteration
console.info("Normal for-loop");
var end2 = 0, start2 = new Date().getTime();
var sum2 = 0;
for (var j=0; j<a.length; j++) { sum2 += a[j]; }
end2 = new Date().getTime();
console.log("Time: %sms; Sum: %d", end2 - start2, sum2);
Each test just sums the array which is simplistic, but something that can be realistically seen in some sort of real life scenario.
Results for FF 3.5
Array length: 10240
function-base iteration
Time: 9ms; Sum: 52423680
Normal for-loop
Time: 22ms; Sum: 52423680
Turns out that a basic for iteration was faster in this test case. I haven't watched the video yet, but I'll give it a look and see if he's differing somewhere that would make function-based iterations slower.
Edit: This is by no means the end-all, be-all and is only the results of one engine and one test-case. I fully expected the results to be the other way around (function-based iteration being slower), but it is interesting to see how certain browsers have made optimizations (which may or may not be specifically aimed at this style of JavaScript) so that the opposite is true.
The fastest possible way to iterate is to cut down on stuff you do within the loop. Bring stuff out of the iteration and minimise lookups/increments within the loop, e.g.
var i = arr.length;
while (i--) {
console.log("Item no "+i+" is "+arr[i]);
}
NB! By testing on the latest Safari (with WebKit nightly), Chrome and Firefox, you'll find that it really doesn't matter which kind of the loop you're choosing if it's not for each or for in (or even worse, any derived functions built upon them).
Also, what turns out, is that the following for loop is very slightly even faster than the above option:
var l = arr.length;
for (var i=l; i--;) {
console.log("Item no "+i+" is "+arr[i]);
}
If the order of looping doesn't matter, the following should be fastest as you only need a single local variable; also, decrementing the counter and bounds-checking are done with a single statement:
var i = foo.length;
if(i) do { // check for i != 0
// do stuff with `foo[i]`
} while(--i);
What I normally use is the following:
for(var i = foo.length; i--; ) {
// do stuff with `foo[i]`
}
It's potentially slower than the previous version (post- vs pre-decrement, for vs while), but more readable.

Categories