This is a grant report by Jonathan Worthington on his grant under Perl 6 Core Development Fund. We thank the TPF sponsors to make this grant possible.
I have completed the second 200 hours of my Perl 6 performance and reliability engineering grant, funded by the Perl 6 core development fund. This report summarizes the work done during those 200 hours. In accordance with community feedback, the vast majority of effort has been put into reliability rather than performance.
The main area of focus in this grant period has been making Perl 6's concurrency support more robust. While work remains to be done, the improvement over the last several months has been noticeable. It is also an important area for me to focus on, given the small number of people in the community with the skills, time, and patience (or, perhaps, stubbornness) to track down and resolve these problems. Here is a summary of the issues resolved.
callwith
in multiple threadss///
construct, which showed
up in concurrent scenarios)accept
could block GC in other threads,
thus blocking program progess)zip-latest
on two intervals would hang)$/
between threads when using grammars
in parallel (mostly fixed RT #128833)Lock.protect
bug when we unwound the stack due to control exceptions
(discovered independently of, but also resolved, RT #126774)Proc::Async
objects and obtaining handles)once
constructPromise
is broken)LAST
/NEXT
/QUIT
phasers in supply
,
react
, whenever
, and for
constructsQUIT
phasers mishandling exceptions thrown synchronously
with the .tap
Supplier::Preserving
on the taps of stdout/stderr in
Proc::Async
, to avoid various innocent-looking usage patterns losing outputsupply
block even after it was
considered done)Proc::Async
that caused occasional crashessupply
/whenever
implementation
and in Supply.interval
Supply.interval(...)
with very small interval would only
ever emit 1 value)Inline::Perl6
OO::Monitors
module>>.foo
implementation||=
, &&=
, and //=
, making them a good bit more
efficient along the wayp6doc
builds)Previously, decoding of bytes to strings for both IO::Socket::Async
and
Proc::Async
was done at the VM level. This created a number of fragilities
with regard to decoding errors. Due to time constraints, different encodings
besides UTF-8 had not been implemented for these classes either, leaving users
of them to do decoding manually if they needed anything else.
To rectify these issues, I first made the VM-backed decoders directly available to userland. These will, in the future, be exposed as a Perl 6-level API, and we'll support user-space encodings. For now, it meant I could move the code that orchestrates the decoding of strings in async I/O into Perl 6 space, fixing the robustness issues. This also means that string decoding for different spawned processes and socket connections can be done in the thread pool, rather than using the event-processing thread. Along the way, I added support for different encodings.
Finally, there were some issues around the way async sockets and processes worked with regard to NFG. I resolved these issues and made sure there was test coverage of the various edge cases.
I did the initial round of work to provide support for non-blocking await
and
react
. At present, these constructs will block a real OS thread, even if used
on a thread in the thread pool. The changes, available via. use v6.d.PREVIEW
,
mean that thread-pool threads will be returned to the thread pool to do other
work, and the code following the await
and react
will be scheduled once the
result is available or processing is complete. This is implemented using
continuations (much like gather
/take
, except in this case the continuation
may be resumed on a different OS thread). The result is that Perl 6 programs
will be able to have hundreds or thousands of outstanding react
s and await
s,
with just a handful of real OS-threads required to process them.
This is just the initial implementation; further work will be required to make this feature ready to be the default in Perl 6.d.
The highlight of the memory management improvements was a simplification to the lifetime management of register working sets in MoarVM. This resulted from the elimination of a couple of speculative features that were not yet being utilized by Rakudo, and in one case never would have been anyway. Coupled with a range of cleanups and some code streamlining, the result was a 10% reduction in peak memory use for CORE.setting compilation, and 20% off the compilation runtime. I also:
Proc::Async
perl6-valgrind-m -e ''
, meaning it is now clean. (Previously, some cleanup
was missed at VM shutdown)I did a number of Unicode improvements, as well as discussing with and reviewing Pull Requests from a new contributor who is now doing a bunch of Unicode work for Perl 6. My own contributions code wise were:
I also took care of a range of other bugs, which don't fit into any of the previously mentioned areas of work.
sprintf
and friends)/$<cat>=@(...)/
)my $!a
)nqp::exception()
op and fixed itnread == 0
as an error in a couple of
places in MoarVM)TOP
in a grammar to try and
make it match until the end of the string)$a++
in sink context for native $a
was a lot
slower than in non-sink context)$/
setting, arising from changes to
the implementation of match
On top of this, some time was spent reviewing pull requests to Rakudo, NQP, and MoarVM, providing feedback, and merging them when appropriate. I also commented on a range of RT tickets besides those I fixed myself. Various other small cleanups and additions resulted from this work, ranging from typo fixes in comments up to improvements to GC debugging macros added while finding bugs.
I noticed a definite reliability improvement. I used to frequently restart my long-running production apps, but no more. They're stable both in not-crashing sense and in not-nomming-tons-of-RAM sense.