From 75bd8dcb7ec359dd9fe31e35352f13d4396bf71f Mon Sep 17 00:00:00 2001 From: Abin Shahab Date: Wed, 12 Feb 2014 10:17:41 -0800 Subject: [PATCH 1/4] DP-402 Added perfcp command for simple file copy perf tests --- bin/perfcp | 127 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100755 bin/perfcp diff --git a/bin/perfcp b/bin/perfcp new file mode 100755 index 0000000..a397687 --- /dev/null +++ b/bin/perfcp @@ -0,0 +1,127 @@ +#!/usr/bin/env ruby + +# Copyright (c) 2013 Altiscale, inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License + +# This script runs the benchmark. It requires the platform +# and benchmark json files + +require 'logger' +require 'optparse' +require 'ostruct' + +LOG_LEVELS = %w( + debug + info + warn + error + fatal +) + +def exec(command, logger) + logger.debug "executing #{command}" + logger.error "Error running #{command}" unless system command + logger.debug "#{command} OK" +end + +logger = Logger.new(STDOUT) +log_map = Hash[LOG_LEVELS.map.with_index.to_a] +settings = OpenStruct.new +options = OptionParser.new do |opts| + settings.log_level = 'info' + settings.times = 1 + + opts.on('-s', + '--sources SOURCES', + 'Comma separated source folders.') do |sources| + settings.sources = sources + end + + opts.on('-d', + '--dest DESTINATIONS', + 'Destination folder.') do |dest| + settings.dest = dest + end + + opts.on('-t', + '--times NUM_TIMES', + 'Times to run script, default 1.') do |times| + settings.times = times + end + + opts.on('-i', + '--init INIT_SCRIPT', + 'Optional init script(runs each time).') do |init| + settings.init = init + end + + opts.on('-o', + '--outfile FILE', + 'Optional output for perf stats).') do |outfile| + settings.outfile = outfile + end + + opts.on('-l', + '--log-level LEVEL', + "Log level: #{LOG_LEVELS.join(', ')}") do |log_level| + settings.log_level = log_level + end + + opts.on('-h', + '--help', + 'Show this help.') do + puts options.to_s + exit + end +end + +# Parse options and make sure we have our required ones. +begin + options.parse! + mandatory = [:sources, :dest] + missing = [] + mandatory.each do |setting| + if !settings.marshal_dump.has_key?(setting) + missing << setting.to_s.gsub('_', '-') + end + end + if !missing.empty? + @logger.warn "Missing required options: #{missing.join(', ')}" + @logger.warn options.to_s + exit 1 + end + unless LOG_LEVELS.include?(settings.log_level) + puts "Invalid log_level (#{settings.log_level}). \ + Valid values: #{LOG_LEVELS.join(', ')}" + exit 1 + end +rescue OptionParser::InvalidOption, OptionParser::MissingArgument + puts $ERROR_INFO.to_s + puts options.to_s + exit 1 +end + + +logger.level = log_map[settings.log_level] +logger.debug "Starting perfcp with sources #{settings.sources}" +# Execute init script +for i in 1..settings.times.to_i + logger.debug "Run: #{i}" + exec(settings.init, logger) if settings.init + settings.sources.split(',').each do |source| + run_command = "time cp #{source} #{settings.dest}" + run_command = "echo #{run_command} >> #{settings.outfile}; { #{run_command}; } 2>> #{settings.outfile}" if settings.outfile + exec run_command, logger + end +end \ No newline at end of file From 6bb57e863baf8270f45563242458b90bc512a6ee Mon Sep 17 00:00:00 2001 From: Abin Shahab Date: Wed, 12 Feb 2014 11:11:35 -0800 Subject: [PATCH 2/4] Fix --- bin/perfcp | 4 ++-- lib/perf_framework/version.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/perfcp b/bin/perfcp index a397687..eea9e8f 100755 --- a/bin/perfcp +++ b/bin/perfcp @@ -97,8 +97,8 @@ begin end end if !missing.empty? - @logger.warn "Missing required options: #{missing.join(', ')}" - @logger.warn options.to_s + puts "Missing required options: #{missing.join(', ')}" + puts options.to_s exit 1 end unless LOG_LEVELS.include?(settings.log_level) diff --git a/lib/perf_framework/version.rb b/lib/perf_framework/version.rb index a3ec9cf..30489bb 100644 --- a/lib/perf_framework/version.rb +++ b/lib/perf_framework/version.rb @@ -14,5 +14,5 @@ # Version module PerfFramework - VERSION = '0.0.3' + VERSION = '0.0.4' end From 69a48940f5b2e03a540dc682258ce272b00c8a49 Mon Sep 17 00:00:00 2001 From: Ravi Prakash Date: Wed, 19 Feb 2014 10:06:14 -0800 Subject: [PATCH 3/4] Additions to the perfcp script for pretty printing --- bin/perfcp | 107 +++++++++++++++++++++++++++++++---------- perf_framework.gemspec | 2 + 2 files changed, 84 insertions(+), 25 deletions(-) diff --git a/bin/perfcp b/bin/perfcp index eea9e8f..df44367 100755 --- a/bin/perfcp +++ b/bin/perfcp @@ -14,12 +14,16 @@ # See the License for the specific language governing permissions and # limitations under the License -# This script runs the benchmark. It requires the platform -# and benchmark json files +# This script tests read and write performance of a file system +# It copies files from a source folder into a sink folder and calculates +# average times with standard deviation require 'logger' require 'optparse' require 'ostruct' +require 'terminal-table' +require 'descriptive_statistics' +require 'fileutils' LOG_LEVELS = %w( debug @@ -29,12 +33,6 @@ LOG_LEVELS = %w( fatal ) -def exec(command, logger) - logger.debug "executing #{command}" - logger.error "Error running #{command}" unless system command - logger.debug "#{command} OK" -end - logger = Logger.new(STDOUT) log_map = Hash[LOG_LEVELS.map.with_index.to_a] settings = OpenStruct.new @@ -43,13 +41,13 @@ options = OptionParser.new do |opts| settings.times = 1 opts.on('-s', - '--sources SOURCES', - 'Comma separated source folders.') do |sources| + '--source SOURCE', + 'Source folder containing files to be copied.') do |sources| settings.sources = sources end opts.on('-d', - '--dest DESTINATIONS', + '--dest DESTINATION', 'Destination folder.') do |dest| settings.dest = dest end @@ -66,12 +64,6 @@ options = OptionParser.new do |opts| settings.init = init end - opts.on('-o', - '--outfile FILE', - 'Optional output for perf stats).') do |outfile| - settings.outfile = outfile - end - opts.on('-l', '--log-level LEVEL', "Log level: #{LOG_LEVELS.join(', ')}") do |log_level| @@ -114,14 +106,79 @@ end logger.level = log_map[settings.log_level] -logger.debug "Starting perfcp with sources #{settings.sources}" -# Execute init script +logger.debug "Starting perfcp with sources in #{settings.sources}" + +#The heading row of the table we are going to print +headings = ["File", "Size"] + +#This table will store the file names in the 1st coloumn, file sizes in the 2nd +#column and times to copy those files in subsequent coloumns. Last 2 coloumns +#will be used for mean and standard deviation +table = Array.new + +# Get the list of files and sizes (and make it static) +i = 0 +Dir.foreach(settings.sources) do |item| + next if item == '.' or item == '..' + table.push(Array.new) + file = File.new(settings.sources + "/" + item) + table[i].push(item) + table[i].push(file.size()) + i += 1; +end + +#Create the destination directory if it doesn't exist already +if !Dir.exists? settings.dest + Dir.mkdir(settings.dest) + logger.debug "Destination directory #{settings.dest} not found. Creating it" +end + +# For each of the times for i in 1..settings.times.to_i logger.debug "Run: #{i}" - exec(settings.init, logger) if settings.init - settings.sources.split(',').each do |source| - run_command = "time cp #{source} #{settings.dest}" - run_command = "echo #{run_command} >> #{settings.outfile}; { #{run_command}; } 2>> #{settings.outfile}" if settings.outfile - exec run_command, logger + headings.push("Run #{i}") + + # Execute init script + if settings.init then + logger.debug("Running init script: " + settings.init) + system(settings.init) end -end \ No newline at end of file + + #Delete directory if it exists already + if Dir.exists? "#{settings.dest}/Run#{i}" + FileUtils.rm_rf("#{settings.dest}/Run#{i}") + logger.debug "Directory #{settings.dest}/Run#{i} existed already. Deleting it" + end + + #Create a new directory in the destination for this run + command = "mkdir #{settings.dest}/Run#{i}" + logger.debug "Making directory. Command: #{command}" + system command + abort("Couldn't create a directory for Run #{i}") if $? != 0 + + # Do the actual copies. For each file + table.each do |row| + command = "cp #{settings.sources}/#{row[0]} #{settings.dest}/Run#{i}" + logger.debug "Copying file #{row[0]}. Command: #{command}" + + #Push the time taken to do the copy into the row + start = Time.now + system command + row.push(Time.now - start) + abort("Couldn't copy file #{row[0]} during Run #{i}") if $? != 0 + end +end + +#Calculate the mean and standard deviations of the times. +#The times are in the 2nd coloumn onwards +headings.push("Average", "Std. Dev.") +table.each do |row| + times = row[2..row.size]; + row.push( times.mean ) + row.push( times.standard_deviation ) +end + +terminal_table = Terminal::Table.new :headings => headings, :rows => table + +puts "These were the times: " +puts terminal_table \ No newline at end of file diff --git a/perf_framework.gemspec b/perf_framework.gemspec index 4ffd516..49f3f4b 100644 --- a/perf_framework.gemspec +++ b/perf_framework.gemspec @@ -26,4 +26,6 @@ Gem::Specification.new do |spec| spec.add_runtime_dependency "net-ssh" spec.add_runtime_dependency "net-scp" spec.add_runtime_dependency "aws-sdk" + spec.add_runtime_dependency "terminal-table" + spec.add_runtime_dependency "descriptive_statistics" end From c8bd0b22db7eb9242e0b2fbd4720402fd1e5d73c Mon Sep 17 00:00:00 2001 From: Ravi Prakash Date: Wed, 26 Feb 2014 08:26:40 -0800 Subject: [PATCH 4/4] Restricting -times argument to be integer in perfcp. Thanks Tucker --- bin/perfcp | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/perfcp b/bin/perfcp index df44367..8358624 100755 --- a/bin/perfcp +++ b/bin/perfcp @@ -54,6 +54,7 @@ options = OptionParser.new do |opts| opts.on('-t', '--times NUM_TIMES', + Integer, 'Times to run script, default 1.') do |times| settings.times = times end