BBH Central IconBBH Central Home Page
  CENTRAL home  |   BBHL home About/Contact Us  |   Subscribe  |   Index by Topic  
You are here: central > Broadband Home Labs > Home Networking > Honest
Created 9/10/02

Home Networking - HONEST: HOme NEtworking Speed Test

The following is the first version of HONEST, the HOme NEtworking Speed Test we created as part of the HomePlug evaluation. (A revised version, created for the Wi-Fi test, will be posted soon.)

We are publishing the source code of this program as a service to the broadband home community. We invite our readers to install and test it with HomePlug and other networks, and would very much appreciate comments and feedback so that we can improve the test.


Installation

In its current form, the test requires the installation of a standard Perl distribution. We developed it with Perl 5.6.0 (Build 622: December 2000) under Windows.

Perl is an open-source language and operating environment with versions for every operating system. ActivePerl, the industry-standard Perl distribution for Linux, Solaris, and Windows, is available for free download from ActiveState ( www.activestate.com/Products/ActivePerl/ ). A CD with the entire current distribution is also available at a nominal price.

HONEST uses four standard Perl modules, all included in the ActiveState distribution. Those modules not loaded when installing the distribution can be loaded easily with the Perl PPM module included in the ActiveState distribution.


Documentation

The program is internally documented in standard Perl format - please see the documentation at the end of the program.


Environment and Testing

We wrote HONEST for Windows and tested it on Windows 98SE, NT 4.0 and XP.

It should be straight-forward to modify it to run on Unix or Linux by removing the minor dependency on the Win32 module used to determine which machine is local and which is remote.


HONEST Source Code

Cut and paste the following source code into a text editor such as Notepad and save it as honest.plx . When you first run it in the Perl environment you may get messages indicating that Perl can't find one or more of the modules - if so, install them with the Perl PPM module.

#HONEST: Home Networking Speed Test
#Measures file transfer speed over a local area network
#7/15/2002 by Dave Waks, Broadband Home Central
use File::stat;
use Win32;
use Time::HiRes qw( gettimeofday tv_interval );
use Statistics::Descriptive;
$VERSION = '1.00';

$machine1 = "LOCALMACHINE";  #these need to be set to the network 
$machine2 = "REMOTEMACHINE";  #  names of the local and remote machines
$localpath = "honest";  #these need to be set to provide valid paths 
$remotepath = "honest";  #  for each machine
$sourcefile = "honest.txt";  #this is the text file used for testing
$testfile = "test.txt";  #this is the name of the file written to the
                         # remote machine
$max = 200000; #max bytes to read each time
$rep = 10; #number of repetitions

$thismachine = Win32::NodeName;
if ($machine1 =~ m/$thismachine/i) {
   $localmachine = $machine1;
   $remotemachine = $machine2;
}
elsif($machine2 eq $thismachine) {
   $localmachine = $machine2;
   $remotemachine = $machine1;
}
else {
   print "Neither $machine1 nor $machine2 matches $thismachine\n";
   exit;
}
$localsourcefilename = "//$localmachine/$localpath/$sourcefile";
$localtestfilename = "//$localmachine/$localpath/$testfile";
$remotetestfilename = "//$remotemachine/$remotepath/$testfile";


unless (-e $localsourcefilename) {
   print "$localsourcefilename does not exist, aborting";
   exit;
}

print "HONEST: Home Networking Speed Test\n..Enter network name:";
$networkname = ;
chomp ($networkname);
print "..Enter notes:";
$notes = ;

$logfilepath = "";
$logfilename = "FileReadLog_$localmachine-" .
   "$remotemachine-$networkname.txt";

#open log file
$start = time();
$starttime = times();
open (LOG, ">$logfilepath"."$logfilename")
   || die "can't open $logfilename: $!";
print "Starting file read test of $sourcefile local=$localmachine",
   "remote=$remotemachine network=$networkname at ",
   scalar localtime($start), "\n";
print LOG "Started file read test at ", scalar localtime($start), "\n";
print LOG " Local machine: $localmachine\n",
   " Remote machine: $remotemachine\n",
   " Local file:  $localtestfilename\n",
   " Remote file: $remotetestfilename\n";
$sb = stat($localsourcefilename);
print LOG " Source file: $sourcefile, File size: ", $sb->size, "\n";
print LOG " Notes: $notes\n";

print LOG "Local file write and read test:\n";
&readfile( $localsourcefilename, $localtestfilename );

$localmbytes = $totalbytes/(1024*1024);
$localreadtime = $totalreadtime;
$localratebytes = ($localmbytes/$totalreadtime);
$localratebits = 8*$localratebytes;
printf "Local:  %6.2f Mbps\n\n", $localratebits;

print LOG "\nRemote file write and read test:\n";
&readfile( $localsourcefilename, $remotetestfilename );

$remotembytes = $totalbytes/(1024*1024);
$remotereadtime = $totalreadtime;
$remoteratebytes = ($remotembytes/$totalreadtime);
$remoteratebits = 8*$remoteratebytes;
printf "Remote: %6.2f Mbps\n\n", $remoteratebits;

$networkreadtime = $remotereadtime - $localreadtime;
unless ($localmachine eq $remotemachine) {
   $networkratebytes = ($remotembytes/$networkreadtime);
   $networkratebits = 8*$networkratebytes;
   printf "Network:  %6.2f Mbps\n\n", $networkratebits;
}

$end = time();
#$endtime = times();

printf LOG "\n    Source   MBytes Read  Read Time  MBps    Mbps\n";
printf LOG "%10s %12.3f %8.2f %8.2f %7.2f\n", $localmachine,
   $localmbytes, $localreadtime, $localratebytes, $localratebits;
printf LOG "%10s %12.3f %8.2f %8.2f %7.2f\n", $remotemachine, 
   $remotembytes, $remotereadtime, $remoteratebytes, $remoteratebits;
printf LOG "%10s %12.3f %8.2f %8.2f %7.2f\n", "Network", $remotembytes,
   $networkreadtime, $networkratebytes, $networkratebits;

print LOG "\nFinished read test at ", scalar localtime($end), "\n";
print "\nFinished read test at ", scalar localtime($end), "\n";
close LOG;
print "..Log file: $logfilename\n";

sub readfile {
   my ($sourcefile) = $_[0];
   my ($testfile) = $_[1];
   my ($testfilename, $testfileext) = split (/\./, $testfile);
   $totalbytes = $totalreadtime = $totalwritetime = 0;
#   print "  Copying $sourcefile, reading $testfile\n";
   print LOG "  Copying $sourcefile, reading $testfile\n";

   #copy file
   $startwrite = [gettimeofday];
   open (INFILE, "$sourcefile")
     || die "open in failure $sourcefile $!";
   open (OUTFILE, ">$testfile")
     || die "open out failure $testfile $!"; 
   while ($count = read(INFILE, $buf, $max) ) {
      print OUTFILE $buf;
   }
   close INFILE;
   close OUTFILE;
   $endwrite = [gettimeofday];
   $writetime = tv_interval ( $startwrite, $endwrite );
   $totalwritetime += $writetime;
   printf "\nWrite time %.3f\n", $writetime;
   printf LOG "  Write time %.3f\n", $writetime;
   my($priorfile) = $testfile;
   $stat = Statistics::Descriptive::Sparse->new();

   for ($i=0; $i<$rep; ++$i) {

     #rename file
     my($readfile) = "$testfilename$i.$testfileext";
     rename ($priorfile, $readfile)
       or die "error renaming $priorfile to $readfile: $!\n";

     #read file
     open (INFILE, "$readfile") || die "open in failure $readfile $!";

     print "Read pass $i..";
#     printf LOG "Starting to read %i at %6.3f\n", $i, $startread;
     $startread  = [gettimeofday];

     while ($count = read(INFILE, $buf, $max) ) {
        $totalbytes += $count;
     }
     $endread = [gettimeofday];
#     printf LOG "Finished %i at %6.3f\n", $i, $startread;
     $readtime = tv_interval ( $startread, $endread ) ;
     $totalreadtime += $readtime;
     $stat->add_data($readtime);
     printf LOG "  Read pass %i time %6.3f\n", $i, $readtime;
     printf ".%6.3f\n", $readtime;
     close (INFILE);
     $priorfile = $readfile;
   } #each read pass 
  printf LOG "  Total read time %.2f\n", $totalreadtime;
     $stddevread = $stat->standard_deviation();
     $meanread = $stat->mean();
     printf "Mean %.3f  Standard Deviation %.3f (%.1f\%)\n", $meanread,
       $stddevread, 100*$stddevread/$meanread;
     printf LOG "  Mean %.3f  Standard Deviation %.3f (%.1f\%)\n",
       $meanread, $stddevread, 100*$stddevread/$meanread;
}

__END__

=head1 NAME

HONEST - Home Networking Speed Test

=head1 SYNOPSIS

    Run HONEST from the command prompt in Windows - will ask for 
      Enter network name: 
        This will be used as part of the file name and can be a
        description of the equipment being tested.
      Enter notes:
        This will be recorded in the log file.

=head1 DESCRIPTION

Measures file transfer speed over a local area network. It assumes that
it is running on a "local" PC and uses a "remote" PC for file reading. 
The "remote" PC should have reasonably fast file reads and network
transfers for consistant results.

The program first writes a test file to the remote PC. It then 
repetitively renames the file and reads it back as fast as it can, 
precisely timing the interval between the start and end of file 
reading. The ratio of the file size to the read time is used to
estimate the data transfer rate.

It needs to rename the file because otherwise Windows will cache the
file and create instantaneous transfer rates.

It actually runs the test twice, once on the local machine and once on 
the remote; the read time on the local machine is subtracted from the 
remote read time in estimating the data rate, thus removing the read 
time from the calculated data transfer rate. This is a coarse 
approximation since it should use properly the read time of the remote 
machine.

The program reports its activity on the console and also creates a log 
file with the names of the local and remote PCs and the "network name" 
entered by the user.

The program calculates the standard deviation of the local and remote 
transfer times as an indication of the repeatablity of the tests. A 
standard deviation of less than 5% indicates little difference between
test repetitions.

=head1 AUTHOR

David J. Waks <dave@system-dynamics.com>.

=head1 COPYRIGHT

Copyright (c) 2002 System Dynamics Inc. All rights reserved.
This module is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.

=cut