unofficial mirror of guile-devel@gnu.org 
 help / color / mirror / Atom feed
* srfi-18 and the vm
@ 2009-05-22 14:39 Andy Wingo
  2009-05-22 15:01 ` Julian Graham
                   ` (2 more replies)
  0 siblings, 3 replies; 14+ messages in thread
From: Andy Wingo @ 2009-05-22 14:39 UTC (permalink / raw)
  To: guile-devel

Hi!

I'm catching up with mail. On my syncase-in-boot-9 branch, I enabled
compilation of srfi-18 and fixed a bug in it regarding multiple-value
returns. Now I just ran the srfi-18 test like 100 times in a row and it
didn't show any strange errors. Yaaaay!

With psyntax running a pre-analysis phase on all source code, we can do
away with lazy memoization entirely -- a neat hack, but it made eval.c
buggy and impenetrable. I'll write more about that in the future.

Andy
-- 
http://wingolog.org/




^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: srfi-18 and the vm
  2009-05-22 14:39 srfi-18 and the vm Andy Wingo
@ 2009-05-22 15:01 ` Julian Graham
  2009-05-22 15:10 ` Ludovic Courtès
  2009-05-23  9:52 ` Neil Jerram
  2 siblings, 0 replies; 14+ messages in thread
From: Julian Graham @ 2009-05-22 15:01 UTC (permalink / raw)
  To: Andy Wingo; +Cc: guile-devel

I second the Yaaaay!


On Fri, May 22, 2009 at 10:39 AM, Andy Wingo <wingo@pobox.com> wrote:
> Hi!
>
> I'm catching up with mail. On my syncase-in-boot-9 branch, I enabled
> compilation of srfi-18 and fixed a bug in it regarding multiple-value
> returns. Now I just ran the srfi-18 test like 100 times in a row and it
> didn't show any strange errors. Yaaaay!
>
> With psyntax running a pre-analysis phase on all source code, we can do
> away with lazy memoization entirely -- a neat hack, but it made eval.c
> buggy and impenetrable. I'll write more about that in the future.




^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: srfi-18 and the vm
  2009-05-22 14:39 srfi-18 and the vm Andy Wingo
  2009-05-22 15:01 ` Julian Graham
@ 2009-05-22 15:10 ` Ludovic Courtès
  2009-05-22 16:20   ` Andy Wingo
  2009-05-23  9:52 ` Neil Jerram
  2 siblings, 1 reply; 14+ messages in thread
From: Ludovic Courtès @ 2009-05-22 15:10 UTC (permalink / raw)
  To: guile-devel

¡Hola!

Andy Wingo <wingo@pobox.com> writes:

> I'm catching up with mail. On my syncase-in-boot-9 branch, I enabled
> compilation of srfi-18 and fixed a bug in it regarding multiple-value
> returns. Now I just ran the srfi-18 test like 100 times in a row and it
> didn't show any strange errors. Yaaaay!

What kind of "strange errors" would it lead to before?

When SRFI-18 wasn't compiled, I would expect multiple value returns
would translate in a `value.c' struct that would then be passed along as
a single value.

Thanks,
Ludo'.





^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: srfi-18 and the vm
  2009-05-22 15:10 ` Ludovic Courtès
@ 2009-05-22 16:20   ` Andy Wingo
  2009-05-22 22:04     ` Ludovic Courtès
  0 siblings, 1 reply; 14+ messages in thread
From: Andy Wingo @ 2009-05-22 16:20 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: guile-devel

On Fri 22 May 2009 17:10, ludo@gnu.org (Ludovic Courtès) writes:

> ¡Hola!
>
> Andy Wingo <wingo@pobox.com> writes:
>
>> I'm catching up with mail. On my syncase-in-boot-9 branch, I enabled
>> compilation of srfi-18 and fixed a bug in it regarding multiple-value
>> returns. Now I just ran the srfi-18 test like 100 times in a row and it
>> didn't show any strange errors. Yaaaay!
>
> What kind of "strange errors" would it lead to before?

Random ones based on races, as code was lazily memoized from multiple
threads at once.

> When SRFI-18 wasn't compiled, I would expect multiple value returns
> would translate in a `value.c' struct that would then be passed along as
> a single value.

Indeed. The VM truncates multiple values, but here we were doing a (let
((x (values))) something x), which returned 0 values to a continuation
needing a value, raising a valid error. Fixed that in the original
source code.

In addition, we were sometimes getting 0 values in a for-effect context,
which the GHIL->GLIL compiler didn't support. Like this:

  (begin (call/cc (lambda (k) (k))) 10)

I've fixed this in the tree-il->glil compiler.

Cheers,

Andy
-- 
http://wingolog.org/




^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: srfi-18 and the vm
  2009-05-22 16:20   ` Andy Wingo
@ 2009-05-22 22:04     ` Ludovic Courtès
  0 siblings, 0 replies; 14+ messages in thread
From: Ludovic Courtès @ 2009-05-22 22:04 UTC (permalink / raw)
  To: guile-devel

Andy Wingo <wingo@pobox.com> writes:

> On Fri 22 May 2009 17:10, ludo@gnu.org (Ludovic Courtès) writes:
>> Andy Wingo <wingo@pobox.com> writes:
>>
>>> I'm catching up with mail. On my syncase-in-boot-9 branch, I enabled
>>> compilation of srfi-18 and fixed a bug in it regarding multiple-value
>>> returns. Now I just ran the srfi-18 test like 100 times in a row and it
>>> didn't show any strange errors. Yaaaay!
>>
>> What kind of "strange errors" would it lead to before?
>
> Random ones based on races, as code was lazily memoized from multiple
> threads at once.

OK (I thought you were referring to errors related to multiple-value
returns.)  Then, indeed, yaay!

> Indeed. The VM truncates multiple values, but here we were doing a (let
> ((x (values))) something x), which returned 0 values to a continuation
> needing a value, raising a valid error. Fixed that in the original
> source code.
>
> In addition, we were sometimes getting 0 values in a for-effect context,
> which the GHIL->GLIL compiler didn't support. Like this:
>
>   (begin (call/cc (lambda (k) (k))) 10)
>
> I've fixed this in the tree-il->glil compiler.

Thanks for the explanation.

Ludo'.





^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: srfi-18 and the vm
  2009-05-22 14:39 srfi-18 and the vm Andy Wingo
  2009-05-22 15:01 ` Julian Graham
  2009-05-22 15:10 ` Ludovic Courtès
@ 2009-05-23  9:52 ` Neil Jerram
  2009-05-23 16:38   ` Andy Wingo
  2 siblings, 1 reply; 14+ messages in thread
From: Neil Jerram @ 2009-05-23  9:52 UTC (permalink / raw)
  To: Andy Wingo; +Cc: guile-devel

Andy Wingo <wingo@pobox.com> writes:

> With psyntax running a pre-analysis phase on all source code, we can do
> away with lazy memoization entirely -- a neat hack, but it made eval.c
> buggy and impenetrable. I'll write more about that in the future.

Anticipating your "more in the future", do you mean that the
pre-analysis does all the memoization in advance?

     Neil




^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: srfi-18 and the vm
  2009-05-23  9:52 ` Neil Jerram
@ 2009-05-23 16:38   ` Andy Wingo
  2009-05-23 22:03     ` Ludovic Courtès
  0 siblings, 1 reply; 14+ messages in thread
From: Andy Wingo @ 2009-05-23 16:38 UTC (permalink / raw)
  To: Neil Jerram; +Cc: guile-devel

Hi Neil,

On Sat 23 May 2009 11:52, Neil Jerram <neil@ossau.uklinux.net> writes:

> Andy Wingo <wingo@pobox.com> writes:
>
>> With psyntax running a pre-analysis phase on all source code, we can do
>> away with lazy memoization entirely -- a neat hack, but it made eval.c
>> buggy and impenetrable. I'll write more about that in the future.
>
> Anticipating your "more in the future", do you mean that the
> pre-analysis does all the memoization in advance?

It does not now, but it could. All output from psyntax is created by
this set of procedures (internal to psyntax.scm):

    build-void
    build-application
    build-conditional
    build-lexical-reference
    build-lexical-assignment
    build-global-reference
    build-global-assignment
    build-global-definition
    build-lambda
    build-primref
    build-data
    build-sequence
    build-let
    build-named-let
    build-letrec

In compile mode, these procedures create tree-il objects. In eval mode,
they create s-expressions, so that the expander can work before modules
are booted. (The mode is the second parameter to sc-expand; it's eval
mode by default, but the compiler sets it to compile mode.)

One should be able to memoize ilocs in from the result of the output, in
a second pass, given that there are so few forms. Of course some
memoization can happen lazily too, e.g. global lookup; but macros have
already been expanded out, so the evaluator doesn't have to know about
`cond' or `do' or anything else, and unmemoization becomes trivial.

The evaluator can be drastically simplified, which is good for
maintainance. It will probably be faster too. Then, later on, we can
implement and compile an evaluator in Scheme itself, so that we can tail
call between interpreted and compiled code.

(We're not limited to just 2 passes, even in the interpreter, of course;
once we have an inliner, it would probably be a good idea to run it on
code for interpretation, as well.)

What do you think?

Andy
-- 
http://wingolog.org/




^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: srfi-18 and the vm
  2009-05-23 16:38   ` Andy Wingo
@ 2009-05-23 22:03     ` Ludovic Courtès
  2009-05-23 22:16       ` Andy Wingo
  0 siblings, 1 reply; 14+ messages in thread
From: Ludovic Courtès @ 2009-05-23 22:03 UTC (permalink / raw)
  To: guile-devel

Hello!

Andy Wingo <wingo@pobox.com> writes:

> On Sat 23 May 2009 11:52, Neil Jerram <neil@ossau.uklinux.net> writes:
>
>> Andy Wingo <wingo@pobox.com> writes:
>>
>>> With psyntax running a pre-analysis phase on all source code, we can do
>>> away with lazy memoization entirely -- a neat hack, but it made eval.c
>>> buggy and impenetrable. I'll write more about that in the future.
>>
>> Anticipating your "more in the future", do you mean that the
>> pre-analysis does all the memoization in advance?
>
> It does not now, but it could.
[...]

> In compile mode, these procedures create tree-il objects. In eval mode,
> they create s-expressions, so that the expander can work before modules
> are booted. (The mode is the second parameter to sc-expand; it's eval
> mode by default, but the compiler sets it to compile mode.)
>
> One should be able to memoize ilocs in from the result of the output, in
> a second pass, given that there are so few forms. Of course some
> memoization can happen lazily too, e.g. global lookup;
[...]

I'm slightly concerned that doing things ahead of time rather than just
in time (i.e., lazily) would have a negative impact on the interpreter's
start-up time, which may be noticeable for short-lived scripts.

What do you think?

Thanks,
Ludo'.





^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: srfi-18 and the vm
  2009-05-23 22:03     ` Ludovic Courtès
@ 2009-05-23 22:16       ` Andy Wingo
  2009-05-24 14:08         ` Ludovic Courtès
  0 siblings, 1 reply; 14+ messages in thread
From: Andy Wingo @ 2009-05-23 22:16 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: guile-devel

Hi!

On Sun 24 May 2009 00:03, ludo@gnu.org (Ludovic Courtès) writes:

> I'm slightly concerned that doing things ahead of time rather than just
> in time (i.e., lazily) would have a negative impact on the interpreter's
> start-up time, which may be noticeable for short-lived scripts.

In the guile -c 0 case, we don't have this issue, because no source is
expanded; it's all compiled already. The load time on my machine is
about 20 ms, which is about equal to what we discussed before (10 ms
base + 10 ms for psyntax). It is faster than before, and will get
faster.

For loading uncompiled scripts, things will be slower, unless your
modules #:use-syntax some other transformer. I don't know where the
tradeoff is between the increased expansion speed due to compilation and
slowdown due to a complete codewalk, but it's certainly there.

OTOH I would suspect that we can implement some kind of just-in-time
compilation -- essentially for each use-modules we can check to see if
the module is compiled, and if not just compile it then and there. It
would be a little slow the first time, but after that it would load much
faster, even faster than before. Python does this. We could add a guile
--no-comp option to disable it.

> What do you think?

I think it's a good question, and we're going to have to settle on a
good answer at some point.

Cheers,

Andy.
-- 
http://wingolog.org/




^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: srfi-18 and the vm
  2009-05-23 22:16       ` Andy Wingo
@ 2009-05-24 14:08         ` Ludovic Courtès
  2009-05-25 21:57           ` Neil Jerram
  0 siblings, 1 reply; 14+ messages in thread
From: Ludovic Courtès @ 2009-05-24 14:08 UTC (permalink / raw)
  To: guile-devel

Hello,

Andy Wingo <wingo@pobox.com> writes:

> For loading uncompiled scripts, things will be slower, unless your
> modules #:use-syntax some other transformer. I don't know where the
> tradeoff is between the increased expansion speed due to compilation and
> slowdown due to a complete codewalk, but it's certainly there.

Yes.  Likewise, it may be reasonable to assume from now on that most of
the code will be compiled.  For instance, an uncompiled script may just
be a small code snipped that uses mostly compiled code.

> OTOH I would suspect that we can implement some kind of just-in-time
> compilation -- essentially for each use-modules we can check to see if
> the module is compiled, and if not just compile it then and there. It
> would be a little slow the first time, but after that it would load much
> faster, even faster than before. Python does this. We could add a guile
> --no-comp option to disable it.

I don't like this idea because it implies implicitly letting Guile
fiddle with the user's file system.  OTOH, it's a pragmatic approach to
the problem at hand.

Thanks,
Ludo'.





^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: srfi-18 and the vm
  2009-05-24 14:08         ` Ludovic Courtès
@ 2009-05-25 21:57           ` Neil Jerram
  2009-05-29  9:55             ` Andy Wingo
  0 siblings, 1 reply; 14+ messages in thread
From: Neil Jerram @ 2009-05-25 21:57 UTC (permalink / raw)
  To: Ludovic Courtès, Andy Wingo; +Cc: guile-devel

ludo@gnu.org (Ludovic Courtès) writes:

> Hello,
>
> Andy Wingo <wingo@pobox.com> writes:
>
>> For loading uncompiled scripts, things will be slower, unless your
>> modules #:use-syntax some other transformer. I don't know where the
>> tradeoff is between the increased expansion speed due to compilation and
>> slowdown due to a complete codewalk, but it's certainly there.
>
> Yes.  Likewise, it may be reasonable to assume from now on that most of
> the code will be compiled.  For instance, an uncompiled script may just
> be a small code snipped that uses mostly compiled code.

It seems to me that once we have a completely working compiler, we
need to ask if there are any circumstances left where it is better to
use the current interpreter instead.

If the answer to that is yes, the details of those remaining
circumstances will (probably) make it obvious whether the
pre-memoization idea is worthwhile.

Do we already have performance measurements, and are those recorded /
summarized somewhere?

>> OTOH I would suspect that we can implement some kind of just-in-time
>> compilation -- essentially for each use-modules we can check to see if
>> the module is compiled, and if not just compile it then and there. It
>> would be a little slow the first time, but after that it would load much
>> faster, even faster than before. Python does this. We could add a guile
>> --no-comp option to disable it.
>
> I don't like this idea because it implies implicitly letting Guile
> fiddle with the user's file system.

I don't see why that should be.  Isn't it possible to read a .scm
file, compile its contents, and hold the compiled programs in memory?

Regards,
        Neil




^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: srfi-18 and the vm
  2009-05-25 21:57           ` Neil Jerram
@ 2009-05-29  9:55             ` Andy Wingo
  2009-05-30 23:07               ` Neil Jerram
  0 siblings, 1 reply; 14+ messages in thread
From: Andy Wingo @ 2009-05-29  9:55 UTC (permalink / raw)
  To: Neil Jerram; +Cc: Ludovic Courtès, guile-devel

Hi Neil,

On Mon 25 May 2009 23:57, Neil Jerram <neil@ossau.uklinux.net> writes:

> ludo@gnu.org (Ludovic Courtès) writes:
>
>> Andy Wingo <wingo@pobox.com> writes:
>>
>>> For loading uncompiled scripts, things will be slower, unless your
>>> modules #:use-syntax some other transformer. I don't know where the
>>> tradeoff is between the increased expansion speed due to compilation and
>>> slowdown due to a complete codewalk, but it's certainly there.
>>
>> Yes.  Likewise, it may be reasonable to assume from now on that most of
>> the code will be compiled.  For instance, an uncompiled script may just
>> be a small code snipped that uses mostly compiled code.
>
> It seems to me that once we have a completely working compiler, we
> need to ask if there are any circumstances left where it is better to
> use the current interpreter instead.

In the short term (within the next year or so), I would imagine that
ceval/deval would be faster than an eval written in Scheme -- though I
do not know.

On the other hand, an eval written in Scheme would allow for
tail-recursive calls between the evaluator and the VM.

Another option, besides an eval in Scheme, is replacing the evaluator
with the compiler. One could compile on the fly and run the compiled
code from memory, or cache to the filesystem, either alongside the .scm
files or in a ~/.guile-comp-cache/ or something. But compilation does
take some time.

It seems clear we still need an eval in C, at least to bootstrap Guile.

WRT replacing ceval, I guess my conclusion is that I don't know yet.

> If the answer to that is yes, the details of those remaining
> circumstances will (probably) make it obvious whether the
> pre-memoization idea is worthwhile.

Yes, perhaps. We probably need some timings.

> Do we already have performance measurements, and are those recorded /
> summarized somewhere?

We don't have very systematic ones. I was just running some of the
feeley benchmarks again, and it looked to me that the VM's speed is
about 3 or 4 times the speed of ceval in 1.9, but I should test with
benchmarks that run for only 1s or so, and measure compilation time, and
test against 1.8 too.

>>> OTOH I would suspect that we can implement some kind of just-in-time
>>> compilation -- essentially for each use-modules we can check to see if
>>> the module is compiled, and if not just compile it then and there. It
>>> would be a little slow the first time, but after that it would load much
>>> faster, even faster than before. Python does this. We could add a guile
>>> --no-comp option to disable it.
>>
>> I don't like this idea because it implies implicitly letting Guile
>> fiddle with the user's file system.
>
> I don't see why that should be.  Isn't it possible to read a .scm
> file, compile its contents, and hold the compiled programs in memory?

Yes, compile-and-load, from (system base compile). But you have to redo
the compilation the next time the file is loaded, of course.

Incidentally, ikarus had a similar discussion recently:

  http://thread.gmane.org/gmane.lisp.scheme.ikarus.user/723
  http://thread.gmane.org/gmane.lisp.scheme.ikarus.user/745

Cheers,

Andy
-- 
http://wingolog.org/




^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: srfi-18 and the vm
  2009-05-29  9:55             ` Andy Wingo
@ 2009-05-30 23:07               ` Neil Jerram
  2009-05-31 13:30                 ` Andy Wingo
  0 siblings, 1 reply; 14+ messages in thread
From: Neil Jerram @ 2009-05-30 23:07 UTC (permalink / raw)
  To: Andy Wingo; +Cc: Ludovic Courtès, guile-devel

Andy Wingo <wingo@pobox.com> writes:

> Hi Neil,
>
> On Mon 25 May 2009 23:57, Neil Jerram <neil@ossau.uklinux.net> writes:
>
>> ludo@gnu.org (Ludovic Courtès) writes:
>>
>>> Andy Wingo <wingo@pobox.com> writes:
>>>
>>>> For loading uncompiled scripts, things will be slower, unless your
>>>> modules #:use-syntax some other transformer. I don't know where the
>>>> tradeoff is between the increased expansion speed due to compilation and
>>>> slowdown due to a complete codewalk, but it's certainly there.
>>>
>>> Yes.  Likewise, it may be reasonable to assume from now on that most of
>>> the code will be compiled.  For instance, an uncompiled script may just
>>> be a small code snipped that uses mostly compiled code.
>>
>> It seems to me that once we have a completely working compiler, we
>> need to ask if there are any circumstances left where it is better to
>> use the current interpreter instead.
>
> In the short term (within the next year or so), I would imagine that
> ceval/deval would be faster than an eval written in Scheme -- though I
> do not know.

I wasn't thinking of an eval written in Scheme.  I was assuming the
other option that you mention below, i.e. eval becomes compile on the
fly followed by VM execution.

> On the other hand, an eval written in Scheme would allow for
> tail-recursive calls between the evaluator and the VM.
>
> Another option, besides an eval in Scheme, is replacing the evaluator
> with the compiler. One could compile on the fly and run the compiled
> code from memory, or cache to the filesystem, either alongside the .scm
> files or in a ~/.guile-comp-cache/ or something.
>
> But compilation does take some time.

I guess this is the key point.  Even when the compiler has itself been
compiled?

So would it be correct to say, based on your performance observations
so far, that the time needed to compile and then VM-execute a piece of
code is greater than the time needed to interpret the same piece of
code?

In other words, that ahead-of-time compilation is helpful,
performance-wise, but that just-in-time compilation is net negative?

(Perhaps that is a ridiculous question even to ask...  It may be well
known that just-in-time compilation is always net negative if you only
consider one execution of the code concerned.  I'm afraid I'm not
familiar enough with the CS background on this.)

> It seems clear we still need an eval in C, at least to bootstrap Guile.

Yes, good point, I had forgotten that!

>> Do we already have performance measurements, and are those recorded /
>> summarized somewhere?
>
> We don't have very systematic ones. I was just running some of the
> feeley benchmarks again, and it looked to me that the VM's speed is
> about 3 or 4 times the speed of ceval in 1.9, but I should test with
> benchmarks that run for only 1s or so, and measure compilation time, and
> test against 1.8 too.

So, extremely roughly, that would mean that just-in-time compilation
could only be net-zero or net-positive if the complexity of the
compilation code was less than 3 or 4 times the complexity of the code
being compiled.  Which for a short piece of code is unlikely.

>>>> OTOH I would suspect that we can implement some kind of just-in-time
>>>> compilation -- essentially for each use-modules we can check to see if
>>>> the module is compiled, and if not just compile it then and there. It
>>>> would be a little slow the first time, but after that it would load much
>>>> faster, even faster than before. Python does this. We could add a guile
>>>> --no-comp option to disable it.
>>>
>>> I don't like this idea because it implies implicitly letting Guile
>>> fiddle with the user's file system.
>>
>> I don't see why that should be.  Isn't it possible to read a .scm
>> file, compile its contents, and hold the compiled programs in memory?
>
> Yes, compile-and-load, from (system base compile). But you have to redo
> the compilation the next time the file is loaded, of course.
>
> Incidentally, ikarus had a similar discussion recently:
>
>   http://thread.gmane.org/gmane.lisp.scheme.ikarus.user/723
>   http://thread.gmane.org/gmane.lisp.scheme.ikarus.user/745

I see; good references - there's no need for us to have the same
conversation again!  I think I agree with Aziz's conclusion -
i.e. "auto-caching" should be disabled by default.

The thread suggested to me that Ikarus has to compile the code that it
reads before it can execute it; i.e. that it doesn't retain an
interpretation option.  Is that correct?  If so, Guile might
reasonably make slightly different decisions - e.g. to interpret a
module when using it for the first time, and also to start compiling
it on another thread, with the module's procedures being replaced
one-by-one by VM programs as the compilation progresses.

Regards,
        Neil




^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: srfi-18 and the vm
  2009-05-30 23:07               ` Neil Jerram
@ 2009-05-31 13:30                 ` Andy Wingo
  0 siblings, 0 replies; 14+ messages in thread
From: Andy Wingo @ 2009-05-31 13:30 UTC (permalink / raw)
  To: Neil Jerram; +Cc: Ludovic Courtès, guile-devel

Ni Neil,

On Sun 31 May 2009 01:07, Neil Jerram <neil@ossau.uklinux.net> writes:

> Andy Wingo <wingo@pobox.com> writes:
>
>> In the short term (within the next year or so), I would imagine that
>> ceval/deval would be faster than an eval written in Scheme -- though I
>> do not know.
>
> I wasn't thinking of an eval written in Scheme.  I was assuming the
> other option that you mention below, i.e. eval becomes compile on the
> fly followed by VM execution.

Incidentally, the REPL compiles by default now, and has been doing so
since VM was merged to master. But expressions at the REPL are less code
than are full libraries.

>> But compilation does take some time.
>
> I guess this is the key point.  Even when the compiler has itself been
> compiled?

Much less time in that case, of course. It's the difference between the
load-compiled case and the ceval case below.

I think my understanding of the performance equation goes like this:

  1.8 ceval time =
     (read time) + ((% of executed code) * (memoization time)) + (ceval runtime)

  1.9 ceval time =
     (psyntax expansion time) + (1.8 ceval time)

  1.9 compile time =
     (read time) + (psyntax expansion time) + (compile time)

  1.9 load-compiled time =
     (VM runtime)

  1.9 compile-and-load time =
     (1.9 compile time) + (1.9 load-compiled time)

Let's say that (VM runtime) == 1/4 * (ceval runtime). Then:

  (1.9 compile-and-load time) < (1.9 ceval time)

    if

  (compile time) < (% of executed code) * (memoization time) + 3/4 * (ceval runtime)

Compile-time and memozation time depend linearly on the "complexity" of
the code, as you say.

And my suspicion is that memoization doesn't actually take very much
time, compared to expansion. So a simpler version would be that we win
when:

  (compile time) < 3/4 * (ceval runtime)

Of course there is probably a lower bound to all of this, that we don't
care about differences under 50 ms or so, when operating on non-compiled
code. So if we keep compile time always under 40 ms or so, we're good.

Now how do we stack up, then? Well:

    scheme@(guile-user)> (use-modules (ice-9 time) (system base compile))
    scheme@(guile-user)> (time (compile-file "module/language/tree-il.scm"))
    clock utime stime cutime cstime gctime
     1.17  1.03  0.14   0.00   0.00   0.30
    $1 = "module/language/tree-il.go"
    scheme@(guile-user)> (time (compile-file "module/language/tree-il.scm"))
    clock utime stime cutime cstime gctime
     1.06  0.91  0.14   0.00   0.00   0.16
    $2 = "module/language/tree-il.go"
    scheme@(guile-user)> (time (compile-file "module/language/tree-il/spec.scm"))
    clock utime stime cutime cstime gctime
     0.03  0.03  0.00   0.00   0.00   0.01
    $3 = "module/language/tree-il/spec.go"
    scheme@(guile-user)> (time (compile-file "module/language/tree-il/spec.scm"))
    clock utime stime cutime cstime gctime
     0.02  0.02  0.00   0.00   0.00   0.00
    $4 = "module/language/tree-il/spec.go"
    scheme@(guile-user)> (time (compile-file "module/language/assembly/disassemble.scm"))
    clock utime stime cutime cstime gctime
     0.25  0.20  0.05   0.00   0.00   0.04
    $5 = "module/language/assembly/disassemble.go"
    scheme@(guile-user)> (time (compile-file "module/language/assembly/disassemble.scm"))
    clock utime stime cutime cstime gctime
     0.26  0.23  0.03   0.00   0.00   0.03
    $6 = "module/language/assembly/disassemble.go"
    scheme@(guile-user)> (time (compile-file "module/language/tree-il/compile-glil.scm"))
    clock utime stime cutime cstime gctime
     0.53  0.44  0.09   0.00   0.00   0.10
    $7 = "module/language/tree-il/compile-glil.go"
    scheme@(guile-user)> 
    wingo@unquote:~/src/guile$ ls -l module/language/tree-il.scm module/language/tree-il/spec.scm module/language/assembly/disassemble.scm module/language/tree-il/compile-glil.scm
    -rw-rw-r-- 1 wingo wingo  6467 2009-05-29 15:39 module/language/assembly/disassemble.scm
    -rw-rw-r-- 1 wingo wingo 16377 2009-05-29 15:39 module/language/tree-il/compile-glil.scm
    -rw-rw-r-- 1 wingo wingo 11734 2009-05-29 15:39 module/language/tree-il.scm
    -rw-rw-r-- 1 wingo wingo  1390 2009-05-29 15:39 module/language/tree-il/spec.scm

This is on my laptop, with the ondemand cpu speed thingie.

It seems expansion itself is taking a bit of this time:

    scheme@(guile-user)> ,m language tree-il
    scheme@(language tree-il)> (use-modules (ice-9 time))
    scheme@(language tree-il)> (time (with-input-from-file "module/language/tree-il.scm" (lambda () (let lp ((x (read))) (if (not (eof-object? x)) (begin (sc-expand x) (lp (read))))))))
    clock utime stime cutime cstime gctime
     0.44  0.44  0.01   0.00   0.00   0.12
    scheme@(language tree-il)> ,m language assembly disassemble
    scheme@(language assembly disassemble)> (use-modules (ice-9 time))
    scheme@(language assembly disassemble)> (time (with-input-from-file "module/language/assembly/disassemble.scm" (lambda () (let lp ((x (read))) (if (not (eof-object? x)) (begin (sc-expand x) (lp (read))))))))
    clock utime stime cutime cstime gctime
     0.10  0.09  0.00   0.00   0.00   0.02

Indeed, the expander seems to take most of the time when loading (ice-9
match). I've run all of these in fresh Guiles so one expansion doesn't
have the cost of creating a module:

    scheme@(guile-user)> (time (load "module/ice-9/match.scm"))
    clock utime stime cutime cstime gctime
     1.02  0.98  0.01   0.00   0.00   0.26
    scheme@(guile-user)> (time (with-input-from-file "module/ice-9/match.scm" (lambda () (let lp ((x (read))) (if (not (eof-object? x)) (begin (sc-expand x) (lp (read))))))))
    clock utime stime cutime cstime gctime
     0.97  0.95  0.01   0.00   0.00   0.25
    scheme@(guile-user)> (time (compile-file "module/ice-9/match.scm"))
    clock utime stime cutime cstime gctime
     3.40  3.01  0.39   0.00   0.00   0.79
    scheme@(guile-user)> (time (load-compiled "module/ice-9/match.go"))
    clock utime stime cutime cstime gctime
     0.00  0.00  0.00   0.00   0.00   0.00

Conclusions:

  * The 1.8 memoizer was a big win, because the % of executed code could
    be very small -- loading (ice-9 match) never had to traverse all of
    those nodes.

    OTOH, the memoizer seems to be irrelevant with psyntax run on all of
    the source code, because expansion has to traverse all nodes, so you
    don't get the savings.

  * Currently, expansion seems to take between 30% and 40% of compile
    time. (I imagine we can reduce this absolute time by a factor of 2
    or so with some more optimized compilation of multiple-values cases,
    which should be easy, and the addition of an inliner, which will be
    a bit of work.)

  * Of course, once code is compiled, loading it is *very* fast, and I
    believe it to run at about 3 or 4 times the speed, though I have not
    shown that here.


I guess the big question is, what will the impact be on our users? I
suppose we can divide those users into three categories:

  * People who write short scripts in Guile, using standard libraries.

    These people are likely to see no change, speed-wise. Guile might
    start up faster and the libraries might load faster, but then again,
    perhaps their scripts take 300 ms to expand, which makes that speed
    gain moot.

    We could offer them compilation of their scripts, in the
    compile-and-load sense, which could help if their scripts take a
    long time to run.

  * People who write big programs in Guile.

    These people will likely be irked by the initial slowness with which
    their programs run, but will also likely be receptive to compiling
    their own programs and libraries, which will make them much faster.
    It is likely that these people have butted up against Guile's speed
    limitations.

  * People who use programs written in Guile, but that don't know
    anything about compiling or maybe even Scheme.

    This people are most likely to be adversely affected by all of this.
    Their programs start up more slowly, and for no obvious reason.


I get the feeling that we really should compile libraries by default. We
can put the results in ~/.guile-something if we don't have permissions
to put them alongside the .scm files -- that allows for sharing in
multiuser installations, but doesn't require it.

This way we offer more advantages to our users. But I don't know. What
do yall think?

>> Incidentally, ikarus had a similar discussion recently:
>>
>>   http://thread.gmane.org/gmane.lisp.scheme.ikarus.user/723
>>   http://thread.gmane.org/gmane.lisp.scheme.ikarus.user/745
>
> I see; good references - there's no need for us to have the same
> conversation again!  I think I agree with Aziz's conclusion -
> i.e. "auto-caching" should be disabled by default.

I think I might have convinced myself otherwise.

> The thread suggested to me that Ikarus has to compile the code that it
> reads before it can execute it; i.e. that it doesn't retain an
> interpretation option.  Is that correct?

Yes.

> If so, Guile might reasonably make slightly different decisions - e.g.
> to interpret a module when using it for the first time, and also to
> start compiling it on another thread, with the module's procedures
> being replaced one-by-one by VM programs as the compilation
> progresses.

Interesting idea :)

Cheers,

Andy
-- 
http://wingolog.org/




^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2009-05-31 13:30 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-05-22 14:39 srfi-18 and the vm Andy Wingo
2009-05-22 15:01 ` Julian Graham
2009-05-22 15:10 ` Ludovic Courtès
2009-05-22 16:20   ` Andy Wingo
2009-05-22 22:04     ` Ludovic Courtès
2009-05-23  9:52 ` Neil Jerram
2009-05-23 16:38   ` Andy Wingo
2009-05-23 22:03     ` Ludovic Courtès
2009-05-23 22:16       ` Andy Wingo
2009-05-24 14:08         ` Ludovic Courtès
2009-05-25 21:57           ` Neil Jerram
2009-05-29  9:55             ` Andy Wingo
2009-05-30 23:07               ` Neil Jerram
2009-05-31 13:30                 ` Andy Wingo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).