Tardis has asked for the wisdom of the Perl Monks concerning the following question:

I've written an interesting little script to solve a puzzle (a physical plastic pieces affair). The script appears to be working, but it's slow..... which brings me neatly round to the point of this post.

I'd like to be able to trap a SIGTERM and dump the state of the program to a file. However it being a recursive algorithm makes a simple Data::Dumper dump or similar a little more difficult.

There are a few global arrays which store the current state of the puzzle. The rest is a recursive call to 'solve_puzzle'.

My initial thought is that I can look for a global variable (set by the SIGTERM handler) which will be seen at the top of the solve_puzzle routine, and cause it to dump it's state out, and exit early - this will filter down through the recursion tree to the bottom, where it will save the puzzle state, and exit.

This sounds tricky and error prone, not to mention the performance overhead of doing this check each time. Any other ideas? Is there some u-beaut module which will let me capture a 'snapshot' of my entire program? (Thinking back to my Commodore 64 and Action Replay days).

  • Comment on Saving the state of a recursive program

Replies are listed 'Best First'.
Re: Saving the state of a recurseive program
by tadman (Prior) on May 16, 2002 at 05:00 UTC
    You're going to have to make sure that your program is aware of what it is doing, and that it can pick up where it left off. This is not necessarily trivial, especially if the work each function does is not entirely trackable.

    Maybe this will do something useful:
    #!/usr/bin/perl -w use strict; use constant RDELIM => "\x00"; use constant ADELIM => "\x01"; my @state; my @f_state; my $state_file = "saved.state"; $SIG{INT} = \&freeze; sub freeze { open (STATE, ">$state_file") || die "Could not save state to $state_file, sorry\n"; foreach (@state) { my ($args, $vars) = @$_; print "@$args...@$vars\n"; print STATE join(RDELIM, @$args); print STATE ADELIM; print STATE join(RDELIM, map { $$_ } @$vars); print STATE "\n"; } close (STATE); exit (0); } sub thaw { # If the file can't be opened, just restart open (STATE, $state_file) || return; @f_state = (); while (<STATE>) { chomp; push (@f_state, [ map { [ split(RDELIM, $_) ] } split(ADELIM, $_) ]); } close (STATE); } sub arg_push { push(@state, [shift(@_), [@_]]); } sub arg_pop { pop(@state); } sub arg_thaw { my ($args,$vars) = @{shift(@f_state)}; @{shift()} = @$args; foreach (@_) { $$_ = shift(@$vars); } } use constant DEPTH => 4; sub recurse { my ($x,$y,$X,$Y); my @args = @_; arg_push(\@args, \$x, \$y); # If there is frozen data specified to at least # this depth... if (@f_state) { # ...use that instead. arg_thaw(\@args, \$X, \$Y); print "Resuming @args ($X,$Y)\n"; } my ($what) = @args; $X = 0 unless (defined($X)); $Y = 'a' unless (defined($Y)); print "$what\n" if (@state == 3); if (length($what) <= DEPTH) { for my $x_iter ($X..'9') { for my $y_iter ($Y..'z') { $x = $x_iter; # Copy $y = $y_iter; recurse("$what$x_iter$y_iter"); } } } pop(@state); } thaw(); recurse('');
Re: Saving the state of a recursive program
by FoxtrotUniform (Prior) on May 16, 2002 at 01:56 UTC

    Warning: This probably isn't the best way to do it, and probably isn't even such a good idea. I'm by no means a perlguts adept. Take this one with a pillar of salt.

    You might find the DB backtrace method useful, since it (seems to) give you immediate data on the call stack.

    Update: You might also want to have a look at caller, which looks like it might do the right thing if called inside package DB. Thanks Zaxo for the idea.

    --
    :wq

Re: Saving the state of a recursive program
by Tardis (Pilgrim) on May 20, 2002 at 00:16 UTC
    Thanks for your comments.

    We've decided the easiest way to solve this (literally :-) is to throw more CPU at it.

    However we need an awful lot more CPU than is available in a single machine, so we've created a distributed version of my solver.

    This has turned into a rather interesting project, involving a couple of my collegues (and several more, anyone with a Unix box and a perl installation :-)

    When it's a bit cleaner (and I've proved it works) I'll post a node all about it.