New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Many improvements to smartmatching and given/when #4653
Conversation
PR resolves Raku/problem-solving#297. Fixes #2676 and, perhaps, a few more issues (yet to be investigated). |
A couple of implementation notes. To get junctions work as topics with As I have noticed in the commit comment, |
Turning this into draft because there is some more work in progress on optimizing |
0e1efd8
to
bbbef1b
Compare
e86ccf0
to
572c08c
Compare
I think I'm done with this. The PR passes spectests, including the additions from Raku/roast#774. Aside of the fix for junctions on LHS and as sub foo(--> Num) { ... }
if foo() ~~ Numeric { ... } Note that it means no call to Overall, the basic principles of optimization I tried to follow were:
Because the above involves a lot of analyzing of value ASTs, I added a new class Unfortunately, I barely can provide many benchmark results as dispatching is seemingly taking so good care of the cases I tried, that sometimes a test loop happens to be even faster, than a base-line loop with no smartmatch in it. But at least in one case of smartmatching against pairs I did observe a speedup of 49%: constant COUNT=1_000_000;
my $base = 0;
my $s = now;
for ^COUNT {
my $v = $foo.bar1 === True;
}
$base = now - $s;
note "Base: ", $base;
my $total = 0;
$s = now;
for ^COUNT {
my $v = $foo ~~ :bar1;
}
$total = now - $s;
note "Total: ", $total, ", actual: ", $total - $base; |
Update. With JVM fixed I've benchmarked it too. 88% for pair, 92% for |
- `when` now uses optimized code unless the topic is a Junction. Falls back to `ACCEPTS`+`Bool` in the latter case. - smartmatch optimization is now more elaborate about its arguments. It implicitly `Bool`-ifies `ACCEPTS` return value unless its LHS is a regex or negation is to be done. Overall, with this commit it must not be a problem if `ACCEPTS` gets autothreaded and results in a junctionized return. Either it will be collapsed with boolification where smartmatch semantics is expected, or junctionized outcome of a regex match is expected.
It would manually thread over topic if it's a junction. The default `ACCEPTS` method now expects `Any`-typed topic. Since we don't have `cmp` operator for `Mu`, `Range`, in order to handle any user-created direct `Mu` child, tries to find user-defined `&infix:<cmp>` and throws with `X::Range::Incomparable` if can't find any appropriate. There are still two problems here. First, there is no support for custom user `cmp` operator for `Any`-inheriting classes. Second, custom operator couldn't be found for `Mu`-inheriting classes if they're part of a junction. In the latter case the problem is about the fact that `CLIENT::` namespace would point into `Junction` class. Both issues can only be fixed at the cost of performance loss which is barely acceptable considering the wide use of ranges across the codebase.
It is expected to return the Match instance itself no matter what argument it is given.
It doesn't have a candidate for non-`Any` children, thus declaring the proto with `Any` as the first positional makes full sense.
The return value must be a `Bool`.
This is only related to typematching kind of method. Since all versions of ACCEPTS methods in core doing this are reducable to simple `nqp::istype` we can still use this optimization. But if a non-core class introduces its own version we must use it for smartmatching.
- Moved `when` optimization from Actions into Optimizer - Unified `when` and smartmatch optimiztions - Extended optimizations of some static cases For example, reducing typematching to `nqp::istype` if `ACCEPTS` method is not overriden or no new candidates added by user code - Added class `Operand` which does its best to provide information about an expression operand
Topicalization is not needed in typematch case if RHS uses the default ACCEPTS method. Also, when we know variable/routine type and can ensure it cannot be a Regexp then the code can be simplified to just `ACCEPTS`+`Bool` method calls. Aside of this, smartmatch optimizator depends on operand nodes to be pre-optimized. This lets it do better analysis. But it was also resulting in incorrect statistics collected about localizable variables whenever the smartmatching node is replaced entirely. Fixing this required a new approach with two-pass optimization where the first pass is done without collecting the variable statistics and localization pass. The statistic collection is now prevented by wrapping the `@!block_var_stack` attribute into a specialized `BlockVarStack` class. All method calls used to be done on the stack top element must now be made via `BlockVarStack` `do` method. The class supports dry run mode when method invocations via `do` are ignored. Smartmatch optimizer uses dry run for first-pass optimization of its operands. Some code re-arrangements took place to improve re-usability.
Use compile-time known setting-only `Pair` typeobject.
Not that I mentioned any visible speedup on core compilation, but it doesn't hurt to have it. It definitely spares a couple of CPU cycles here and there.
The outcome would significantly vary depending on the actual code, but in the most typical case of `$obj ~~ :foo` I have observed a speedup of up to 49%.
Null pointer exception again.
As a follow up to an earlier merged PR rakudo#4687
717b568
to
d0f2690
Compare
when
topicwhen
now uses optimized code unless the topic is a Junction. Falls back toACCEPTS
+Bool
in the latter case.Bool
-ifiesACCEPTS
return value unless its LHS is a regex or negation is to be done.Overall, with this PR it must not be a problem if
ACCEPTS
gets autothreaded and results in a junctionized return. Either it will be collapsed with boolification where smartmatch semantics is expected, or junctionized outcome of a regex match is expected.