Perl ranges - my code is working, but I know it's not efficient

Kaos

[H]ard|Gawd
Joined
Oct 14, 2003
Messages
1,328
I've been dealing with an issue with my ISP for some time now where my ping speeds are extremely slow, when they send a tech out he just shurgs his shoulders and say it looks fine now so I asked them if I recorded some statistics on the ping speeds if that would help.

I did this test behind my router and directly connected to the modem.

Essentially I just ran a "ping myisp.com >> pingfile.txt" from my nix box and left it run for about 6 hours or so.

I then took that file and did the following against it so I would just have the ping response times

"cut -d= -f4 | cut -d' ' -f1 >> cut_ping.txt

so that left me with a file that would look like this (a short sample):

Code:
28.2
24.5
23.9

So then I wanted to get an average of the ping times (I chose perl to do this), that part was easy, I just read line by line, incremented a counter each time and added the values up - that's the simple math part.

I wanted to determine how many ping response fell into a certain range but for the life of me couldn't figure out how to use the range operator to do this.

What I have works but is ugly as sin.

I looked into Number::Range as well, but really that seems to come down to the same thing, I'd have to define all these ranges and then test my current line against those ranges (unless there's a more elegant way to use it) - which is essentially what I'm doing, I'd likely save myself some simple processing at the end.

Is there a better way to do what I've done?

Code:
#!/usr/bin/perl
use strict;


#clean up the ping output file by doing the following nix commands
#before running this
#cut -d= -f4 | cut -d' ' -f1 >> cut_ping.txt

#Variables

my $total=0;
my $average;
my $counter=0;

my $under50=0;
my $over500=0;
my $over600=0;
my $over700=0;
my $over800=0;
my $over900=0;
my $over1k=0;
my $over1100=0;
my $over1200=0;



#Logic
open pf, "cut_ping.txt";

foreach ( <pf> )
{
	

		#Get some idea of the range of where the issues are 
		#could be cleaner if implementing the range module.
		chomp($_);
		if ($_ < 50 ){$under50++}
		if ($_ > 500 ){$over500++}
		if ($_ > 600){$over600++}
		if ($_ > 700){$over700++}
		if ($_ > 800){$over800++}
		if ($_ > 900){$over900++}
		if ($_ > 1000){$over1k++}
		if ($_ > 1100){$over1100++}
		if ($_ > 1200){$over1200++}

	$counter++;
	$total = $total + $_;
}

$average = $total / $counter;

#compute amount at each range
my $b500_600 = $over500 - $over600;
my $b600_700 = $over600 - $over700;
my $b700_800 = $over700 - $over800;
my $b800_900 = $over800 - $over900;
my $b900_1k = $over900 - $over1k;
my $b1k_1100 = $over1k - $over1100;
my $b1100_1200 = $over1100 - $over1200;

#Script Output 
print "Total of " . $counter . " lines is " . $total . "\n";
print "Average Ping time is " . $average . "\n\n";
print "The ping time was under 50 " . $under50 . " times\n";
print "The ping time was between 500ms and 600ms " . $b500_600 . " times\n";
print "The ping time was between 600ms and 700ms " . $b600_700 . " times\n";
print "The ping time was between 700ms and 800ms " . $b700_800 . " times\n";
print "The ping time was between 800ms and 900ms " . $over800 . " times\n";
print "The ping time was between 900ms and 1000ms " . $over900 . " times\n";
print "The ping time was between 1000ms and 1100ms " . $b1k_1100 . " times\n";
print "The ping time was between 1100ms and 1200ms " . $b1100_1200 . " times\n";
print "The ping time was over 1200ms " . $over1200 . " times\n";

The output looks like this

Code:
Total of 28275 lines is 5120397.69999997
Average Ping time is 181.092756852342

The ping time was under 50 17532 times
The ping time was between 500ms and 600ms 575 times
The ping time was between 600ms and 700ms 441 times
The ping time was between 700ms and 800ms 475 times
The ping time was between 800ms and 900ms 1923 times
The ping time was between 900ms and 1000ms 1357 times
The ping time was between 1000ms and 1100ms 389 times
The ping time was between 1100ms and 1200ms 311 times
The ping time was over 1200ms 224 times
 
Looks fine to me, what's the problem?

*edit* you could eliminate the subtraction by reversing your if statements and making them else if, but *shrug* it doesn't seem that important
 
You probably can't get more efficient than this unless you want regularly spaced intervals (where you could start using integer division to come up with counting indexes) but you could probably make it a little cleaner by using some sort of data structure rather than a bunch of discrete variables. Like [ {min=>0, max=>50}, {min=>500, max=>600}... ] and loop over that. It'd be a touch slower but nothing perceptible for the purposes you're using & your code would be cleaner and easier to extend.

The big step I'd take to improve the program would be to have the Perl script actually split the ping times out of the line for you - there's no point in having to take the intermediate step of making a second text file.
 
Thanks for the feedback.

In regards to having perl do the work that cut did, is there a function that would do that for me? I've done some similar stuff with awk a few times before - but having it be all perl (and cross platform!) would certainly be beneficial.
 
Thanks for the feedback.

In regards to having perl do the work that cut did, is there a function that would do that for me? I've done some similar stuff with awk a few times before - but having it be all perl (and cross platform!) would certainly be beneficial.

what does a typical output line look like from your ping command?
 
You should be able to do it with split/grep or just a regular expression match.
 
what does a typical output line look like from your ping command?

just a typical linux ping output

Code:
-bash-3.2$ ping localhost
PING localhost.localdomain (127.0.0.1) 56(84) bytes of data.
64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=1 ttl=64 time=0.020 ms
64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=2 ttl=64 time=0.024 ms
64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=3 ttl=64 time=0.024 ms
64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=4 ttl=64 time=0.022 ms

--- localhost.localdomain ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3000ms
rtt min/avg/max/mdev = 0.020/0.022/0.024/0.005 ms

So I'd need to split up everything to get the time, and rip the other stuff off....or use the min/avg/max they list
 
The simplest regex to grab the ping time while reading the uncut output is probably:

Code:
if(/time=(\S+)/) {
 $ping_time = $1;
}
 
And the fastest would loop over characters until the 7th " " is encountered, then move up 5 characters, then take a substring until the next space and cast as integer.
 
Back
Top