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

I'm using an old module from CPAN called WinConsole. It is a simple windowing utility for terminals. It works pretty good for me until recently. I decided to profile my application and I discovered that this sub takes up about 1/3 of the processing time. I suspect the substrs at the bottom of the innermost loop to be the problem, but I'm not even 100% sure what that line is doing.

Any suggestions for improvements

I have reformatted the code for clarity, but not changed it's functionality.

# # makeFullBackBuffer # # return a fullscreen attribute and a fullscreen text backbuffers usin +g all the miniwins' backbuffer content. sub makeFullBackBuffer { my ($self) = @_; my (@backAtt, @backTxt, $screen, $current, $destCol, $destRow, $act +ive); $screen = $self->{'miniwin'}[0]; for (1..$screen->{'height'}) { push @backTxt, ' ' x $screen->{'width'}; push @backAtt, [(0) x $screen->{'width'}]; } for $active (reverse @{$self->{'winStack'}}) { $current=$self->{'miniwin'}[$active]; for my $row (1..$current->{'height'}) { for my $col (1..$current->{'width'}) { $destCol = $current->{'colTop'} + $col - 2; if ($destCol > $screen->{'width'}) { $destCol = $screen->{'width'}; } $destRow = $current->{'rowTop'} + $row - 2; if ($destRow>$screen->{'height'}) { $destRow = $screen->{'height'}; } $backAtt[$destRow][$destCol] = $current->{'backAtt'}[$row +- 1][$col - 1]; $self->{'zBuffer'}[$destRow][$destCol] = $active; substr($backTxt[$destRow], $destCol, 1) = substr($current- +>{'backTxt'}[$row - 1],$col - 1, 1); } } } return \(@backAtt,@backTxt); }

And before someone mentions it, it does run under strict & warnings without a problem.

Replies are listed 'Best First'.
Re: Help in improving Performance of WinConsole makeFullBackBuffer
by BrowserUk (Patriarch) on Apr 24, 2009 at 22:58 UTC

    This is untested! You'll need to check it carefully.

    First thing to look at is the inner loop. And the first thing I noticed there is that these lines are loop invariant.

    $destRow = $current->{'rowTop'} + $row - 2; if ($destRow>$screen->{'height'}) { $destRow = $screen->{'height'}; }

    (Ie They do not vary with the value of the inner loop counter), so they should be moved outside.

    Next, these three lines respectively:

    $backAtt[$destRow][$destCol] = $current->{'backAtt'}[$row - 1][$col - 1]; $self->{'zBuffer'}[$destRow][$destCol] = $active; substr($backTxt[$destRow], $destCol, 1) = substr($current->{'backTxt'}[$row - 1],$col - 1, 1);

    1. Copy a slice of an array element by element;

      Which can be done more efficiently by an array slice assignment.

    2. Assign to an array slice element-wise.

      Again, an array slice assignment.

    3. Copy a substr byte-wise.

      Can be replaced by a single substring assigment.

    For that to happen, you need to calculate the source and destination start points and the width.

    • Source start is just 0
    • Destination start is {colTop} - 1
    • Width is min( $screen->{width}, $current->{colTop} + $screen->{width} - 2 ) - destination start.

    Putting that together I get:

    sub makeFullBackBuffer { my ($self) = @_; my (@backAtt, @backTxt, $screen, $current, $destCol, $destRow, $ac +tive); $screen = $self->{miniwin}[ 0 ]; for( 1 .. $screen->{height} ) { push @backTxt, ' ' x $screen->{width}; push @backAtt, [ (0) x $screen->{width} ]; } for $active (reverse @{ $self->{winStack} } ) { $current = $self->{miniwin}[ $active ]; for my $row ( 1 .. $current->{height} ) { $destRow = $current->{rowTop} + $row - 2; $destRow = $screen->{height} if $destRow>$screen->{height} +; my $colMin = $current->{colTop} - 1; my $colMax = min( $screen->{width}, $current->{colTop} + $screen->{width} - 2 ); my $cols = $colMax - $colMin +1; @{ $backAtt[ $destRow ] }[ $colMin .. $colMax ] + = @{ $current->{backAtt}[ $row - 1 ] }[ 0 .. cols ] +; @{ $self->{zBuffer}[$destRow] }[ $colMin .. $colMax ] = ( $active ) x $cols; substr( $backTxt[ $destRow ], $colMin, $cols ) = substr( $current->{backTxt}[ $row - 1 ], 0, $cols ); } } return \( @backAtt, @backTxt ); }

    Looking at that, I see that you could simplify the $destRow also, but as-is--assuming my logic is not too screwed, you could (maybe) see an order of magnitude improvement through the elimination of the inner loop.

    Update:One thing you might consider is that Win32::Console gives access to the console apis and they allow the attachment of mutliple buffers to a screen which can be switched very quickly. The api also provides primatives for copying rectangular windows of both text and attributes (and introspection), which would be far more efficient than the role-your-own equivalents in the module you're using--but the switchover would probably involve a considerable amount of work.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      Thanks for your suggestions. I will try them out.
      I wish I could use a more "modern" module, like Win32::Console but this is going to run on Solaris and a bit out of date Perl (v5.6.1 ) as well. Unfortunately I can't get a newer Perl version installed and there's no C compiler either so I can't install my own version either.
        I wish I could use a more "modern" module, like Win32::Console but this is going to run on Solaris and a bit out of date Perl (v5.6.1 )

        I'm pretty sure that Win32::Console was around and working in 5.6.1 days...but no matter as it wouldn't run on Solaris! (It is Win32 (Windows only!)).

        My mistake. I assumed from the "Win" in WinConsole that it was a Windows module.

        Anyway, I hope that the refactoring I did will work for you.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.