>> n = int[10]
>>
>> if you try to access index 11 in array n, an IndexOutOfBounds
>> Exception is thrown. Same for C#, Objective C and obviously C. This
>> is fundamental and basic to all professional languages and
>> Actionscript 3 doesn't have it? Why? Who knows but it's not there.
Action script has associative arrays, it is part of the language design,
practally to use etc. In Visual Basic we would call this a collection, not
an array.
Letting the compiler know the size of the array allows implementation of
bounds checks throwing exceptions if the program violates the array bounds.
In real languages this is important for survival, if we have n = int[10] and
write to element 11 or read from element 11 we may get a page fault or write
to some data which lets our program crash 3 and a half hours later.... badly
to find out what happened afterwards. In AS writing element 11 is at least
save, we do not destroy memory.
It may break our algorythmns if we made something wrong with array indexes
like hoping with our chess figure out of the 8x8 board while we calculate
the answer to a chess move may result in some days of debugging ....
It seems the VM does not have any method to access real bytes, maybe it
would be possible to create an n-dimensional array of bytes into a string
accessing with charAt, depending what the string implementation does if we
put 0 inside the string and how cost effective is converting string to
numbers and vice versa.
> a) a compiler and the resulting bytecode are two very distinct things
> you may optimize the bytecode output, but you just can't add features
> to this bytecode or you become incompatible
We can not really add features to the bytecode, because this way we must
write a new VM or extend the old one. Would be too complicated. It is also
not really needed. We can maybe use undocumented features, for example
members of a class in AVM1 are usually created by a name, but the flash
player is completly happy if we use the prototype array with only numbers.
// in AVM1
push r:1,'fib'
function2(r:2='n') (r:1='this')
player is happy if we write
push r:1,0
function2() (r:1='this')
and later use 0 in callMethod.
The member function is now called 0 instead of fib but flash player is happy
with this and thanks with a bit more speed and extra obfuscation.
If we talk in optimizations we mean things like approving what we have and
find a solution with minimal number of commands, most of them can be done by
the programmer writing different the input for the compiler.
First are silly optimizations
r = 360/3
becomes
r = 120
MTASC does not do this, it writes push 360,3, devide
Another silly one is
a = b+c
d = a+e
A is alredy on stack, no need loading it in next expression it was alredy
there but deleted by varaible assignment. If a is local variable and never
used, drop variable assignment.
a = b+c
d = b+c+e
Avoid common sub expression b+c in second command. We may have some other
code lines betwenn that both to make things more difficult. In languages
that compile for real processors, common sub expressions are often found in
array index calculation type conversion etc. Things we do not have in Flash.
In a less silly example:
// Decrease counter of bombs and explode
// silly code anyway...
for (i=0;i<game.bombs.count;i++)
{
if (game.bombs[i].time == 0)
game.bombs[i].Explode()
else
{
game.bombs[i].time--;
game.bombs[i].Redraw();
}
}
game.bombs[i] is evaluated 4 times, game.bombs is evaluated 5 times,
registers used: 1. But we have 255 registers this makes plenty room for
optimization. If you compile and disassemble, you see that game.bombs.count
is evaluated time the loop is executed, the compiler can not tell if the
count member will modify by some of the functions we call, so it is the save
way to re-evaluate the expression until the programmer gives us hints or a
very very smart compiler is examining the explode and redraw function and
everything called by explode and readraw and so on....
// harder to read in real world apps but much faster
var temp1 = game.bombs
var temp2 = temp1.count // for evaluates the expression i<bombs.count each
time it loops, we take out that code.
for (i=0;i<temp2;i++)
{
var temp3=game.bombs[i];
if (temp3.time==0)
temp3.Explode()
else
{
temp1.bombs[i]--;
temp3.Explode()
}
}
game.bombs[i] is about 10 lines of assembly code, so we save a lot. A good
optimizer is finding the same blocks of code automatically, prove if the
variables it depends on are not modified and stores the result to a place
where it can be accessed fast, if we can not prove, the expression must be
re-evaluated and the effect is more locally.
We also can add new language elements to help in optimization. Using them is
optional. The "with" statement lets the programmer prove which part can be
optimized.
// Optimization with "with" stolen from Pascal or VB (do we have "with" in
AS?)
var temp1 = game.bombs.count
for (i=0;i<temp1;i++) // otherwise game.bombs.count will be evaluated in
each loop
{
with (game.bombs[i]) do // game.bombs[i] to the stack and store in a
register
{
// compiler may save .time to a register or use the stack and
save some time
if (.time == 0) .Explode()
else
{
.time--;
.Redraw();
}
}
"with" simply stores the result of the expression to the stack and into a
register for later use. If it is possible to approve the stack top the stack
is used otherwise, the regsiter. The ending block drops the element and the
register is marked as free. In other word the "." before .Explode simple
compiles into "dup" which is a very fast operation.
In real world things are more complicated, however.... Even Visual Studio
has problems with common sub expressions and sometimes creates code which
crashes.
All optimizations are have good theory background, but mostly the theory
requires a 3 operand assembly language without stack, we have 1 operand
stack code.
Thomas
_______________________________________________
osflash mailing list
[email protected]
http://osflash.org/mailman/listinfo/osflash_osflash.org