You won't believe this one quick Perl 6 optimization hack!

Now that I've alienated half the crowd, here's the scoop. Perl6::Parser has a fairly extensive test suite, which I run on my laptop inside an Ubuntu 14.04 VM - it's the latest version I can find that supports seamless integration, though I'm considering completely dumping the GUI and going with just a few SSH connections.

But enough of that. Baseline Rakudo Perl 6 currently runs the Perl6::Parser test suite in 0.61s usr, 0.07s sys + 80s cusr time, 90 wallclock seconds, which is rather amazing considering how extensive and invasive the test suite is. After this one simple change, that time dropped from 90 wallclock seconds to 70 wallclock seconds.

If you look at Perl6::Parser::Factory at this version, you'll see a 'role Matchable', which is public and shouldn't be.

Change that to 'my role Matchable' - it won't change the semantics of the code, and keeping scopes narrow is good for programmer sanity. Next, this role contains essentially a bunch of constructors under different names. 'from-int', for example, takes the offset into the string where a match appears, the text of the match, and returns a blessed object, like you might in Perl 5 OO. If you'll look for 'Matchable' later on in this file, you'll see this role get used in a bunch of places.

Some of the classes that need the Matchable role use this constructor method ('from-int',) but most don't. Moving the 'from-int' method out into its own role ("my role Constructor-from-int { method from-int(...) }") and only importing the role ("also does Constructor-from-int;") in the classes where it's needed was the other half of the equation.

From:
role Matchable {
  method from-int(...) {
    self.bless(...)
  }
  method from-match(...) {
    self.bless(...)
  }
}

class Perl6::Semicolon {
  does Matchable;
}
To: (trimming the role to just export what's needed)
my role Constructor-from-int {
  method from-int(...) {
    self.bless(...)
  }
}

class Perl6::Semicolon {
  also does Constructor-from-int;
}

apparently saved quite a bit of wallclock time. Since I'm running Linux on a VM on a Windows 10 laptop, all of these timings are somewhat suspect, but after this change things went from 80 +/- 5 seconds to 60 +/- 5 seconds, so I don't think the change is illusory.

I'm going to finish this job up mainly because I want to see which constructors are used in which classes, and how I can do a better job arranging roles. It's also a prequel to seeing if refactoring out classes back into files kills performance on the VM like it did last time.

In passing you'll notice that while the GitHub file (Perl6/Parser/Factory.pm6) does contain the actual Perl6::Parser::Factory class, it also contains pretty much every other class that it needs. Most of the classes are pretty much empty, and except for documentation purposes it's silly to have them in separate files. Also, and more importantly, when I asked Rakudo to load all .. well, at last count - 135 - files into the compiler, it... didn't like it very much. Moving out one file saw my walllclock time shoot up by 2 seconds. Hopefully that's now been fixed, and now that I actually want to document these classes separately I *will* move these back out into files.

Leave a comment

About DrForr

user-pic I blog about Perl.