#!/usr/bin/perl use strict; use warnings; # testFileCopy # This script is intended for use in testing the speed of file copying # (e.g. when experiencing slow copying of files from a networked drive) # # The source and destination files are given as command-line arguments # in the same order as the 'cp' command # If only one command-line argument is supplied, then that is taken # as the destination file and the source data will be read from STDIN. # # Note that it is often useful to give /dev/null as the destination # and/or to give /dev/random as the source # and/or to generate source data programmatically (read from STDIN) # in order to factor out issues with the source or destination file systems. # # Sample usages: # testFileCopy /Volumes/name_of_server/MyStuff/bigfile ~/Junk/bigfile # testFileCopy ~/Junk/bigfile /Volumes/name_of_server/MyStuff/bigfile # testFileCopy /Volumes/name_of_server/MyStuff/bigfile /dev/null # randBytes 100000000 | testFileCopy /Volumes/name_of_server/MyStuff/bigfile # # Cameron Hayne (macdev@hayne.net) Oct 2003, revised Aug 2006 my $scriptName = "testFileCopy"; my $numArgs = scalar(@ARGV); if ($numArgs < 1) { die "Usage: $scriptName srcFile destFile\n" . " or $scriptName destFile (if reading from STDIN)\n"; } my $srcFile; my $destFile; my $readingFromStdin = 0; if ($numArgs == 2) { $srcFile = $ARGV[0]; $destFile = $ARGV[1]; die "$scriptName: srcFile \"$srcFile\" does not exist\n" unless -f $srcFile; } else { $readingFromStdin = 1; $destFile = $ARGV[0]; } die "$scriptName: destFile must be a file, not a directory\n" if -d $destFile; my $buffsize = 64 * 1024; # experiment with different buffer sizes my $buffer; my $verbose = 1; # change this to 0 to avoid reports during the copying # We use the Time::HiRes module in order to get sub-second timing # This module might not be installed by default but you can # get it via http://cpan.org/ # Otherwise, comment out the following line use Time::HiRes qw(time); # function declarations sub printMessage($); # catch control-C and "kill" and try to exit gracefully my $forceQuit = 0; $SIG{INT} = $SIG{TERM} = sub { $SIG{INT} = $SIG{TERM} = 'DEFAULT'; $forceQuit = 1; }; my $startTime = time(); my $srcFH; if ($readingFromStdin) { $srcFH = *STDIN; } else { use IO::File; $srcFH = IO::File->new(); open($srcFH, "<$srcFile") || die "can't open file $srcFile for reading: $!\n"; } open(DEST, ">$destFile") || die "can't open file $destFile for writing: $!\n"; # set both files to binary # this doesn't affect anything on OS X # but it allows the script to run correctly on Windows binmode($srcFH); binmode(DEST); printMessage("files are open"); my $totalNread = 0; my $prevTime = $startTime; my $totalTimeReading = 0; my $totalTimeWriting = 0; my $peakReadRate = 0; my $peakWriteRate = 0; my $peakCopyRate = 0; while (my $nread = read($srcFH, $buffer, $buffsize)) { my $timeAfterRead = time(); print DEST $buffer; my $timeAfterWrite = time(); my $elapsedTimeReading = $timeAfterRead - $prevTime; my $elapsedTimeWriting = $timeAfterWrite - $timeAfterRead; my $elapsedTimeCopying = $elapsedTimeReading + $elapsedTimeWriting; $totalTimeReading += $elapsedTimeReading; $totalTimeWriting += $elapsedTimeWriting; $prevTime = $timeAfterWrite; $totalNread += $nread; my $msg = "$totalNread bytes"; if ($elapsedTimeCopying > 0) { $msg .= " ("; if ($elapsedTimeReading > 0) { my $readRate = ($nread / 1024) / $elapsedTimeReading; $peakReadRate = $readRate if $readRate > $peakReadRate; $msg .= sprintf("read:%.1f KB/s ", $readRate); } if ($elapsedTimeWriting > 0) { my $writeRate = ($nread / 1024) / $elapsedTimeWriting; $peakWriteRate = $writeRate if $writeRate > $peakWriteRate; $msg .= sprintf("write:%.1f KB/s ", $writeRate); } my $copyRate = ($nread / 1024) / $elapsedTimeCopying; $peakCopyRate = $copyRate if $copyRate > $peakCopyRate; $msg .= sprintf("copy:%.1f KB/s", $copyRate); $msg .= ")"; } printMessage($msg) if $verbose; if ($forceQuit) { print "Incomplete copy since script was force quit\n"; last; # break out of the 'while' loop } } close(DEST); close($srcFH) unless $readingFromStdin; printMessage("files are closed"); my $totalTime = time() - $startTime; if ($totalTime > 0) { my $numKB = $totalNread / 1024; my $avgReadRate = $numKB / $totalTimeReading; my $avgWriteRate = $numKB / $totalTimeWriting; my $avgTransferRate = $numKB / $totalTime; printf("Peak read rate: %8.1f KB/s\n", $peakReadRate); printf("Peak write rate: %8.1f KB/s\n", $peakWriteRate); printf("Peak copy rate: %8.1f KB/s\n", $peakCopyRate); printf("Average read rate: %8.1f KB/s\n", $avgReadRate); printf("Average write rate: %8.1f KB/s\n", $avgWriteRate); printf("Average transfer rate: %8.1f KB/s\n", $avgTransferRate); } exit; # ----- FUNCTIONS ----- sub printMessage($) { my ($message) = @_; my $elapsed = time() - $startTime; printf("%.2f s: %s\n", $elapsed, $message); }