Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are inline if statements an average of at least one-third slower than other types of if?

Consider the following Perl 6 script skeleton:

my regex perlish    { .*[ea]?[ui]? rl $ }
my Str @words = '/usr/share/dict/words'.IO.lines;

for @words -> $word {
    ...
}

base idea for the code in this question from the perl6 website's examples.

My /usr/share/dict/words is an indirect symbolic link to /usr/share/dict/american-english. It's 99,171 lines long, with one word/line.

For comparison's sake, Python 3 does 100 loops of the below in a total of 32 seconds: that's just 0.32942s / loop.1

Here are the things I've tried putting in place of the stub code, with their benchmark times as noted:

  • "Inline" if100 loops, average 9.74219s / loop, totalling 16 min 14.219s

    say "$word probably rhymes with Perl" if $word ~~ /<perlish>/;
    say "$word is a palindrome" if $word eq $word.flip && $word.chars > 1;
    
  • Short Circuit (not ternary) — 10 loops, average 6.1925s / loop, normalised to totalling +/- 10.3 min

    $word eq $word.flip  && $word.chars > 1 && say "$word is a palindrome";
    $word ~~ /<perlish>/ && say "$word probably rhymes with Perl";
    
  • given/when (switch/case) — 100 loops, average 6.18568s / loop totalling 10 min 18.568s

    given $word {
      when /<perlish>/ 
        { say "$word probably rhymes with Perl"; proceed; }
      when $word eq $word.flip && $word.chars > 1 
        { say "$word is a palindrome"; proceed; }
    }
    
  • "normal" if block — 100 loops, average 6.0588s / loop totalling 10 min 5.880s

    if $word eq $word.flip && $word.chars > 1 { say "$word is a palindrome"; }
    if $word ~~ /<perlish>/ { say "$word probably rhymes with Perl"; }
    

Somewhat unsurprisingly, the normal if block is fastest. But, why is the inline if (what the website uses for an example) so much slower?


1 I'm not saying Perl 6 is slow... but I thought Python was slow and... wow. Perl 6 is slow... ignoring multithreading, parallelism and concurrency, all of which are built in by Perl 6 and which Python leaves much to be desired.


Specs: Rakudo version 2015.12-219-gd67cb03 on MoarVM version 2015.12-29-g8079ca5 implementing Perl 6.c on a 2.2GHz QuadCore Intel Mobile i7 with 6GB of RAM.

I ran the tests like time for i in ``seq 0 100``; do perl6 --optimize=3 words.pl6; done.

like image 932
cat Avatar asked Jan 27 '16 02:01

cat


Video Answer


1 Answers

(This page became the p6doc Performance page.)

Dealing with Perl 6 speed issues

I don't know why the statement modifier form of if is slower. But I can share things that can help folk deal with Perl 6 speed issues in general so I'll write about those, listed easiest first. (I mean easiest things for users and potential users to do, not easiest for compiler devs.)

Why does the speed of your code matter?

I recommend you share your answer to these higher level questions:

  • How much faster would your code need to run to make a worthwhile difference? Could the full speed up wait another month? Another year?

  • Are you exploring Perl 6 for fun, assessing its potential long term professional relevance to you, and/or using it in your $dayjob?

Wait for Rakudo to speed up

5 years ago Rakudo was 1,000 times slower or more for some operations. It's been significantly speeding up every year for years even though speeding it up was explicitly not the #1 dev priority. (The mantra has been "make it work, make it work right, make it fast". 2016 is the first year in which the "make it work fast" aspect is truly in the spotlight.)

So, imo, one sensible option if the Rakudo Perl 6 compiler is really too slow for what you want to do, is to wait for others to make it faster for you. It could make sense to wait for the next official release (there's at least several each year) or wait a year or three depending on what you're looking for.

Visit the freenode IRC channel #perl6

Compiler devs, the folk who best know how to speed up Perl 6 code, aren't answering SO questions. But they are generally responsive on #perl6.

If you don't get all the details or results you want from here then your best bet is to join the freenode IRC channel #perl6 and post your code and timings. (See next two headings for how best to do that.)

Profile code snippets

Rakudo on MoarVM has a built in profiler:

$ perl6 --profile -e 'say 1'
1
Writing profiler output to profile-1453879610.91951.html

The --profile option is currently only for micro-analysis -- the output from anything beyond a tiny bit of code will bring your browser to its knees. But it could be used to compare profiles of simple snippets using if conventionally vs as a statement modifier. (Your regex using examples are almost certainly too complex for the current profiler.)

Profiling results may well mean little to you without help and/or may point to confusing internal stuff. If so, please visit #perl6.

Write faster Perl 6 code, line by line

Your immediate focus seems to be the question of why one way of writing a line of code is slower than another way. But the flipside of this "academic" question is the practical one of writing faster lines of code.

But if someone's a Perl 6 newbie, how are they going to know how? Asking here is one way but the recommended approach is visiting #perl6 and letting folk know what you want.

#perl6 has on-channel evalbots that help you and others investigate your issue together. To try code snippets out publicly enter m: your code goes here. To do so privately write /msg camelia m: your code goes here.

For simple timing use variations on the idiom now - INIT now. You can also generate and share --profile results easily using a #perl6 evalbot. Just join the channel and enter prof-m: your code goes here.

Write faster Perl 6 code by refactoring

  • Use better algorithms, especially parallel/concurrent ones.

  • Use native arrays (eg Array[int8] for an array of 8 bit integers) for compact, faster number crunching.

For more info about doing this, visit #perl6.

Use (faster) foreign code

  • Use NativeCall wrappers for C libs such as Gumbo or for C++ libs (experimental). NativeCall itself is currently poorly optimized but that's set to change in 2016 and for many applications the NativeCall overhead is a small part of performance anyway.

  • Inline::Perl5 builds on NativeCall to enable use of Perl 5 in Perl 6 (and vice-versa) including arbitrary Perl 5 code and high-performance Perl 5 XS modules. This interop allows passing integers, strings, arrays, hashes, code references, file handles and objects between Perl 5 and Perl 6; calling methods on Perl 5 objects from Perl 6 and calling methods on Perl 6 objects from Perl 5; and subclassing Perl 5 classes in Perl 6.

(There are similar but less mature or even alpha variants for other langs like Inline::Python, Inline::Lua and Inline::Ruby.)

Review benchmarks

The best relevant benchmarking tool I know of is perl6-bench which compares various versions of Perl with each other including various versions of both Perl 5 and Perl 6.

There may already be benchmarks contrasting a regular if statement and a statement modifier form if statement but I doubt it. (And if not, you would be making a nice contribution to Perl 6 if you wrote an extremely simple pair of snippets and got them added to perl6-bench.)

Help speed Rakudo up

The Rakudo Perl 6 compiler is largely written in Perl 6. So if you can write Perl 6, then you can hack on the compiler, including optimizing any of the large body of existing high-level code that impacts the speed of your code.

Most of the rest of the compiler is written in a small language called NQP that's almost just a subset of Perl 6. So if you can write Perl 6 you can fairly easily learn to use and improve the middle-level NQP code too.

Finally, if low-level C hacking is your idea of fun, checkout MoarVM.

like image 148
raiph Avatar answered Sep 29 '22 19:09

raiph