It's going to sound controversial to many, but writing you for loop like
for(var i = 0; i < a.length; i++) { // Do code stuff here }
is the only way you should be writing it. Tell your boss, your coworker, your professor. No other way matters and your time is better spent on learning other things. Knowing how to write a for loop in any other way than this is completely developer-preference and a style statement, nothing more.
this for loop is:
Every new programmer has a strong desire to write great code, and one aspect of great code is one that runs fast, right? Wrong.
Great code is easy to understand, is consistent, works well, and if it's fast then great!
If the first thing done when writing code is making in run fast, then it's being done wrong.
The argument could be made years and years ago that fast and efficient code is important, and memory was an expensive resource. Nowadays, memory is cheap and computers and mobile phones run blazingly fast. Having fast and efficient code isn't really important anymore unless you're working in data science or dealing with big data.
Also when I talk about writing code, I'm really referring to Javascript since that's where I spend most of my time these days. I'm sure there's parallels to other languages, but Javascript it seems has been infected by the brocoders and people looking to make a statement.
My experience and exposure to all sorts of Javascript applications has shown me programmers have gotten pretty good at trying different optimization methods to make looping over 20-30 items as fast as possible.
I highly doubt any of these provide any measurable performance advantage over another.
There's so many ways people have tried to optimize and provide hysterical rationalizations for the above.
for (var i = 0; i < rowData.length; i++) { for (var s, i = 1, n = arguments.length; i < n; i++) { for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i])); for (var s, i = 1, n = arguments.length; i < n; i++) { for (let i = 0; i < unshiftedKeys.length; i++) { for (let i = 0; i < address_components.length; i++) {
Some like to move the initial value declaration outside of the looping code to "save cycles".
for (i = 0; i < 5; i++) { for (i; i < checkboxesLen; i++) { for (i = 0; i < suggestionsLen; i++) { for (x = 0; aa[x] && bb[x]; x++) { for (i = 0; i < partialAddressFields.length; i++) { for (i; i < len; i += 1) {
There's the typical advice on not rereading the length property on each loop cycle.
for (var i = 0; i < locCount; i++) {
Also, the way the initial value is incremented could potentially make a difference?
for (var i = 0; i < locCount; i += 1) {
My favorite - don't include the initial value in the loop at all!
for (; i > -1; i -= 1) { for (; i < len; i += 1) {
So i decided to run my own performance test, first with some bogus academia test facility. Later on, I try to run some actual real world tests in React and render a page using some of these.
I created an array of about 1,000,000 elements and created a testrunner to process each for loop pattern 25 times. I would record the time it took each loop to complete and calculate the average time of 25 runs for each.
const data = [...Array(1000000).keys()] function doMaths (a) { var result = a + 100 result = result * 2000 result = Math.sqrt(result) result = result / 100 return result }
Using a Chrome browser (Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36), I ran each of the patterns through a testrunner I wrote and recorded the results.
The old reliable:
for(var i = 0; i < a.length; i++) { // Do code stuff here }
Averaging 25 runs: 1.2ms
Some over optimized Frankensteins-monster for loop I created that is heavily inspired by the plethora of different patterns above:
var i = 0 var len = data.length for (; i < len; i++) { doMaths(data[i]) }
Averaging 25 runs: 1.32ms
Every new programmers favorite "one-liner":
data.forEach(x => { doMaths(x) })
Averaging 25 runs: 10.96ms
Also Every new programmers favorite "one-liner":
function mapFn (x) { return doMaths(x) } data.map(mapFn)
Averaging 25 runs: 83.4ms
I saw a suggestion in some React programming site that .map
was slow due to having to redeclare the map function over and over on each iteration. Clearly, this was not true as I did both styles and saw no difference.
In my opinion, the only for loop you'll ever need (at least in Javascript) is the one you hopefully the simplest.
All of these variations are developer-preference and that's it. There is no difference how looping code is written, and if anything, the other patterns only make it worse for other developers and potentially cause performance problems due to over-optimization.
For some reason, programmers tend to focus on speed above all else, even when it is unwarranted. Think about correctness, then readability. If anything, readability is the thing that's suffering the most when using other patterns.
In the end, any tweak made is a micro-optimization at best. With all of the work being done on the speed of Javascript engines and modern CPUs (to include current smart phones) capable of millions of calculations per second, it's unlikely to be a measurable difference anymore. Perhaps somewhere in a very long very tight loop it might make a difference, but I doubt it.
Here's the full code I created to run the test:
var dataSize = 1000000 var numRuns = 25 const data = [...Array(dataSize).keys()] function doMaths (a) { var result = a + 100 result = result * 2000 result = Math.sqrt(result) result = result / 100 return result } const patterns = { plain: () => { for(var i = 0; i < data.length; i++) { doMaths(data[i]) } }, modified: () => { var i = 0 var len = data.length for (; i < len; i++) { doMaths(data[i]) } }, es6ForEach: () => { data.forEach(x => { doMaths(x) }) }, es6Map: () => { function mapFn (x) { return doMaths(x) } data.map(mapFn) } } const results = {} const runs = [...Array(numRuns).keys()] runs.forEach((run, i) => { Object.keys(patterns).forEach(patternName => { let begin = new Date() patterns[patternName]() let end = new Date() var diff = end.getTime() - begin.getTime() const result = results[patternName] || [] result.push(diff) results[patternName] = result }) }) Object.keys(results).forEach(patternName => { console.log(`Averaging ${results[patternName].length} runs of "${patternName}": ${results[patternName].reduce((a, b) => a + b) / results[patternName].length}ms`) })