On 11/06/03 Leopold Toetsch wrote:
> >     because pmc as so much slower that native types i loose most of 
> >     parrots
> >     brilliant speed due to the fact that i don't know the types at
> >     compile-time.
> 
> When you get rid of all the temps and the clone[1], the loop with PMCs 
> runs at about 3 times the speed of perl5. So I'd not use the term 
> "slow". It would be faster with native types of course, but its hard to 
> impossible to decide at compile time, that native types do the same 
> thing. I don't know PHP but in perl the + operator could be overloaded 
> and calculate the phase of the moon instead of adding 5.5 ...

Yes, this is a very important point. Current dynamic languages can get a
speedup in several ways:
*) reducing opcode dispatch overhead (the various mechanisms parrot
uses, like cgoto etc. and by jitting the code)
*) better VM implementation (for example the use of the vtable instead
of multiple checks trick that Dan mentions)
*) code specialization

The first two you get for free using parrot or another advanced VM
with a JIT (well, the second item may require to properly design
the language specific PMCs or classes, but that's not so difficult after
so many years with the perl/python/php engines).
The latter is what potentially gives the bigger benefit, but it requires
lot of work in the compiler frontend (using typed variables make it
easier, but to preserve a better feel something like pysco is needed).
For example, in the sample code, the compiler could see that the
constants are integers and doubles and that the '+' operator hasn't been
overloaded, so it could do away with using PMCs (but, again, this is a
lot of work).

Parrot already helps with the first two items a lot and it provides most
of the needed features for the last item: specialized registers and a
JIT. What I think it's missing is a way to perform function/method calls
very fast (maybe also allowing inlining). This is needed when mixing
different languages that install their own vtable implementations
and also inside a dynamic language that uses vtables to multiplex
(like it would happen with perl's IV, NV etc variables). But this is a
small concern: much more work needs to go into the compiler frontends
for php/python/perl etc to take advantage of it.

> # for ($i = 0; $i < 10_000_000; $i = $i + 5.5) { }
> .sub _main
>     .sym var i
>     i = new PerlUndef
>     i = 0
>     $P0 = new PerlUndef
>     $P0 = 10000000
> lp:
>     if i >= $P0 goto ex
>     i = i + 5.5
>     goto lp
> ex:
>     end
> .end

I couldn't resist writing an equivalent program that runs on the Mono
VM:-) The Perl* classes implement just a few methods to support the
sample code, but adding more is mostly an issue of cut&paste.
I believe the code accurately simulates what would happen with perl or
php: a scalar starts out as an integers and becomes a float mid-way.
There are three different loops that simulate how smart a compiler
frontend could be: one puts just $i in a 'PMC' (PerlSV in the C# code),
the second puts the loop upper bound, too, to match with the above imcc
code and the last puts the 5.5 constant in a PerlSV, too.

The run times on my machine are:

mono smart-compiler: 250 ms
mono imcc-equiv: 340 ms
mono little-dumber-compiler: 460 ms
parrot -j -O 4: 580 ms
parrot -j: 600 ms
parrot: 635 ms
perl: 1130 ms
php4: 2150 ms

The parrot build was configured with --optimize. Parrot has a very good
startup time (~10 ms), while the startup time for mono (running the same
program with the loop limit set to 10) is about 100 ms. We obviously need
to improve this, but this also means that the actual time to execute the
loops above is about 100 ms lower for mono and 10 ms lower for parrot 
(times were measured with time (1)).
My *guess* is that mono executes the same code faster because of better
register allocation and faster function calls, I expect parrot to
improve on both counts as it matures, at least as long as the vtable
functions are implemented in C: anyone with performance numbers for
vtable functions implemented in parrot asm?
Hope this helps in the discussion (I included the C# code below so that
people can check if I missed something of the semantics).

lupus

-- 
-----------------------------------------------------------------
[EMAIL PROTECTED]                                     debian/rules
[EMAIL PROTECTED]                             Monkeys do it better


using System;

abstract class PerlBase {
        public abstract PerlBase Add (double v);
        public abstract PerlBase Add (int v);
        public abstract PerlBase Add (PerlBase v);
        public abstract bool LessThan (int v);
        public abstract bool LessThan (PerlBase v);
        public abstract PerlIV IsIV ();
        public abstract PerlNV IsNV ();
}

sealed class PerlIV: PerlBase {
        internal int value;

        public PerlIV (int v) {
                value = v;
        }

        public override PerlBase Add (double v) {
                PerlNV res = new PerlNV (v + (double)value);
                return res;
        }
        public override PerlBase Add (int v) {
                value += v;
                return this;
        }
        public override PerlBase Add (PerlBase v) {
                PerlIV iv = v.IsIV ();
                if (iv != null)
                        return Add (iv.value);
                PerlNV nv = v.IsNV ();
                if (nv != null)
                        return Add (nv.value);
                // warn: numeric add with non-numeric val
                return this;
        }
        public override bool LessThan (int v) {
                return value < v;
        }
        public override bool LessThan (PerlBase v) {
                PerlIV iv = v.IsIV ();
                if (iv != null)
                        return value < iv.value;
                PerlNV nv = v.IsNV ();
                if (nv != null)
                        return value < nv.value;
                // warn: numeric compare with non-numeric val
                return false;
        }
        public override PerlIV IsIV () {
                return this;
        }
        public override PerlNV IsNV () {
                return null;
        }
}

sealed class PerlNV: PerlBase {
        internal double value;

        public PerlNV (double v) {
                value = v;
        }

        public override PerlBase Add (int v) {
                value += v; // double + int gives a double
                return this;
        }
        public override PerlBase Add (double v) {
                value += v;
                return this;
        }
        public override PerlBase Add (PerlBase v) {
                PerlNV nv = v.IsNV ();
                if (nv != null)
                        return Add (nv.value);
                PerlIV iv = v.IsIV ();
                if (iv != null)
                        return Add (iv.value);
                // warn: numeric add with non-numeric val
                return this;
        }
        public override bool LessThan (int v) {
                return value < v;
        }
        public override bool LessThan (PerlBase v) {
                PerlNV nv = v.IsNV ();
                if (nv != null)
                        return value < nv.value;
                PerlIV iv = v.IsIV ();
                if (iv != null)
                        return value < iv.value;
                // warn: numeric compare with non-numeric val
                return false;
        }
        public override PerlIV IsIV () {
                return null;
        }
        public override PerlNV IsNV () {
                return this;
        }
}

class PerlSV: PerlBase {
        internal PerlBase value;

        public PerlSV (int v) {
                value = new PerlIV (v);
        }
        
        public PerlSV (double v) {
                value = new PerlNV (v);
        }

        public override PerlBase Add (double v) {
                value = value.Add (v);
                return this;
        }

        public override PerlBase Add (int v) {
                value = value.Add (v);
                return this;
        }
        public override PerlBase Add (PerlBase v) {
                value = value.Add (v);
                return this;
        }
        public override bool LessThan (int v) {
                return value.LessThan (v);
        }
        public override bool LessThan (PerlBase v) {
                return value.LessThan (v);
        }
        public override PerlIV IsIV () {
                return value.IsIV ();
        }
        public override PerlNV IsNV () {
                return value.IsNV ();
        }
}

class Test {
        static void Main () {
                /*
                 * C# code representing:
                 * for ($i = 0; $i < 10_000_000; $i = $i + 5.5) { }
                 * Note: $i starts as an int and becomes a double midway
                 */
                PerlBase sv = new PerlSV (0);
                PerlBase limit = new PerlSV (10000000);
                // alternative small value to check startup cost
                // PerlBase limit = new PerlSV (10);
                // the upper bound is a constant in the source, so the compiler
                // could easily produce this code:
                // for (; sv.LessThan (10000000); sv = sv.Add (5.5)) { }
                // 
                // but we're going with the slower version below, since
                // the imcc code uses the same 'pattern' and we don't want
                // to cheat:-)
                for (; sv.LessThan (limit); sv = sv.Add (5.5)) { }
                //
                // even slower version: all operands are objects (but we're faster 
                // than parrot anyway)
                // PerlBase increment = new PerlSV (5.5);
                // for (; sv.LessThan (limit); sv = sv.Add (increment)) { }
        }
}

Reply via email to