Skip to content

Commit

Permalink
Optimize supply emit <arg>
Browse files Browse the repository at this point in the history
This is a convenient way of getting a Supply that will simply emit a
single argument and then be done (or quit if the process of producing
the argument throws an exception). Given the program `my $s = supply
emit 42; for ^100_000 { react whenever $s {  } }`, this results in it
running in 78% of the time it would without this optimization.
  • Loading branch information
jnthn committed Jan 11, 2018
1 parent 55e5639 commit d41accc
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 1 deletion.
20 changes: 20 additions & 0 deletions src/Perl6/Actions.nqp
Expand Up @@ -2246,7 +2246,27 @@ class Perl6::Actions is HLL::Actions does STDActions {
}

method statement_prefix:sym<supply>($/) {
# Case-analyze what's inside of the Supply, to spot cases that can be
# turned into something cheap rather than needing the whole supply
# concurrency control mechanism.
my $past := $<blorst>.ast;
my $block := $past.ann('past_block');
if $*WHENEVER_COUNT == 0 {
my $stmts := $block[1];
if nqp::istype($stmts, QAST::Stmts) && nqp::elems($stmts) == 1 {
my $stmt := $stmts[0];
$stmt := $stmt[0] if nqp::istype($stmt, QAST::Want);
if nqp::istype($stmt, QAST::Op) && $stmt.op eq 'call' && $stmt.name eq '&emit'
&& nqp::elems($stmt.list) == 1 {
# Single statement emit; make block just return the expression
# (or die) and pass it to something that'll cheaply do a one
# shot emit.
$stmts[0] := $stmt[0];
make QAST::Op.new( :op('call'), :name('&SUPPLY-ONE-EMIT'), $past );
return 1;
}
}
}
$past.ann('past_block').push(QAST::WVal.new( :value($*W.find_symbol(['Nil'])) ));
make QAST::Op.new( :op('call'), :name('&SUPPLY'), $past );
}
Expand Down
32 changes: 31 additions & 1 deletion src/core/Supply.pm
Expand Up @@ -5,7 +5,7 @@ my class Tap {

submethod BUILD(:&!on-close --> Nil) { }

method new(&on-close) {
method new(&on-close = Callable) {
self.bless(:&on-close)
}

Expand Down Expand Up @@ -2005,6 +2005,32 @@ augment class Rakudo::Internals {
method sane(--> True) { }
method serial(--> True) { }
}

class OneEmitTappable does Tappable {
has &!block;

submethod BUILD(:&!block! --> Nil) {}

method tap(&emit, &done, &quit, &tap) {
my int $closed = 0;
my $t = Tap.new;
tap($t);
try {
emit(&!block());
done();
CATCH {
default {
quit($_);
}
}
}
$t
}

method live(--> False) { }
method sane(--> True) { }
method serial(--> True) { }
}
}

sub SUPPLY(&block) {
Expand All @@ -2028,4 +2054,8 @@ sub REACT(&block) {
await $p;
}

sub SUPPLY-ONE-EMIT(&block) {
Supply.new(Rakudo::Internals::OneEmitTappable.new(:&block))
}

# vim: ft=perl6 expandtab sw=4

0 comments on commit d41accc

Please sign in to comment.