August 22nd, 2005

diesel, learning, evil, sweeti

Graphs - OR - CPAN, CPAN, rah, rah rah

One of the ever more infrequent articles on CPAN to go up recently was this one to do with .ics files and graphing. Interesting enough as it was it also thoroughly failed to use CPAN.

I sort of see the point of teaching people how to do something (in this case glueing disparate data sources together, something Perl is very good at) even when there exists other solutions - after all I learnt network programming by writing an SMTP client and an HTTP client and then referring back to Net::SMTP and LWP::UserAgent - but I think he should have at least mentioned what was out there. And what the limitations of his code was. Maybe I'm being picky.

Either way - I went and wrote a script that used CPAN modules to do the same thing. The results can be seen here.

I really ought to turn that into GraphViz::Ics or something I suppose.

diesel, learning, evil, sweeti

Cache in Hand

Oh God. The title puns are getting worse and worse.

I started playing around with caching strategies for Data::ICal::DateTime and ended up with these three strategies.

All of them assume this function

    sub get_name {                                                                       
        my $ics   = shift;
        my $span  = shift;
        my $start = ($span->start_is_open)? "infinite" : $span->start->epoch;
        my $end   = ($span->end_is_open)?   "infinite" : $span->end->epoch;
        return "$ics-$start-$end";
    }  

Memoize:

    tie my %cache => 'DB_File', $filename, O_RDWR|O_CREAT, 0666;                         
    memoize('events', NORMALIZER => 'get_name',                                          
                      SCALAR_CACHE => [HASH => \%cache]);                                
                                                                                     
    my @events = events($ics, $span);                                                    
                                                                                     
    sub events {                                                                         
        my $ics  = shift;                                                            
        my $span = shift;                                                            
        my $cal  = Data::ICal->new( filename => $ics );                              
        return $cal->events;                                                         
    }                                                                                    
                                                                                     

Pixie:

    my $pixie   = Pixie->new->connect('bdb:cache/cache.db');                               
    my $name   = get_name($ics, $set);                                                     
    my $events = $pixie->get_object_named($name);                                        
                                                                                         
    my @events;                                                                          
    if ($events) {                                                                       
        @events = @$events;                                                              
    } else {                                                                             
        my $cal = Data::ICal->new( filename => $ics);                                    
        @events = $cal->events($set);                                                    
        my $object = bless \@events, "Random";                                           
        $pixie->bind_name( $name => $object );                                           
    }        

Storable:

    my @events;
    my $name  = get_name($ics, $set);  
    my $cache = "cache/$name.cache";
    if (-e $cache) {                                                              
        # stat to check the cache is still valid...                                          
        @events = @{ Storable::retrieve( $cache ) };
    } else {
        my $cal = Data::ICal->new( filename => $ics );
        @events = $cal->events($span);
        Storable::store \@events, $cache;
    }

Benchmarked that gives -

 Benchmark: timing 1000 iterations of memoize, pixie, storable... 
   memoize:  4 wallclock secs ( 3.47 usr +  0.06 sys =  3.53 CPU) @ 283.29/s (n=1000)
     pixie: 13 wallclock secs (11.57 usr +  0.11 sys = 11.68 CPU) @ 85.62/s (n=1000) 
  storable:  4 wallclock secs ( 3.91 usr +  0.09 sys =  4.00 CPU) @ 250.00/s (n=1000)
               Rate    pixie storable  memoize
    pixie    85.6/s       --     -66%     -70% 
    storable  250/s     192%       --     -12%
    memoize   283/s     231%      13%       --  

Which is pretty respectable.

The full script I used to do this is here.