A new general dispatch mechanism to speed up various slow Raku constructs

Jonathan Worthington — 45 minutes

Dispatch is about mapping a set of arguments into some kind of action. For example, when we make a method call, such as `$obj.meth($arg)`, the invocant `$obj`, the name `meth`, and potentially the argument `$arg` are involved in deciding what code we should run. Many things we may not immediately think of as dispatch can also be seen as a case of it. For example, a return value type check can be seen as a dispatch that resolves to the identity function in the case the type is acceptable, or to an exception throw if not.

Dispatch is everywhere, and thus plays a key role in program performance. Therefore, it's typical for runtimes to provide mechanisms that make dispatch more efficient. MoarVM is no exception, from an early stage providing method caching, multiple dispatch caching and unwrapping of high-level code objects to bytecode handles.

These mechanisms do successfully speed up the constructs in question. However, they are highly specific to certain kinds of dispatch. Thus, while standard method dispatch and multiple dispatch on nominal types have long performed decently - especially when further optimizations like inlining get involved - other constructs do far less well. For example, using `callsame` to call a method in a parent class or to defer to the next multiple dispatch candidate or wrapper is comparatively glacial, and the penalty for using `where` clauses on `multi` candidates is also very high.

Teaching the runtime about every kind of dispatch is one option. This approach leads to large quantities of low-level code that is time-consuming and difficult to write. Everything we write in C in MoarVM gives us a chance to make mistakes that are simply not possible when writing code running atop of the runtime.

A more promising approach is to provide a general, programmable, dispatch mechanism. Dispatch logic is written in NQP, a restricted subset of Raku and compiling down to MoarVM bytecode. As well as resolving the dispatch, it can inform the runtime not only of the outcome, but of a set of conditions that, if matched, mean this outcome can be considered valid in the future too. These conditions - known as guards - can be quickly checked by the runtime the next time a dispatch at this program point is done, and - assuming they match - the dispatch can be quickly resolved.

In this session I'll discuss my work on implementing such a general dispatch mechanism in MoarVM and my journey to use it to replace all of the more specific dispatch mechanisms currently in place, as well as to greatly improve the performance of a number of language constructs that have so far been slow.



Talk tags
performance
Target audience
Intermediate
Talk duration
45 minutes
Talk status
accepted