Perl 6 Is Slower Than My Fat Momma!

I notice several groups of people: folks who wish Perl 6's performance weren't mentioned; folks who are confused about Perl 6's perfomance; folks who gleefully chuckle at Perl 6's performance, reassured the threat to their favourite language XYZ hasn't arrived yet.

So I'm here to talk about the elephant in the room and get the first group out of hiding and more at ease, I'll explain things to the second group, and to the third group... well, this post isn't about them.

Why is it slow?

The simplest answer: Perl 6 is brand new. It's not the next Perl, but a brand new language in the Perl family. The language spec was finished less than 4 months ago (Dec 25, 2015). While some optimization has been done, the core team focused on getting things right first. It's simply unrealistic to evaluate Perl 6's performance as that of an extremely polished product at this time.

The second part of the answer: Perl 6 is big. It's easy to come up with a couple of one-liners that are much faster in other languages. However, a Perl 6 one-liner loads the comprehensive object model, list tools, set tools, large arsenal of async and concurrency tools... When in a real program you have to load a dozen of modules in language XYZ, but can still stay with bare Perl 6 to get same features, that's when performance starts to even out.

What can you do about it?

Now that we got things right, we can focus on making them fast. Perl 6 uses a modern compiler, so in theory it can be optimized quite a lot. It remains to be seen whether theory will match reality, but looking through numerous optimization commits made since the start of 2016, many stand out by the boosts they bring in:

Thus, the answer is: we're working on it... and we're making good progress.

What can I do about it?

I'll mention three main things to keep in mind when trying to get your code to perform better: pre-compilation, native types, and of course, concurrency.

Pre-Compilation

Currently, a large chunk of slowness you may notice comes from parsing and compiling code. Luckily, Perl 6 automagically pre-compiles modules, as can be seen here, with a large Foo.pm6 module I'm including:

$ perl6 -I. -MFoo --stagestats -e ''
Stage start      :   0.000
Stage parse      :   4.262
Stage syntaxcheck:   0.000
Stage ast        :   0.000
Stage optimize   :   0.002
Stage mast       :   0.013
Stage mbc        :   0.000
Stage moar       :   0.000

$ perl6 -I. -MFoo --stagestats -e ''
Stage start      :   0.000
Stage parse      :   0.413
Stage syntaxcheck:   0.000
Stage ast        :   0.000
Stage optimize   :   0.002
Stage mast       :   0.013
Stage mbc        :   0.000
Stage moar       :   0.000

The first run was a full run that pre-compiled my module, but the second one already had the pre-compiled Foo.pm6 available and the parse stage went down from 4.262 seconds to 0.413: a 1031% start-up improvement.

Modules you install from the ecosystem get pre-compiled during installation, so you don't have to worry about them. When writing your own modules, however, they will be automatically re-pre-compiled every time you change their code. If you make a change before each time you run the program, it's easy to get the impression your code is not performing well, even though the compilation penalty won't affect the program once you're done tinkering with it.

Just keep that in mind.

Native Types

Perl 6 has several "native" machine types that can offer performance boosts in some cases:

my Int $x = 0;
$x++ while $x < 30000000;
say now - INIT now;

# OUTPUT:
# 4.416726

my int $x = 0;
$x++ while $x < 30000000;
say now - INIT now;

# OUTPUT:
# 0.1711660

That's a 2580% boost we achieved by simply switching our counter to a native int type.

The available types are: int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, num, num32, and num64. The number in the type name signifies the available bits, with the numberless types being platform-dependent.

They aren't a magical solution to every problem, and won't offer huge improvements in every case, but keep them in mind and look out for cases where they can be used.

Concurrency

Perl 6 makes it extremely easy to utilize multi-core CPUs using high-level APIs like Promises, Supplies, and Channels. Where language XYZ is fast, but lacks ease of concurrency, Perl 6 can end up the winner in peformance by distributing work over multiple cores.

I won't go into details—you can consult the documentation or watch my talk that mentions them (slides here). I will show an example, though:

await (
    start { say "One!";   sleep 1; },
    start { say "Two!";   sleep 1; },
    start { say "Three!"; sleep 1; },
);
say now - INIT now;

# OUTPUT:
# One!
# Three!
# Two!
# 1.00665192

We use the start keyword to create three Promises and then use the await keyword to wait for all of them to complete. Inside our Promises, we print out a string and then sleep for at least one second.

The result? Our program has three operations that take at least 1 second each, yet the total runtime was just above 1 second. From the output, we can see it's not in order, suggesting code was executed on multiple cores.

That was quite easy, but we can crank it up a notch and use a HyperSeq to transform ordinary code into concurrent code with a single method call:

for (1..4).race( batch => 1 ) {
    say "Doing $_";
    sleep 1;
}
say "Code took {now - INIT now} seconds to run";

# OUTPUT:
# Doing 1
# Doing 3
# Doing 2
# Doing 4
# Code took 1.0090415 seconds to run

We had a list of 4 items to work with. We looped over each of them and performed an expensive operation (in this case, a 1-second sleep). To modify our code to be faster, we simply called the .race method on our list of 4 items to get a Hyper Sequence. Our loop remains the same, but it's now executing in a concurrent manner, as can be seen from the output: items are out of order and our total runtime was just over 1 second, despite a total of 4 seconds of sleep.

If the default batch size of 64 is suitable for you, it means you can go from a plain loop to a concurrent loop by simply typing 5 characters (. r a c e).

Let's See Some Benchmarks

I won't show you any. There's hardly any sense in benchmarking entire languages. Clever one-liners can be written to support one point of view or another, but they simply abstract a problem into a simplistic singularity. Languages are different and they have vastly different tool kits to solve similar problems. Would you choose code that completes in 1 second and takes you 40 minutes to write or code that completes in 2 seconds, yet takes you 10 minutes to write? The choice depends on the type of application you're writing.

Conclusion

Perl 6 is a brand new product, so it doesn't make sense to compare it against software that existed for decades. It is being actively improved and, at least in theory, it should become performant on the level similar to other competing languages.

You don't have to wait for that to happen, however. Thanks to Perl 6's pre-compilation of modules, support of native types, and superb concurrency primitives you can substantially improve the performance of your code right now.

Some may disagree that Perl 6 is slow, some may find it faster than another language, and some may say Perl 6 is slower than my fat momma.

Who's to decide for you? Only you yourself can.

3 Comments

I'm glad you seem really passionate about Perl6 but I'm not clear after reading this if I understand how some of these problems are being addressed. I think it would be better to have a little more humility here, instead of say, "its slow because its new..." Perl6 is far from new, its been in progress for most of my 20+ years career in IT.

Also I find your addressing a fat momma to be very weird...

I think we'd be better off if we could focus on specifics right? People are saying "Perl6 is slow"... but what does that mean? Slow in comparison to what? We should have some specific benchmarks around this, and discussion around if this is a problem or now, and if so what can be done about it.

Like I remember when Moose first hit the scene, it was really slow. But people bought into the 'you get what you pay for' mantra. And we made it clear, you don't want to use this for non persistent web applications, like CGI, and probably not for scripts and cron jobs that get run a lot where the startup time penalty was a problem. So the Moose community addressed the performance issue by acknowledging it, and offering work around and advice about what projects Moose would be good for and what projects are not good. And Steven Little was very humble about what Moose brought to the table, and his approach was very inviting to people.

So then we also found ways to mitigate some of the worst performance problems, some with the immutable setting, and there was a project to move some Moose stuff to the XS layer. Moose is still slow at startup, but runtime its now pretty fast, close enough to non Moose stuff that if you are willing to pay the startup penalty, performance is not an issue.

I think Perl6 would benefit from that history around how Moose addressed its issues with performance and the early lack of documentation, both are thinks I think Perl6 also has issues with, and you can use Moose as a template for how to address those problems.

So I think the second part of your post, talking about ways to mitigate these problems is spot on and helpful. Just all the talk about how new Perl6 is and how fat your momma is, well that part is confusing and honestly detracted from the real value here. I'd love to see more about async and Perl6 since I think that is going to be the thing that has a good chance to draw more people from Perl5 like myself that are working in performance situations where high concurrency is really important. And I think showing that you can precompile and use types to get great performance wins that is also great. At some point we need some hard core projects to measure this stuff, like a pure Perl6 web server and see how fast it is, and how will it scales with concurrency. Stuff like that I can show my boss and get him excited about Perl6 and maybe I can actually get permission to use it on a real project.

I see, maybe if you'd link to that it would help. Still, I'd consider taking the high road and just focus on what is going to make us awesome, but that's me personally.

FWIW the regex stuff seems pretty fast, at least from my various attempts to hack on DOM::Parser. I just can't figure out all the new regex stuff P6 does :) I wish I could make that work, I'd port my Template::Pure to P6 since its build on that and maybe start to learn the async stuff.

Leave a comment

About Zoffix Znet

user-pic I blog about Perl.