#!/usr/local/jython-2.7b3/bin/jython '''Test one datastructure for a given number pattern, heap state, and size''' # pylint: disable=superfluous-parens # Parentheses are good for clarity and portability # port re import sys import time import random # port subprocess import python2x3 import linked_list_mod import common import memuse_mod def usage(retval): '''Output a usage message''' if retval == 0: write = sys.stdout.write else: write = sys.stderr.write write('Usage: {}\n'.format(sys.argv[0])) write(' --datastructure ds\n') write(' --size size\n') write(' --just-run\n') write(' --help\n') sys.exit(retval) def my_range(top): '''range generator with consistent semantics across 2.x and 3.x''' number = 0 while number < top: yield number number += 1 # We use a default argument to compile once, use many # def get_load_factor(regex=re.compile(br'^ .*load average: ([0-9]*\.[0-9]*), [0-9]*\.[0-9]*, [0-9]*\.[0-9]*$')): def get_load_factor(): '''Obtain the system load factor''' # process = subprocess.Popen('uptime', stdout=subprocess.PIPE) # (input_, output) = process.communicate() # dummy = output # retval = process.returncode # assert retval in {0, None} # lines = input_.rstrip(b'\n').split(b'\n') # assert len(lines) == 1 # # match_obj = regex.match(lines[0]) # # assert match_obj is not None # # return float(match_obj.group(1)) with open('/proc/loadavg') as file_: line = file_.readline() fields = line.split() one_minute_load_average_string = fields[0] one_minute_load_average_float = float(one_minute_load_average_string) return one_minute_load_average_float def in_good_load_factor(): '''True iff load factor is below 1.0''' load_factor = get_load_factor() if load_factor >= 1.0: return False else: return True # return indicates a good load factor def get_hour(): '''Get the hour of the day''' hour = time.localtime(time.time()).tm_hour return hour def in_good_time_window(): '''True iff in a good time window''' hour = get_hour() dummy = hour return True # 11PM to 5:59AM # All times are good, because this machine is dedicated to these tests now, other # than twice-weekly backups. # if hour <= 5 or hour >= 23: # return True # else: # return False def commas(string): '''Output a number with commas''' reverse_string = string[::-1] list_ = [] for elementno, character in enumerate(reverse_string): if elementno and elementno % 3 == 0: list_.append(',') list_.append(character) reversed_list = list_[::-1] return ''.join(reversed_list) def prefragment(): '''Fragment the heap''' time0 = time.time() linked_list = linked_list_mod.Linked_list() # 4.0 gigabytes max_size = int(2**30 * 5.0) num_strings = 0 sys.stderr.write('Starting prefragment: {}\n'.format(time.ctime(time0))) while True: # 1 to 1000 string_length = common.get_random_number(999) + 1 string = b'a' * string_length linked_list.append(string) bytes_in_use = memuse_mod.memory() gig_in_use = float(bytes_in_use) / (1024 * 1024 * 1024) num_strings += 1 if num_strings and num_strings % 1000000 == 0: sys.stderr.write('num_strings: {} gig_in_use: {}\n'.format(num_strings, gig_in_use)) if bytes_in_use >= max_size: break time1 = time.time() seconds = time1 - time0 minutes = seconds / 60.0 sys.stderr.write('{}: prefragment required {} minutes and {} strings\n'.format(sys.argv[0], minutes, num_strings)) # when we return, linked_list goes out of scope and can be garbage collected - no need for a del def possible_pause(options): '''Pause if relevant, until good time window and good load factor, but only if not options.just_run''' if not options.just_run: while True: is_good_time_window = in_good_time_window() is_good_load_factor = in_good_load_factor() if is_good_time_window: if is_good_load_factor: # Both conditions are good, return break else: # Good time window, bad load factor sys.stderr.write('{}: good time, bad load: {}\n'.format(sys.argv[0], time.ctime())) time.sleep(20) else: if is_good_load_factor: # Bad time window, good load factor sys.stderr.write('{}: bad time, good load: {}\n'.format(sys.argv[0], time.ctime())) time.sleep(60 * 10) else: # Bad time window, bad load factor sys.stderr.write('{}: bad time, bad load: {}\n'.format(sys.argv[0], time.ctime())) time.sleep(60 * 10) def test(options): ''' Perform tests for a given datastructure, number_pattern (sequential or random), number of operations, and heap state. ''' possible_pause(options) random.seed(0) get_percent = 5 set_percent = 95 total = get_percent + set_percent time0 = time.time() obj = options.datastructure.initializer() something_set = False for operationno in my_range(options.size): time1 = time.time() delta_time = time1 - time0 if options.too_long is not None and delta_time > options.too_long: # -1 Signifies a timeout return -1 dummy = operationno if common.get_random_number(total) < get_percent: # Do a get if not something_set: # ...unless there's nothing there to get yet continue key = options.number_pattern.next_get_key() if options.number_pattern.key_verifies(key, obj): pass else: sys.stderr.write('%s: key failed to verify: %s\n' % (sys.argv[0], key)) sys.exit(1) else: # Do a set something_set = True key = options.number_pattern.next_set_key() obj[key] = str(key) return delta_time class Options(object): # pylint: disable=too-few-public-methods,too-many-instance-attributes '''Hold and check command line options''' def __init__(self): self.datastructure = None self.number_pattern = None self.size = None self.just_run = False self.too_long = None while sys.argv[1:]: if sys.argv[1] == '--datastructure': self.datastructure = common.DATASTRUCTURES[python2x3.string_to_binary(sys.argv[2])] del sys.argv[1] elif sys.argv[1] == '--number-pattern': self.number_pattern = common.NUMBER_PATTERN[python2x3.string_to_binary(sys.argv[2])] del sys.argv[1] elif sys.argv[1] == '--size': self.size = int(sys.argv[2]) del sys.argv[1] elif sys.argv[1] == '--just-run': self.just_run = True elif sys.argv[1] == '--too-long': self.too_long = float(sys.argv[2]) del sys.argv[1] elif sys.argv[1] in {'-h', '--help'}: usage(0) else: sys.stderr.write('{}: Unrecognized option: {}\n'.format(sys.argv[0], sys.argv[1])) usage(1) del sys.argv[1] def check_options(self): '''Make sure command line options were reasonable''' if self.datastructure is None: sys.stderr.write('{}: --datastructure is a required option\n'.format(sys.argv[0])) usage(1) if self.number_pattern is None: sys.stderr.write('{}: --number-pattern is a required option\n'.format(sys.argv[0])) usage(1) if self.size is None: sys.stderr.write('{}: --size is a required option\n'.format(sys.argv[0])) usage(1) def main(): '''Main function''' sys.setrecursionlimit(2 ** 20) options = Options() options.check_options() duration = test(options) print('good {}'.format(duration)) sys.exit(0) main()