Weird behavoir while using function aliases

D

Daniel Rucareanu

I have the following script:

function Test(){}
Test.F = function(){}
Test.F.FF = function(){}
Test.F.FF.FFF = function(){}
Test.F.FF.FFF.FFFF = function(){}

//var alias = function(){};
var alias = Test.F.FF.FFF.FFFF;

var date1 = new Date();
for (var index = 0; index < 100000; index++)
Test.F.FF.FFF.FFFF();
//alias();
var date2 = new Date();
print(date2.getTime() - date1.getTime());


Why is it when I use Test.F.FF.FFF.FFFF() I get around 100ms and when I
get alias() I get around 280ms?
What is going on here? This is something I would have never anticipated
and I think that neither of you as well. Aliases whould work faster
then doing lockups at each iteration, right?
Also if I uncomment this line: var alias = function(){}, from 280ms I
get a drop to 265 ms?

Does anybody now the reason for this? It only happends in Firefox (I
have 1.5.0.6). IE and Opera behaive as they should
 
R

Richard Cornford

Daniel Rucareanu wrote:
Why is it when I use Test.F.FF.FFF.FFFF() I get around 100ms and
when I get alias() I get around 280ms?
What is going on here? This is something I would have never
anticipated and I think that neither of you as well. Aliases whould
work faster then doing lockups at each iteration, right?
Also if I uncomment this line: var alias = function(){}, from 280ms I
get a drop to 265 ms?

Given the relatively low resolution and precision of javascript date
objects, and the influence of task switching on the processor, I would
not trust results from measurements of periods as short as this.
Does anybody now the reason for this? It only happends in Firefox (I
have 1.5.0.6). IE and Opera behaive as they should

If the effect is real, and particular to Firefox then asking the people
who wrote the javascript engine for Firefox may help. They:-

netscape.public.mozilla.jseng

Richard.
 
D

Daniel Rucareanu

The precision of js date objects is indeed kind of low, but more the
enough when measuring durations larger than a few hundred milliseconds.

Also I don't think that tasks / threading switching have any influence,
because the result are constant, no matter how may time I run this
test. I also increased the loop from 100K iterations to 1M and the
results stay the same: using the alias is a lot slower.
 
M

Matt Kruse

Daniel said:
Why is it when I use Test.F.FF.FFF.FFFF() I get around 100ms and when
I get alias() I get around 280ms?

I get different results than you:
http://javascripttoolbox.com/temp/func_lookup_speed_test.html

Using alias for me is always almost twice as fast in Firefox 2 beta 2,
10%-25% faster in FF 1.5, and 15%-20% faster in IE6.
Try my url and see if you get the same results.

In any case, doing a lookup 100,000 times and only taking 100-200ms total
surely makes these results insignificant.
 
D

Daniel Rucareanu

Strange enough, I get results close to you. Probably must have
something to do the fact the functions are passed as parameters... even
the total executions time, in both situations are smaller. Anyway, this
complicates thing even more, rather than answering them.
I have posted this on another forum as well and there people have
confirmed by results. You might want to try with my test as well, just
to see what kind of result you get.
In FF 2, beta 2, using my test, the alias() test runs 3 times more
slowly that the other test. Also, the fact the 100,000 iterations take
about 200ms is because the application is extremely simple. Rest assure
then in more complex applications with a lot of object, heavy dom usage
etc., the results will be different.

Thank you,
Daniel
 
R

Richard Cornford

Daniel Rucareanu wrote:
confirmed by results. You might want to try with my test as well, just
to see what kind of result you get.
<snip>

Firefox 1.5.0.6 gives me - alias() - running in about 3/4 of the time
that - Test.F.FF.FFF.FFFF() - takes, with your code. Your case is not
demonstrated.

Richard.
 
M

Matt Kruse

Richard said:
Firefox 1.5.0.6 gives me - alias() - running in about 3/4 of the time
that - Test.F.FF.FFF.FFFF() - takes, with your code. Your case is not
demonstrated.

Same here. Are you sure that your posted code is actually your complete test
case?
 
D

Daniel Rucareanu

Yes, the test case is complete. Hoever, for testing I was using an
online environment that evaluates whatever code you write. It suited me
fine until now when I needed just a quick way to test something. It
seems that is has something to do with it. Creating a standalone page
and running it does creates some more plausible results.
Strange that this is the first time the environments in giving me
results other then what a real page would give. If anybody want to run
the test there and to check the result, the url is:
http://www.squarefree.com/jsenv/.
In IE and Opera however, running the test in this environment or
running the standalone page gives the same results...
Now I wonder why 2 persons (whom I don't know) confirmed my first
results... :) What were they using?

Daniel
 
M

Matt Kruse

Daniel said:
Yes, the test case is complete. Hoever, for testing I was using an
online environment that evaluates whatever code you write.
http://www.squarefree.com/jsenv/.

Ah, so you weren't just testing the speed of your test case, but also the
speed of any code wrapped around it to make the "environment" function.
These are important details not to be left out in the original post ;)

BTW, if you want to do speed tests, take a look at the code in the url I
posted in a previous message. I wrote the timeIt() function because I was
tired of manually doing the new Date() thing before and after, and I wanted
side-by-side comparisons of multiple ways of doing the same task. It's been
working nicely for me for a while.

One caveat: When testing long-running functions or many iterations, browsers
will usually pop up the "code is running slow" messages. Disable these in
your browser before doing speed tests.
 
V

VK

Daniel said:
Why is it when I use Test.F.FF.FFF.FFFF() I get around 100ms and when I
get alias() I get around 280ms?
What is going on here? This is something I would have never anticipated
and I think that neither of you as well. Aliases whould work faster
then doing lockups at each iteration, right?

Not at all. That may be true for some C-like language with pointers and
an execution cache (a la processor cache). I do not know for sure of a
such language but a technical possibility is here. For a Java-like
language without pointers, byRef aliases only slow down the execution
as the engine gets one more reference to resolve (alias > ref > ref >
ref instead of ref > ref > ref). The difference is fractional, so feel
free to use aliases if it improves the code readability: but do not
expect any serious performance changes.

That can be a performance improvement using a byVal intermediary
variable rather than recalculations, the most known sample is
var len = something.length;
for (var i=0; i<len; ++i) {
//...
}

It is also beneficial to use byRef intermediary variable for DOM
references rather than using getElementById each time. The latter is
especially true for IE because their getElementById implementation is
an artificial build-up atop of IE's native schema (with ID'ed elements
automatically referenced in the Global scope) and about four times
slower than a ready to use reference (MSDN says sorry for it, but they
are really not and it doesn't help too much in neither case :)

For within JavaScript references only (like in your case) that is the
matter of such fractions that pigs will become flying aces before you
get some practical impact out of it :)

What practical is that IE'e schema is much quicklier in resolving
references, because they do not use ECMAScript standard for Function
objects. Instead they treat each and every function as expression. The
result of this expression is function reference added to the Global
scope, no matter how deep this function is burried inside and by how
many syntax signs is it surrounded.
var foo = function bar(){};
window.alert(typeof bar);
- as just a starting hint of what am I talking about, not as an
explanation.

This speeds up the lookup process, because the engine jumps from one
ref to another in the same Global scope. It also mean that IE schema is
rather indifferent to how many levels down to go, as it really stays on
the same top level.

It also means that for really speed crutial processes like say visible
points calculation for 3D SVG/VML graphics it is highly benefitial to
skip on "correctness of approach" and use only top level functions
calling each other. IE is indifferent, but FF will "appreciate it".


P.S.
Also if I uncomment this line: var alias = function(){},
from 280ms I get a drop to 265 ms?

The draw back of traditional timerON > many loops > timerOFF tests is
that any good engine is much more than just "execute a chunk - move to
next chunk". A good engine (and both FF and IE ones are) has a
look-ahead and code optimisation mechanics inside. So starting with the
second loop you are dealing not only with the test subject but also
with optimization mechanics differences.
And this is just an icing on the cake where the cake is Date object
working with the precision no better than one system tick (10ms - 60ms
depending on OS).
 
M

Matt Kruse

VK said:
Not at all. That may be true for some C-like language with pointers
and an execution cache (a la processor cache). I do not know for sure
of a such language but a technical possibility is here. For a
Java-like language without pointers, byRef aliases only slow down the
execution as the engine gets one more reference to resolve (alias >
ref > ref > ref instead of ref > ref > ref). The difference is
fractional, so feel free to use aliases if it improves the code
readability: but do not expect any serious performance changes.

Wow, after Richard and I have already identified the root problem (the
environment used to do the test) and in fact posted results showing that the
aliased function *does* run faster in both IE and FF, you come out with this
load of crap? Almost everything you've written is wrong. You have no
understanding of the lookup process used to resolve the function references.

Maybe pictures are easier for you to understand:

Test --> F --> FF --> FFF --> FFFF <-- alias

Count the arrows.
quicklier

What a perfectly cromulant word!
 
V

VK

Matt said:
after Richard and I have already identified the root problem...

....UA producers have nothing left but urge to bring their engines
uniformly to the spelled rules. I'm not a big help to expedite the
process though ;-)
Maybe pictures are easier for you to understand:

Test --> F --> FF --> FFF --> FFFF <-- alias

A nice picture. From what programing language did you get it?
 
M

Matt Kruse

VK said:
A nice picture. From what programing language did you get it?

From the javascript posted:

function Test(){}
Test.F = function(){}
Test.F.FF = function(){}
Test.F.FF.FFF = function(){}
Test.F.FF.FFF.FFFF = function(){}
var alias = Test.F.FF.FFF.FFFF;

If you don't see how the "diagram" corresponds exactly to the code above,
you have more learning to do than I thought.
 
S

scriptguru

VK напиÑав:
...UA producers have nothing left but urge to bring their engines
uniformly to the spelled rules. I'm not a big help to expedite the
process though ;-)


A nice picture. From what programing language did you get it?
It is not important in this case that there is no pointers in JS. In
the first case interpreter will search few times in hashtables (bacause
in objects of JS properties and methods names are always keys of
hashtables). In the second case it is not needed to search in hashtable
because there is no properites or methods. Alias contains direct
reference to function object.

Val
 
R

Richard Cornford

VK напиÑав:
It is not important in this case that there is no pointers in JS.
In the first case interpreter will search few times in hashtables
(bacause in objects of JS properties and methods names are
always keys of hashtables).

Generalisations abut implementation details are rarely going to be
true. It has been shown that JScript appears to uses a list-like
structure for its javascript objects rather than a hashtable.
In the second case it is not needed to search in hashtable
because there is no properites or methods. Alias contains direct
reference to function object.

The Identifier for - alias - still needs to be resolved against the
scope chain, which does involve looking up the properties of objects.
The alias is quicker because it will take as long to resolve its
identifier to a function, that can then be called, as it does to
resolve the - test - identifier, and then the property accessor has to
look-up another three objects before it gets to the function.

Richard.
 
V

VK

<OT>
There is an all time slogan in this group "JavaScript is not Java". I
feel it's time to re-introduce the 2nd most popular: "JavaScript is not
C++" :) The timing seems right, as "memory leaks", "destructors",
"aliases", "pointers" and stuff is hitting the fan again. (This is why
many popular libraries are sub-optimal: not because they are imitating
the "classical" class-based paradigm - let them imitate whatever
customers want - but because they are written with performance
considerations taken from a rather *uniformed* and *very different*
environment).
</OT>

You want a performance jump (not random fractions due to run-time
optimization)? Then follow particular engine's mechanics.

// C-like thinking (? bad):
function Test(){}
Test.F = function(){}
Test.F.FF = function(){}
Test.F.FF.FFF = function(){}
Test.F.FF.FFF.FFFF = function(){}
var alias = Test.F.FF.FFF.FFFF();
....
for (var j=0; j<100000; ++j) {
alias();
}
....


// Script engine thinking (good):
// double performance improve for FF, IE doesn't care:
function Test(){
this.f1 = Test.F;
this.f1.f2 = Test.FF;
this.f1.f2.f3 = Test.FFF;
this.f1.f2.f3.f4 = Test.FFFF;
}
Test.F = function(){}
Test.FF = function(){}
Test.FFF = function(){}
Test.FFFF = function(){}
....
for (var j=0; j<100000; ++j) {
obj.f1.f2.f3.f4();
}
....


P.S. That is with leaving aside practical needs of a "package member"
like
utils.member.doing.something.useful()
If one needs to build such long chain, she's most definitely doing
something way too conceptual to be practical. IMO.
 
R

Richard Cornford

VK said:
that's a typo of course, correct:

var alias = Test.F.FF.FFF.FFFF;

You didn't try running your code, did you? There are many errors in it
beyond that one, and it doesn't demonstrate anything, or any better
performance in the second loop.

Richard.
 
V

VK

"Alias is more effective then explicit method call" is some kind of
urban legend to me appealing to one of the most strong mythologems of
programming: "shorter text === quicker program"; "alias()" is much
shorter than "f.ff.fff.ffff()", thusly it just has to be more
effective.
With such linear interpretation it would be also obvious to expect that
f() - f.ff.fff.ffff() - f.ff.fff.ffff.f.ff.fff.ffff() - ... have to
show twice bigger time difference between each pair from left to right.

In fact the maximum possible performance is very rarely the primary
target in programming. There are much more important factors:
stability, easiness of maintenance (including readability), easiness to
upgrade and to incorporate into other blocks of code. Speed is really
the last in the list. And with speed itself timed loops is the worst
tool one can get: because as I said you are really studying run-time
optimization mechanics of a particular engine, and only after (and if
lucky) your own question.

There is a simple and effective alternative to it: the code is quick
enough if it's quick enough for yourself. That leads to: never program
on a "best configuration of the year": always use an average-good
configuration of the *last* year (or even few years old). If say you
are satisfied with results on 900Mz/128Mb, your customers will be most
definitely satisfied on 3Gz/1Gb.

That doesn't eliminate a normal human curiosity: "how is this stuff
really ticking inside?" It's ticking rather interestingly and
differently for different script engines. In application to Microsoft
JScript one can study IDispatch interface (search MSDN for "IDispatch",
"IDispatch::Invoke", "IDispatch dispid").


P.S. See
<http://www.geocities.com/schools_ring/chicken_runs/index.html> What do
one thinks is being studied here? alias or run-time optimization and
threads management in different UA's? If one thinks the first, then
play with setTimeout delay, try runs with different handycaps. Choose
the "J-horse" you bet for sure by three runs results and tell me when
ready :)

P.P.S. As it is build on frames, it can be boring to copy page by page
(if one wants).
<http://www.geocities.com/schools_ring/chicken_runs/chicken_runs.zip>
contains full frameset.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top