From: Thomas 'PointedEars' Lahn on
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
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
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
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
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.