From: Thomas 'PointedEars' Lahn on 13 Jan 2010 19:18 Scott Sauyet wrote: > Thomas 'PointedEars' Lahn wrote: >>> Scott Sauyet wrote: >>>> var photos = [], captions = []; >>>> for (var i = 0, len = library.length; i < len; i++) { >>>> photos.push(library[i]["img"]); >>>> captions.push(library[i]["caption"]); >>>> } >> >> var >> photos = [], >> captions = [], >> len = library.length; >> >> photos.length = captions.length = len; >> >> for (var i = len; i--;) >> { >> var o = library[i]; >> photos[i] = o.img; >> captions[i] = o.caption; >> } > > Any of the suggestions will work, of course. Alas, not all, as I have pointed out. > The differences have to do with readability versus performance. > Perhaps the most readable version would be something like this: > > var photos = []; > var captions = []; > for (var i = 0; i < library.length; i++) { > var element = library[i]; > photos.push(element.img); > captions.push(element.caption); > } > > For people used to C-style languages, that loop will feel quite > familiar. You don't know what you are talking about. First of all, for all intents and purposes, ECMAScript is a C-style language. (If you have eyes to see you can observed the similarities.) (Therefore,) the backwards-counting loop will be familiar to "people used to C-style languages", and it will also be a lot more efficient than the one above. After all there is no boolean type in C before C99, so you would use `int' (and guess what, C's `for' statement works the same as in ECMAScript save the implicit conversion!), and in C99 you would use `(_Bool) i--' for maximum efficiency. The ascending order and the unnecessary push() calls will decrease efficiency considerably, and the push() call will decrease compatibility, too (JScript 5.0 does not have it). > My version added three minor changes: combining the initial > declarations into one statement, hoisting the length field out of the There is no field, `length' is a property. > loop, and removing the additional variable declaration inside the > loop. The first is mainly an issue of style (and perhaps bandwidth). > The second can be quite important for performance if the length is > determined by calling a heavy-weight function (such as if the list > were a dynamic collection of DOM nodes) but it probably has little > effect with a simple array like this. I have no idea how the third > change affects performance. It affects it considerably, as variable instantiation only happens once before execution, and identifiers are _not_ block-scoped -- don't you know *anything*? As a result, removing the variable declaration and initialization, rather unsurprisingly, *decreases* performance as the property lookup is repeated. Make benchmarks (or take more care when reading, and observe the results that I have posted already). > What Thomas suggests optimizes by reversing the iteration order. I > have heard that such a change often improves performance of JS loops, > but I don't know any numbers. Make benchmarks then, and you will see that the simple post-decrement increases efficiency considerably as well (else the pattern would not have prevailed, would it?). We have been over this ad nauseam before. > The main disadvantage for beginning programmers is that the loop is > somewhat less readable, at least until you get used to the convention. > It takes advantage of the fact that 0 in a test is read as false, > whereas positive integers are read as true. Do you get a kick out of explaining the obvious even though nobody has asked a question? And you are being imprecise after all: *All* numeric values *except* 0 (+0 and -0, but these are only Specification mechanisms) and NaN type-convert to `true'. We have discussed this numerous times as well. > One nice variation of this is this loop format: > > for (var i = library.length; i --> 0;) { > // ... > } > > This looks like "for i, starting at library.length, proceeding to 0, > do something". It really looks as though the code contains an arrow > ("-->"). In actuality this is the postfix decrement operator ("--") > followed by a greater-than compare (">"), and it takes advantage of > the fact that the comparison happens before the decrement. As in the more simple and more efficient i-- ... > But it's very pretty. Pretty according to whose standards? It could create misconceptions with newcomers about a limit-type `-->' operator, and it is even less readable or obvious than the versions you were incompetently whining about. > The OP will have to decide how to weight performance advantages > against code clarity, but in order to help, does anyone have links to > metrics on the different performance characteristics of loops in > different browser environments? The browser does not matter (the ECMAScript implementation does), and this discussion has been performed a hundred times or so already. So have results been posted. Much you have to learn. Score adjusted PointedEars -- Danny Goodman's books are out of date and teach practices that are positively harmful for cross-browser scripting. -- Richard Cornford, cljs, <cife6q$253$1$8300dec7(a)news.demon.co.uk> (2004)
From: Tuxedo on 14 Jan 2010 03:08 Thanks to all for posting the various code examples, it more than solved my small multi to single array items conversion problem! Tuxedo
From: Scott Sauyet on 14 Jan 2010 11:41 On Jan 13, 7:18 pm, Thomas 'PointedEars' Lahn <PointedE...(a)web.de> wrote: > Scott Sauyet wrote: >> The differences have to do with readability versus performance. >> Perhaps the most readable version would be something like this: > >> var photos = []; >> var captions = []; >> for (var i = 0; i < library.length; i++) { >> var element = library[i]; >> photos.push(element.img); >> captions.push(element.caption); >> } > >> For people used to C-style languages, that loop will feel quite >> familiar. > > You don't know what you are talking about. First of all, for all intents > and purposes, ECMAScript is a C-style language. (If you have eyes to see > you can observed the similarities.) I'm sure that often I don't, but in fact here I do know what I'm talking about. Javascript is syntactically in the family of C-style languages most recognizable for blocks delimited with curly braces. Just about any programmer who has programmed with a member of this family of languages will recognize this syntax: for (i = 0; i < bound; i++) { // Do something. } They not only recognize it, but have themselves have coded with it. The general form has been described [1] as for (initialization; continuation condition; incrementing expr) { statement(s) } This is of course inaccurate, because the last expression does not *have* to increment. It can decrement, it can do some strange combination, or it can be skipped altogether. But the incrementing version is the form with which programmers are most familiar. > (Therefore,) the backwards-counting loop will be familiar to "people used > to C-style languages", Many, probably most, will recognize this easily enough: for (i = bound; i > 0; i--) { // Do something. } But this is a less commonly used, and less familiar, variation. The following one though, is seen significantly less often: for (i = bound; i--;) { // Do something. } It won't work in languages which do not read a zero as false (Java), and it might be frowned upon on languages which do so, but discourage it (PHP?). I would not suggest it to a beginner unless it was to clear up some performance problem. The point, though, is that the OP was posting a problem that could be solved easily by someone with only a little Javascript experience. It might be a bad guess, but I did guess that the OP was a JS beginner, and gave the simplest answer I could think of that could work (except that I didn't think to remove what's become standard for me in hoisting the loop bound.) > and it will also be a lot more efficient than the > one above. [ ... ] I did say that I had heard that descending loops in JS were more efficient than ascending ones, and asked if there was documentation for that. I'm sorry, but your assertion is not enough proof for me. Do you know of any decent references? > The ascending order and the unnecessary push() calls will decrease > efficiency considerably, and the push() call will decrease compatibility, > too (JScript 5.0 does not have it). I don't know about the OP, but I tend to worry little about JScript 5.0. As to the efficiency claims, do you have any documentation? >> My version added three minor changes: combining the initial >> declarations into one statement, hoisting the length field out of the > > There is no field, `length' is a property. True. Pedantic, but true. >> loop, and removing the additional variable declaration inside the >> loop. The first is mainly an issue of style (and perhaps bandwidth). >> The second can be quite important for performance if the length is >> determined by calling a heavy-weight function (such as if the list >> were a dynamic collection of DOM nodes) but it probably has little >> effect with a simple array like this. I have no idea how the third >> change affects performance. > > It affects it considerably, as variable instantiation only happens once > before execution, and identifiers are _not_ block-scoped Yes, they are function-scoped in JS. So you're saying that this: for (var i = 0; i < library.length; i++) { var element = library[i]; photos.push(element.img); captions.push(element.caption); } is considerably more efficient than this: for (var i = 0; i < library.length; i++) { photos.push(library[i].img); captions.push(library[i].caption); } I don't know the relative costs of variable assignment versus array look-up. Do you have any references on this? I count an additional assignment in the first and one fewer look-up. Does that offer a considerable performance gain? > don't you know *anything*? Yes. I do. > As a result, removing the variable declaration and initialization, rather > unsurprisingly, *decreases* performance as the property lookup is repeated. > Make benchmarks (or take more care when reading, and observe the results > that I have posted already). I see differences in integer array look-ups, not arbitrary property look-ups. I don't know if ES implementations have any optimizations for dense arrays over arbitrary property look-ups. As to benchmarks, perhaps I will try some. But if you have references, would you please share them? Still, you are the one making the claim. Have you any benchmarks of your own to share? >> What Thomas suggests optimizes by reversing the iteration order. I >> have heard that such a change often improves performance of JS loops, >> but I don't know any numbers. > > Make benchmarks then, and you will see that the simple post-decrement > increases efficiency considerably as well (else the pattern would not have > prevailed, would it?). We have been over this ad nauseam before. Funny, you're often the one berating others for not understanding USENET. Having been over a subject ad nauseam, the group is best off pointing new users to FAQ entries or other resources. All you've done is make assertions. >> The main disadvantage for beginning programmers is that the loop is >> somewhat less readable, at least until you get used to the convention. >> It takes advantage of the fact that 0 in a test is read as false, >> whereas positive integers are read as true. > > Do you get a kick out of explaining the obvious even though nobody has > asked a question? Again, the OP asked a relatively easy question, and has received several competing solutions. If the OP knew all that, don't you think it likely she or he would have been able to solve the problem without asking in this group? > And you are being imprecise after all: *All* numeric > values *except* 0 (+0 and -0, but these are only Specification mechanisms) > and NaN type-convert to `true'. We have discussed this numerous times as > well. No, I was being quite precise. The construct under discussion does not take advantage of the fact that 2.718281828 or -pi are interpreted as true, only that the positive integers are. But thank you for pointing this out, as it brings to mind another potential pitfall of this technique: if the body of your loop decrements the loop variable, you might transform working code into a nearly-endless loop. Of course this is a sign of bad code, but it's more likely to happen with an equality condition ("i") than an inequality ("i < bound"). >> One nice variation of this is this loop format: > >> for (var i = library.length; i --> 0;) { >> // ... >> } > [ ... ] >> But it's very pretty. > > Pretty according to whose standards? The eye of the beholder, of course. Who else would you appoint the arbiter of code beauty? But I personally find it very appealing. > It could create misconceptions with > newcomers about a limit-type `-->' operator, and it is even less readable > or obvious than the versions you were incompetently whining about. Suddenly you're worried about newcomers? :-) My previous post was clearly not aimed at those who know Javascript well, just an attempt to help the OP. But I'm not sure that your two clauses above really work together well. If it creates misconceptions about a non-existent operator, it's only *because* it is more readable to the newcomer. Still, I take offense at the notion that my whining was incompetent. It clearly grated on you, so it seems to be very competent whining. :-) >> The OP will have to decide how to weight performance advantages >> against code clarity, but in order to help, does anyone have links to >> metrics on the different performance characteristics of loops in >> different browser environments? > > The browser does not matter (the ECMAScript implementation does), Overly pedantic again, but I'll certainly concede the point. > and this discussion has been performed a hundred times or so > already. So have results been posted. Do you happen to have references? > Much you have to learn. Yes I do. I'm just hoping to find some competent instructors. > Score adjusted So it's what now? 40 - love, my serve again? -- Scott ____________________ [1] http://en.wikipedia.org/wiki/Curly_bracket_programming_language#Loops
From: Scott Sauyet on 14 Jan 2010 13:17 On Jan 14, 11:41 am, Scott Sauyet <scott.sau...(a)gmail.com> wrote: > As to benchmarks, perhaps I will try some. Ok, I tried. I don't have much experience at JS benchmarks, so there may be major flaws in them. I'd appreciate it if someone could point out improvements. I've posted some tests here: http://scott.sauyet.com/Javascript/Test/LoopTimer/1/test/ These tests run two of the code samples discussed earlier [1, 2] multiple times. I try them with initial arrays of length 10, 100, 1000, and 10000. I run them, respectively, 100000, 10000, 1000, and 100 times, so in each case the number of iterations of the main body of the function will happen one million times. (This should lead to slightly higher efficiency with larger arrays, as the outer loop has to run fewer times, but I suspect that that's only noise in the results.) To run this in IE, I could only run one test at a time or the browser would alert me of long-running scripts and my running times were compromised. In any case, I report the number of iterations of the tested algorithm that run per millisecond. I collected the results at http://scott.sauyet.com/Javascript/Test/LoopTimer/1/ But I've done something screwy to IE on the page trying to be clever. If anyone can tell me why the generated links are not working properly in IE, I would love to know. In other browsers, you can see the results, run one of the predefined tests, or choose to run either algorithm in your browser, supplying the size of the array and the number of iterations to run. I tested on the browsers I have on my work machine: Chrome 3.0.195.27 FF 3.5.7 IE 8 Opera 9.64 Safari 4.0.3 All running on Windows XP on a fairly powerful machine. The results definitely say that the backward looping algorithm supplied by Thomas is generally more efficient than the forward one I gave. But there is some murkiness. First of all, the values reported as this is run in different browsers (and specifically in their ECMAScript implementations, for the pedantic among you) vary hugely. They can differ by a factor of 50 or more. In Opera the backward looping was faster at all array sizes, by an approximate factor of 3. In Safari it was faster by a factor of 5. In IE, it was faster, but at a factor that decreased as the array size increased, down to about 1.21 for a 10000-element array. In Firefox and Chrome it was more complicated. For array sizes of 10, 100, and 1000 in Chrome, the backward was faster than forward by factors approximately 1.5 - 2.5. But for 10000 elements, forward was faster by a factor of about 1.25; I checked at 100000 elements too, and forward was faster by a factor of about 1.5. In Firefox, backwards was faster than forwards for 10 and 100 elements, by a factor of 1.5 and 2, respectively, but for 1000, forward was faster than backward by a factor of 17, and at 10000, forward was faster by a factor of 27. It's not that forward improved at higher array sizes in FF but that backwards slowed way down. I did try reversing the order of the tests to see if garbage collection had anything to do with this, but it make no substantive difference. The conclusion I can draw from this is that backward is generally a better bet, but that might be reversed for higher array sizes, at least in FF and Chrome. So, has anyone got suggestions for improving these tests? Can someone tell me what's wrong in IE in my results page? -- Scott Sauyet ____________________ [1] http://groups.google.com/group/comp.lang.javascript/msg/73c40b2b284d970a [2] http://groups.google.com/group/comp.lang.javascript/msg/6c56a2bac08daaa4
From: Jorge on 14 Jan 2010 15:01
On Jan 14, 7:17 pm, Scott Sauyet <scott.sau...(a)gmail.com> wrote: > (...) > So, has anyone got suggestions for improving these tests? > (...) Yes, :-) http://jorgechamorro.com/cljs/093/ BTW, It's not faster i-- than i++. Cheers, -- Jorge. |