Showing posts with label optimization. Show all posts
Showing posts with label optimization. Show all posts

2011-09-16

prisk gains its own map format, allowing translations!

till now, games::risk was using map files from jrisk. this allowed me to concentrate on the gui and the game experience without having to tackle everything at once.

being a simple format, it was somehow easy to read the maps... but its simplicity has some drawbacks, and for example it's not possible to translate them. therefore, i finally bit the bullet and implemented a format for prisk. basically, information remains the same (such as the mechanism to determine the current country), with one exception: maps are implemented as perl modules. and this allows the use of gettext and other i18n schemes.

this means that maps are now translatable! french translation is of course provided, but help is needed for other languages... (hint, hint)

of course, i wrote an importer to migrate jrisk maps to new prisk format. i also intend to take this opportunity to create some new perl dists with extra maps.

this prisk release shows also a lot of cleanup in the internals, with a partial migration to moose and moosex::poe. using weak_ref for scalar attributes and a tied hash::noref to cache objects allowed to use circular references without having to deal with their problems. finally, prisk is now deferring some module loading to runtime, leading to a faster startup. the changes are plenty, so now is a good time to give it a try! (after v3.112590 has hit your nearest mirror of course)

future releases will continue to see code cleanups and migration to moose. prisk also finally has a configuration system to save user preferences - i "just" need to use this system throughout the code. some dialogs needs also to migrate to prisk's look&feel (thanks tk::role::dialog), and i have some ideas to improve the artificial intelligences & better use poe. not counting other game modes to take into account... oh well, let's say that prisk will keep me busy quite some time! :-)

2011-02-25

magpie update now waaaaaaay faster!

"magpie update", used to update automatically a perl module rpm to its latest version, was a bit slow. the culprit was parse::cpan::packages, taking a whole 10 seconds to parse 02packages.details.txt.gz

fortunately, i found parse::cpan::packages::fast (from slaven++), which does exactly the same job in less than a second...

so, with a 16-line patch (-2/+2), magpie update now is almost instant. cpan is definitely the home of nice gems, and *the* advantage of perl.

2010-02-09

how to profile a perl program?

so, it seems that google isn't aware about perl profiling best practices... this blog post will thus try to link a lot of times to devel-nytprof, which is the solution to use to profile a perl program.

for profiling perl, just use devel::nytprof. it's an easy to use perl profiler:
$ perl -d:NYTProf my_prog.pl
[... let it run, it will be slower than your usual run ...]
$ nytprofhtml
when this is done, just point your brower to the locally created ./nytprof/index.html and enjoy the nice reports.

this is the best profiler for perl available currently. in case you missed the point: the perl profiler devel-nytprof is great, use it for your perl profiling needs.

2009-06-11

redefining exported subs in perl

i've explained in a previous post that i changed the way i was logging debug statements within language::befunge. i mentioned that i applied some tricks and promised to explain them - so here are the explanations.

the goal is to minimize time spent for debug statements. previously, i was doing:
$interpreter->debug(@stuff);
and debug was a method defined as:
sub debug {
my ($self, @stuff) = @_;
return unless $self->debug_mode;
warn @stuff;
}

so, to log a debug message, i was doing:
  • a method call on $interpreter
  • a second method call to check an attribute
  • finally the actual logging (skipped if we're not in debug mode)
this is bad, especially since method calls cannot be resolved at compile time by perl, and thus are actually resolved during run-time. but what's worse is that this always happens, even if we're not in debug mode (which is around 99% of the time).

so, one obvious way to improve was to move from a method to a plain sub. this would remove the run-time cost of resolving the method. the debug mode can be stored as a package scalar instead of an attribute.

but we can do even better. knowing that:
  • perl optimizes out calls to empty subs
  • we are not in debug mode most of the time
we can define the debug sub as an empty sub!

here's our code at that point:
package Language::Befunge::Debug;

use 5.010;
use strict;
use warnings;

use base qw{ Exporter };
our @EXPORT = qw{ debug };

sub debug {}
of course, we need to provide a way to activate debugging. a naive approach would be to redefine our debug() sub in our debug package:
sub enable {
*debug = sub { warn @_; };
}
alas, this won't work. well, it will work for calls such as:

Language::Befunge::Debug::debug(@stuff);
but calls using exported debug() will still log nothing. indeed, it's important to understand that exporter installs a copy of exported sub in the package. therefore, changing the definition of the original does not change the exported copies.

so, to redefine exported subs, one is forced to walk the symbol table of all packages and redefine subs on the fly. here's what i ended up doing:

my %redef;
sub enable {
%redef = ( debug => sub { warn @_; } );
_redef();
}

sub disable {
%redef = ( debug => sub {} );
_redef();
}

my %orig; # original subs
sub _redef {
my $parent = shift;
if ( not defined $parent ) {
$parent = '::';
foreach my $sub ( keys %redef ) {
$orig{ $sub } = \&$sub;
}
}
no strict 'refs';
no warnings 'redefine';
foreach my $ns ( grep /^\w+::/, keys %{$parent} ) {
$ns = $parent . $ns;
_redef($ns) unless $ns eq '::main::';
foreach my $sub (keys %redef) {
next # before replacing, check that...
unless exists ${$ns}{$sub} # ... named sub exist...
&& \&{ ${$ns}{$sub} } == $orig{$sub}; # ... and refer to the one we want to replace
*{$ns . $sub} = $redef{$sub};
}
}
}
there, it will redefine my sub in all packages, even the ones that hold an exported copy.

now, do you think this would warrant a sub::redefine module on cpan? after all, i found nothing on cpan that would achieve that. otoh, i'm not sure it's that common to do this kind of things... so tell if you're interested, and i'll turn that in a cpan module for your own use.

2009-06-05

some befunge love

i took some time to review my befunge modules. trying to speed up things is always fun, so thanks to devel::nytprof, i saw that i was spending quite some time on my debug statements.

it should be noted that those statements were method calls on the main language::befunge::interpreter object. and the method then was outputing things depending on the value of a debug attribute of the interpreter. the interpreter was not used outside of this.

knowing that method calls are expensive (since perl doesn't know until run-time where to find the method), i therefore created a language::befunge::debug module that exports a debug() sub. 2 other subs are provided (but not exported) to turn on/off the debug. (there's a trick here, that i will explain in another post).

net result? around 20% speedup (a bit more in fact). not bad for one hour spent on the subject. :-)

other than that, language::befunge tests got sanitized (using test::more, test::output and test::exception everywhere instead of crafting stuff by hand). part of this code was not touched since 2002...

finally, language::befunge got some new extensions, still passing all mycology tests. you can now enjoy the following in jqbef98:
  • CPLI - complex numbers extension
  • DIRF - directory operations
  • FILE - file i/o operations
  • FIXP - fixed point operations
  • STRN - string operations
  • SUBR - subroutines extension
  • TIME - date/time operations
some of them were pretty difficult to get right, if you forget some befunge basis (note to self: the storage offset is here for a reason, dammit!).

which leaded me to update language::befunge::debugger to load mycology correctly, with a new option to run without delay till the next breakpoint. using it, things were easier to get right. still not perfect, but already more than usable...

so, enjoy language::befunge 4.11 and language::befunge::debugger 0.3.6, now available on cpan!

2009-01-17

how to shave 10% speed?

contributing to other projects is good (not counting the fact that it's fun):
  • you help open-source as a whole
  • you gain some knowledge on the projects you help
  • ... and you discover some new stuff, techniques, tricks
so recently i've been helping the padre team, where i discovered the module class::xsaccessor. reading the pod, it seemed quite good, but the question was: is it really that interesting?

so, i decided to try that on language::befunge, which currently uses a mix of class::accessor::fast and hand-crafted accessors (man, some part of the code is untouched since 2002!). who recalls of using the following in his/her code:
BEGIN {
my @attrs = qw{ attr1 attr2 attr3 };
foreach my $attr ( @attrs ) {
my $code = qq[ sub get_$attr { return \$_[0]->{$attr} } ];
$code .= qq[ sub set_$attr { \$_[0]->{$attr} = \$_[1] } ];
eval $code;
}
}

anyway, i created a new git branch in langage::befunge's repository, and ported all my classes to use class::xsaccessor. and then i've run both the module's test suite and mycology:
  • current language::befunge: tests = 5.28s, mycology = 20.48s
  • using class::xsaccessor: tests = 5.12s, mycology = 18.18s
(everything ran once to warm cache, and then averaging three passes. all output directed to /dev/null in order not to pollute cpu measures with io)

that is, around 5% saved (a bit less) for the tests, but 10% (a bit more) for mycology. knowing that the tests are not representative of real befunge workload, this means a 10% speedup in befunge programs... neat!

needless to say that i merged this temp branch (ain't git cool?) to master, and language::befunge 4.09 is on its way to cpan!

conclusion: use class::xsaccessor, it fulfils its promises... and contribute to other projects, at least you'll find some ideas for your own projects!