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

Okay well I pulled the calculation stuff from the procps (top) source code, and knowing absolutely no C at all
I'm not to certain that I interpreted it right, the code seems to work though, but feels like a cheap hack especially
with sleep(). So any suggestions on how to improve this would be greatly appreciated.
#!/usr/bin/perl -w use strict; sub cpu_stat { my($cpu_user, $cpu_nice, $cpu_total, $cpu_idle); open STAT_FILE, "/proc/stat" or warn "Couldn't open /proc/stat: $! +\n"; while (<STAT_FILE>) { ($cpu_user, $cpu_nice, $cpu_total, $cpu_idle) = ($1,$2,$3,$4) +if /^cpu\s+(\d+)\ (\d+) (\d+) (\d+)/; last; } close STAT_FILE; return($cpu_user, $cpu_nice, $cpu_total, $cpu_idle); } my($cpu_user_old, $cpu_nice_old, $cpu_total_old, $cpu_idle_old) = cpu_ +stat(); sleep(1); my($cpu_user, $cpu_nice, $cpu_total, $cpu_idle) = cpu_stat(); my $ticks_past = ($cpu_user + $cpu_nice + $cpu_total + $cpu_idle) - ($ +cpu_user_old + $cpu_nice_old + $cpu_total_old + $cpu_idle_old); my $scale = 100.0 / $ticks_past; $cpu_user = sprintf "%.0f", ($cpu_user - $cpu_user_old) * $scale; $cpu_nice = sprintf "%.0f", ($cpu_nice - $cpu_nice_old) * $scale; $cpu_total = sprintf "%.0f", ($cpu_total - $cpu_total_old) * $scale; $cpu_idle = sprintf "%.0f", ($cpu_idle - $cpu_idle_old) * $scale; print "user: $cpu_user%, nice: $cpu_nice%, total: $cpu_total%, idle: +$cpu_idle%\n";

Edit Masem 2001-11-04 - CODE tags

Replies are listed 'Best First'.
Re: suggestions for improvement
by blakem (Monsignor) on Nov 04, 2001 at 15:55 UTC
    First suggestion: Use the special <CODE> tags around your code. Notice how your while clause got munged, that won't happen with code enclosed in <code> tags...

    Second suggestion: The regex line in cpu_stat can be shortened... Since a m// regex returns ($1,$2,$3,etc) when called in list context, you can shorten that line to:

    ($cpu_user, $cpu_nice, $cpu_total, $cpu_idle) = /^cpu\s+(\d+)\ (\d+) ( +\d+) (\d+)/;
    Third suggestion, since you aren't actually doing anything with the individual values in the subroutine, don't even bother with multiple scalars... just use an array. Fourth: while seems overkill since you aren't really looping. Just grab one line w/o a loop.

    Fifth: you can also remove the backslash before the space in your regex...

    sub cpu_stats { my @cpustats; open STAT_FILE, "/proc/stat" or warn "Couldn't open /proc/stat: $!\ +n"; my $line = <STAT_FILE>; @cpustats = $line =~ /^cpu\s+(\d+) (\d+) (\d+) (\d+)/; close STAT_FILE; return(@cpustats); }

    -Blake

Re: suggestions for improvement
by Fastolfe (Vicar) on Nov 04, 2001 at 21:29 UTC
    use List::Util 'sum'; # might be overkill sub cpu_stat { open(STAT_FILE, "< /proc/stat") or warn "/proc/stat: $!"; my $line = <STAT_FILE>; close(STAT_FILE); $line =~ /^cpu\s+(\d+) (\d+) (\d+) (\d+)/; } my @before = cpu_stat(); sleep 1; my @after = cpu_stat(); my $ticks_past = sum(@after) - sum(@before); my $scale = 100 / $ticks_past if $ticks_past; # divide by zero error # Arguably, this is less readable than your code printf("user: %d%%, nice: %d%%, total: %d%%, idle: %d%%\n", map { ($after[$_] - $before[$_]) * $scale } 0 .. 3);
    There is benefit, though, of using those temporary variables in cases where you want to do additional operations to them. Working with them in array form is not very readable. A hash is another option:
    my %hash; @hash{qw/ user nice total idle /} = cpu_stat(); print "nice: $hash{nice}\n";
    A lot of this, though, is just personal style. You're doing these once a second, so it's not like you're coding yourself into a hugely inefficient corner here. Use whatever method maximizes readability and maintainability for you. There was nothing really wrong with your code from an efficiency point of view, and it was very readable. (Though I'm still trying to figure out if that's the way you want to calculate $scale...)

    Why were you concerned about doing the sleep? Any implementation that tries to keep real-time tabs on CPU statistics has to do it in a polling style, which is what this is. If you do it much faster than that, your monitoring process is going to end up using 100% of the very CPU you're trying to keep tabs on!

    Hope this helps...

Re: suggestions for improvement
by rendler (Pilgrim) on Nov 04, 2001 at 15:59 UTC
    um that should be
    while (<STAT_FILE>) {