MUD on speed Marco Fontani – Glasgow.pm - 2011-05-12
MUD on speed A journey I failed to talk about yet, about Perl, MUDs, strings, Gentoos, and other funny characters. And some who think they are funny.
Back in '99... Multi User Dungeon, written in C
~20 italian users logged in most evenings
V2, rewrite - still in C
V3, attempted rewrite in C++ merging code from another developer who joined
Came to Scotland.. no v4 for a while.
Anacronia v4 Perl, Moose, POE
speed/workflow fake user tests
Handled ~20/30 (fake) users constantly submitting output-intensive commands (admittedy with zlib compression)
A select loop in 1999 handled more
Anacronia v4 – second attempt Class::XSAccessor, EV, AnyEvent, Gearman
speed/workflow fake user tests
Now handles a hell of a lot of users, asynchronously, making db connections, using background tasks, etc.
But let's not get ahead of ourselves
NYTProf Back when the MUD ran on POE, I profiled it Since most of the time the fake users were receiving output, it's sensible to expect time to be mostly spent on the output subs Nay, it was spent mostly on  one  sub The others came way after it
MUD ANSI encoding People liked colours in 1999, and they still do They didn't like raw ANSI, and they still do not \e[31mRED\e[30mreset vs. &rRED&w
MUD ANSI encoding The letters are x, r, g, y, b, p, c, w for black/reset, red, green, yellow and so on Uppercase makes bold, etc. --^p&WA&yn&Wa&yc&Wr&yo&Wn&yi&Wa &CV4&^--
From &r to \e[31m sub ansify { my ($string, $previous_ansi_state) = @_; # … my @str = unpack('C*',$string); for my $i ( 0 .. $#str ) { # …; if after a '&' then check/get char color my $newcol =  getcolor ($str[$i]); # … construct ansi, blah blah
Sub getcolor, rev. 1 { my @colors = map { ord } qw/x r g y b p c w/; sub perl_getcolor { my $clrchar = shift; for ( 0 .. $#colors ) { return $_ if ( $clrchar == $colors[$_] ); return $_ if ( ( $clrchar + 32 ) == $colors[$_] ); } return 255; } } # Pure Perl version  34262/s
Sub getcolor, rev. 2 { # -funroll-loops for the Gentooist enthusiasts sub opt_perl_getcolor { $_[0] == 120 && return 0;  $_[0] == 88  && return 0; $_[0] == 114 && return 1;  $_[0] == 82  && return 1; $_[0] == 103 && return 2;  $_[0] == 71  && return 2; $_[0] == 121 && return 3;  $_[0] == 89  && return 3; $_[0] == 98  && return 4;  $_[0] == 66  && return 4; $_[0] == 112 && return 5  $_[0] == 80  && return 5; $_[0] == 99  && return 6;  $_[0] == 67  && return 6; $_[0] == 119 && return 7;  $_[0] == 87  && return 7; return 255; } } # 98814/s, ~3x faster
The MUD version When I find my code in tons of trouble, Friends and colleagues come to me, Speaking words of wisdom: "Write in C."
The MUD version Inline::C, Inline::C Inline::C, oh, Inline::C. Pure Perl won't quite cut it. Inline::C.

Anacronia on speed

  • 1.
    MUD on speedMarco Fontani – Glasgow.pm - 2011-05-12
  • 2.
    MUD on speedA journey I failed to talk about yet, about Perl, MUDs, strings, Gentoos, and other funny characters. And some who think they are funny.
  • 3.
    Back in '99...Multi User Dungeon, written in C
  • 4.
    ~20 italian userslogged in most evenings
  • 5.
    V2, rewrite -still in C
  • 6.
    V3, attempted rewritein C++ merging code from another developer who joined
  • 7.
    Came to Scotland..no v4 for a while.
  • 8.
  • 9.
  • 10.
    Handled ~20/30 (fake)users constantly submitting output-intensive commands (admittedy with zlib compression)
  • 11.
    A select loopin 1999 handled more
  • 12.
    Anacronia v4 –second attempt Class::XSAccessor, EV, AnyEvent, Gearman
  • 13.
  • 14.
    Now handles ahell of a lot of users, asynchronously, making db connections, using background tasks, etc.
  • 15.
    But let's notget ahead of ourselves
  • 16.
    NYTProf Back whenthe MUD ran on POE, I profiled it Since most of the time the fake users were receiving output, it's sensible to expect time to be mostly spent on the output subs Nay, it was spent mostly on one sub The others came way after it
  • 17.
    MUD ANSI encodingPeople liked colours in 1999, and they still do They didn't like raw ANSI, and they still do not \e[31mRED\e[30mreset vs. &rRED&w
  • 18.
    MUD ANSI encodingThe letters are x, r, g, y, b, p, c, w for black/reset, red, green, yellow and so on Uppercase makes bold, etc. --^p&WA&yn&Wa&yc&Wr&yo&Wn&yi&Wa &CV4&^--
  • 19.
    From &r to\e[31m sub ansify { my ($string, $previous_ansi_state) = @_; # … my @str = unpack('C*',$string); for my $i ( 0 .. $#str ) { # …; if after a '&' then check/get char color my $newcol = getcolor ($str[$i]); # … construct ansi, blah blah
  • 20.
    Sub getcolor, rev.1 { my @colors = map { ord } qw/x r g y b p c w/; sub perl_getcolor { my $clrchar = shift; for ( 0 .. $#colors ) { return $_ if ( $clrchar == $colors[$_] ); return $_ if ( ( $clrchar + 32 ) == $colors[$_] ); } return 255; } } # Pure Perl version 34262/s
  • 21.
    Sub getcolor, rev.2 { # -funroll-loops for the Gentooist enthusiasts sub opt_perl_getcolor { $_[0] == 120 && return 0; $_[0] == 88 && return 0; $_[0] == 114 && return 1; $_[0] == 82 && return 1; $_[0] == 103 && return 2; $_[0] == 71 && return 2; $_[0] == 121 && return 3; $_[0] == 89 && return 3; $_[0] == 98 && return 4; $_[0] == 66 && return 4; $_[0] == 112 && return 5 $_[0] == 80 && return 5; $_[0] == 99 && return 6; $_[0] == 67 && return 6; $_[0] == 119 && return 7; $_[0] == 87 && return 7; return 255; } } # 98814/s, ~3x faster
  • 22.
    The MUD versionWhen I find my code in tons of trouble, Friends and colleagues come to me, Speaking words of wisdom: "Write in C."
  • 23.
    The MUD versionInline::C, Inline::C Inline::C, oh, Inline::C. Pure Perl won't quite cut it. Inline::C.
  • 24.
    Sub getcolor, “live”use Inline C => <<'END_INLINE_C'; unsigned char __colors[8] = &quot;xrgybpcw&quot;; unsigned char getcolor(unsigned char clrchar) { char i = 0; for ( i = 0; i < 8; i++ ) { if ( clrchar == __colors[i] ) { return i; } if ( ( clrchar+32) == __colors[i] ) { return i; } } return (unsigned char) -1; } END_INLINE_C # Inline::C version 337079/s # 10x faster than the pure-perl one
  • 25.
  • 26.
  • 27.
  • 28.
    That's all folks!I blogged about it, and got some suggestions: Use hash lookups
  • 29.
  • 30.
    Use (much faster)array lookups
  • 31.
  • 32.
    Use map, joinand quotemeta
  • 33.
    -funroll-loops the Inline::Cversion (that was me!)
  • 34.
    Who wins? http://darkpan.com/files/ansi--inline-c--vs--perl.pl.txt Rate Pure Perl Opt Perl Hash Perl Lookup Perl Inline::C Opt Inline::C Pure Perl 35294/s -- -67% -76% -80% -90% -91% Opt Perl 106157/s 201% -- -27% -39% -71% -74% Hash Perl 146056/s 314% 38% -- -16% -60% -65% Lookup Perl 174216/s 394% 64% 19% -- -52% -58% Inline::C 363196/s 929% 242% 149% 108% -- -12% Opt Inline::C 412088/s 1068% 288% 182% 137% 13% -- The array lookup is indeed only ½ the speed of Inline::C but Pure Perl The unrolled loops Inline::C version is made of win, though ;)
  • 35.
    There is morethan one way to shave a yak
  • 36.
  • 37.
    “You could've simplymemoized the conversions” use Memoize; memoize('sub_name');
  • 38.
  • 39.
    It's designed forhelping with similar problems
  • 40.
  • 41.
    Marco, why don'tyou use Memoize? In this case, because it sucks.
  • 42.
    Rate Memoized PurePerl Pure Perl
  • 43.
    Memoized Pure Perl15012/s -- -54%
  • 44.
    Pure Perl 32573/s 117% --