Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

run-once code

0 views
Skip to first unread message

David Storrs

unread,
Jan 13, 2004, 11:37:21 PM1/13/04
to The Perl6 Language List
Given this code:

if ( some_expensive_lookup_function() >= $MAX_RECORDS ) {
mark_that_we_have_reached_max_records();
return;
}

After I enter that block once, I never want to evaluate the condition
again--I want the code to completely disappear from the bytecode (or,
at least, be jumped around). How would I do that in Perl 6?

(I recall seeing something about how to make assertions drop out, but
I want to be able to do this at run-time, not compile-time.)

--Dks

Luke Palmer

unread,
Jan 14, 2004, 12:16:48 AM1/14/04
to The Perl6 Language List
David Storrs writes:
> Given this code:
>
> if ( some_expensive_lookup_function() >= $MAX_RECORDS ) {
> mark_that_we_have_reached_max_records();
> return;
> }
>
> After I enter that block once, I never want to evaluate the condition
> again--I want the code to completely disappear from the bytecode (or,
> at least, be jumped around). How would I do that in Perl 6?

Hmm...

my $max_reached;
sub mark_that_we_have_reached_max_records() {
$max_reached = 1;
}

if !$max_reached && some_expensive_lookup_function() > $MAX_RECORDS {
mark_that_we_have_reached_max_records();
return;
}

That's a new feature we're adding called "conditional code removal",
sometimes known as "short circuiting" :-)

I've given some thought to this sort of thing for optimization, however.
Obviously a conditional on a variable isn't a big price to pay, but when
you have a lot of them, removing the code altogether might save you some
important cycles. This is particularly true if this condition is within
a tight loop.

So, let's say you have something like:

$x = 100_000;
my $seen;
while $x --> 0 {
if $seen || cheap_condition() {
something();
}
$seen++ if other_cheap_condition();
}

We'd like this to be easy to unroll into:

$x = 100_000;
while $x --> 0 {
if cheap_condition() {
something();
}
last if other_cheap_condition();
}
while $x --> 0 {
something();
}

Especially if something() is a complex expression that's prone to typing
errors, we'd like to keep out the code duplication.

This could well be something the optimizer does, but assuming otherwise,
optimized currying could be to our benefit.

sub do_the_loop($x, $seen) {
while $x --> 0 {
if $seen || cheap_condition() {
something();
}
last if !$seen && other_cheap_condition();
}
return $x;
}

&do_the_loop.assuming(seen => 1).(
&do_the_loop.assuming(seen => 0).(100_000));

If curries are subject to a short constant-folding pass, this should
easily give us the effect we want.

> (I recall seeing something about how to make assertions drop out, but
> I want to be able to do this at run-time, not compile-time.)

As uri said, who can tell the difference?

Luke

Richard Nuttall

unread,
Jan 14, 2004, 6:57:05 AM1/14/04
to David Storrs, The Perl6 Language List
David Storrs wrote:

>Given this code:
>
> if ( some_expensive_lookup_function() >= $MAX_RECORDS ) {
> mark_that_we_have_reached_max_records();
> return;
> }
>
>After I enter that block once, I never want to evaluate the condition
>again--I want the code to completely disappear from the bytecode (or,
>at least, be jumped around). How would I do that in Perl 6?
>
>

How about

$test = sub
{


if ( some_expensive_lookup_function() >= $MAX_RECORDS )

mark_that_we_have_reached_max_records();

$test = sub{};
};

Then call &$test() as needed;

R.
--
Richard Nuttall
Nuttall Consulting
www.nuttall.uk.net


Dan Brook

unread,
Jan 14, 2004, 9:31:46 AM1/14/04
to perl6-l...@perl.org
On Tue, 13 Jan 2004 20:37:21 -0800
David Storrs <dst...@dstorrs.com> wrote:

> Given this code:
>
> if ( some_expensive_lookup_function() >= $MAX_RECORDS ) {
> mark_that_we_have_reached_max_records();
> return;
> }
>
> After I enter that block once, I never want to evaluate the condition
> again--I want the code to completely disappear from the bytecode (or,
> at least, be jumped around). How would I do that in Perl 6?

Another approach would be to just memoize the subroutine. Thanks to the
spiffy new subroutine signature syntax it's absurdly easy e.g

sub some_expensive_lookup_function() is cached { ... }

That's side-stepping the implications of memoizing a subroutine of
course, but it does save maintaining state in user-level code.

Dan

Luke Palmer

unread,
Jan 14, 2004, 10:39:46 AM1/14/04
to Jonathan Scott Duff
Jonathan Scott Duff writes:

> On Tue, Jan 13, 2004 at 10:16:48PM -0700, Luke Palmer wrote:
> > So, let's say you have something like:
> >
> > $x = 100_000;
> > my $seen;
> > while $x --> 0 {
>
> Don't do that! I had to look at this twice before I decided that
> perl6 didn't get a new operator :)

That was Damian's favorite trick for a while. I liked it and adopted
it. :-)

> > [ loop unroll + currying example elided ]
>
> Not sure I like the currying, but if that's what works, so be it.

I've been trying to stress this as an undertone in all my posts about
optimization: Perl isn't going to optimize for you! It's going to
help, but if you want real speed, you're probably going to have to work
for it. Computers just aren't smart enough to do it for you --
especially in a language like Perl.

In fact, I'd like it to stay back and let me decide what needed speed.
If I care enough, I'll tell it. I remember a project I did recently
that made extensive use of backward chaining. For instance, you'd have:

rule '$age' => sub {
print "What is your age? ";
$age = <STDIN>;
}
rule '$main' => sub {
$age; # Force the backward chain
$main = "done";
}

But the line with just $age got optimized away by Perl, much to my
discontent, and I was unable to disable this "feature". So I ended up
saying:

sub null { }
...
$age && null;

To force the evaluation. This I consider a hack, and would rather that
Perl 6 not force me to do this. I don't mind something like a

no optimize;

Though.

</rant>

Luke

Melvin Smith

unread,
Jan 14, 2004, 10:59:52 AM1/14/04
to Luke Palmer, The Perl6 Language List
At 10:16 PM 1/13/2004 -0700, Luke Palmer wrote:
>David Storrs writes:
> > Given this code:
> >
> > if ( some_expensive_lookup_function() >= $MAX_RECORDS ) {
> > mark_that_we_have_reached_max_records();
> > return;
> > }
> >
> > After I enter that block once, I never want to evaluate the condition
> > again--I want the code to completely disappear from the bytecode (or,
> > at least, be jumped around). How would I do that in Perl 6?
>
>Hmm...
>
> my $max_reached;
> sub mark_that_we_have_reached_max_records() {
> $max_reached = 1;
> }

If you use a hint in Perl6 to tell Parrot that $max_reached needs
to be a native type you'll probably get near C-speed conditional tests with
the JIT. (The JIT is many x faster than current Perl5 conditionals, but the
reason we don't rave about it more often is that languages like Perl6
lose a lot of what the JIT provides since we have to create most variables
as PMCs)

I think Perl6 will allow a hint like so:

my int $max_reached;

The important thing is that $max_reached is used simply as a conditional,
and you don't pass it to a routine or otherwise use it in a way to cause it
to be
promoted to a PMC (which is much heavier than a native). Currently the back-end
compiler (imcc) doesn't coerce native types to a PMC, but it will eventually.
If you use a hint, and simply set and test the conditional, the compiler should
be able to allocate the conditional as a pure integer register, test it,
and skip the
PMC promotion.

Then your worry about getting the test removed from the bytecode would
be needless as you'd probably only waste a couple of JITed CPU cycles.

-Melvin


David Storrs

unread,
Jan 14, 2004, 12:28:36 PM1/14/04
to The Perl6 Language List
On Tue, Jan 13, 2004 at 10:16:48PM -0700, Luke Palmer wrote:

> sub mark_that_we_have_reached_max_records() {
> $max_reached = 1;
> }
>
> if !$max_reached && some_expensive_lookup_function() > $MAX_RECORDS {
> mark_that_we_have_reached_max_records();
> return;
> }
>
> That's a new feature we're adding called "conditional code removal",
> sometimes known as "short circuiting" :-)

Well, not what I asked (the block is not jumped around, just
shortcircuited) and doesn't really address the principle (i.e., how to
manipulate the bytecode at runtime) I was trying to grok, but ok.


[...]


> This could well be something the optimizer does, but assuming otherwise,
> optimized currying could be to our benefit.
>
> sub do_the_loop($x, $seen) {
> while $x --> 0 {
> if $seen || cheap_condition() {
> something();
> }
> last if !$seen && other_cheap_condition();
> }
> return $x;
> }
>
> &do_the_loop.assuming(seen => 1).(
> &do_the_loop.assuming(seen => 0).(100_000));
>
> If curries are subject to a short constant-folding pass, this should
> easily give us the effect we want.


Had to study this for a bit to assure myself it all worked, but cool!
This is exactly what I was looking for. Thanks.


> > (I recall seeing something about how to make assertions drop out, but
> > I want to be able to do this at run-time, not compile-time.)
>
> As uri said, who can tell the difference?

Point.

--Dks

David Storrs

unread,
Jan 14, 2004, 12:35:12 PM1/14/04
to The Perl6 Language List
On Wed, Jan 14, 2004 at 11:57:05AM +0000, Richard Nuttall wrote:

> How about
>
> $test = sub
> {
> if ( some_expensive_lookup_function() >= $MAX_RECORDS )
>
> mark_that_we_have_reached_max_records();
>
> $test = sub{};
> };
>
> Then call &$test() as needed;


Neat. I wouldn't have thought of that; thank you.

--Dks

David Storrs

unread,
Jan 14, 2004, 12:31:43 PM1/14/04
to The Perl6 Language List
On Wed, Jan 14, 2004 at 10:59:52AM -0500, Melvin Smith wrote:

> I think Perl6 will allow a hint like so:
>
> my int $max_reached;
>
> The important thing is that $max_reached is used simply as a conditional,
> and you don't pass it to a routine or otherwise use it in a way to cause it
> to be promoted to a PMC

What uses would cause it to be promoted to a PMC?

--Dks

Melvin Smith

unread,
Jan 14, 2004, 12:48:11 PM1/14/04
to David Storrs, The Perl6 Language List

Any use that doesn't allow a native type. Currently: passing it to a
non-prototyped
routine or storing it into a hash/list might trigger this.

It depends on how well the optimizer handles hints. Since the optimizer could
decide it only needs to create a temporary PMC for the uses in question,
but leave the base variable as a native. If the optimizer is too aggressive it
might decide that it is less expensive to just declare it as a PMC, so that is
where we have to decide how serious we take hints. :)

If the hint we are talking about becomes a directive, then the compiler just
does what it is told. Any expression needing a coercion will get a temporary
created, but all simple uses of $max_reached (increment, evaluation,
conditional
tests) will still work with a native int register, and this is probably
what you
want in this case.

On the flip side, for a standard declaration: my $max_reached; (no hint)
it should be possible with data-dependency analysis to decide $max_reached
should be a native, without the hint. We shall see.

-Melvin

Luke Palmer

unread,
Jan 14, 2004, 1:02:18 PM1/14/04
to The Perl6 Language List

Let's not mention that that has way more overhead than a
short-circuiting test, but I guess "the idea's the important thing".

Slight correction here,

&$test()

will not call that sub. Both of:

$test()
$test.()

Will, and I think

&$test.()

Will, but &$test() just refers to the sub object itself.

Which brings up a question: Can you have a sub reference to a multi?
That is to say, one reference referring to a multiply-dispatched name.
For example:

multi sub typrint(Int $x) { print "Int $x" }
multi sub typrint(Str $x) { print "Str $x" }

my $ref = &typrint;

$ref.(99); # Int 99
$ref.("Hello"); # Str Hello

?

Luke

Luke

> --Dks

A. Pagaltzis

unread,
Jan 14, 2004, 3:57:37 PM1/14/04
to The Perl6 Language List
* Luke Palmer <fibo...@babylonia.flatirons.org> [2004-01-14 19:03]:

> Let's not mention that that has way more overhead than a
> short-circuiting test, but I guess "the idea's the important
> thing".

How about a calculated goto? *grin*

--
Regards,
Aristotle

"If you can't laugh at yourself, you don't take life seriously enough."

0 new messages