1#! /usr/bin/python3 -B 2# 3# Copyright (c) 2018-2019 Gavin D. Howard and contributors. 4# 5# All rights reserved. 6# 7# Redistribution and use in source and binary forms, with or without 8# modification, are permitted provided that the following conditions are met: 9# 10# * Redistributions of source code must retain the above copyright notice, this 11# list of conditions and the following disclaimer. 12# 13# * Redistributions in binary form must reproduce the above copyright notice, 14# this list of conditions and the following disclaimer in the documentation 15# and/or other materials provided with the distribution. 16# 17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27# POSSIBILITY OF SUCH DAMAGE. 28# 29 30import os 31import sys 32import subprocess 33import time 34 35def usage(): 36 print("usage: {} [test_num exe]".format(script)) 37 print("\n test_num is the last Karatsuba number to run through tests") 38 sys.exit(1) 39 40script = sys.argv[0] 41testdir = os.path.dirname(script) 42 43print("\nWARNING: This script is for distro and package maintainers.") 44print("It is for finding the optimal Karatsuba number.") 45print("Though it only needs to be run once per release/platform,") 46print("it takes forever to run.") 47print("You have been warned.\n") 48print("Note: If you send an interrupt, it will report the current best number.\n") 49 50if __name__ != "__main__": 51 usage() 52 53mx = 520 54mx2 = mx // 2 55mn = 16 56 57num = "9" * mx 58 59if len(sys.argv) >= 2: 60 test_num = int(sys.argv[1]) 61else: 62 test_num = 0 63 64if len(sys.argv) >= 3: 65 exe = sys.argv[2] 66else: 67 exe = testdir + "/bin/bc" 68 69exedir = os.path.dirname(exe) 70 71indata = "for (i = 0; i < 100; ++i) {} * {}\n" 72indata += "1.23456789^100000\n1.23456789^100000\nhalt" 73indata = indata.format(num, num) 74 75times = [] 76nums = [] 77runs = [] 78nruns = 5 79 80for i in range(0, nruns): 81 runs.append(0) 82 83tests = [ "multiply", "modulus", "power", "sqrt" ] 84scripts = [ "multiply" ] 85 86if test_num != 0: 87 mx2 = test_num 88 89try: 90 91 for i in range(mn, mx2 + 1): 92 93 print("\nCompiling...\n") 94 95 makecmd = [ "./configure.sh", "-O3", "-k{}".format(i) ] 96 p = subprocess.run(makecmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 97 98 if p.returncode != 0: 99 print("configure.sh returned an error ({}); exiting...".format(p.returncode)) 100 sys.exit(p.returncode) 101 102 makecmd = [ "make" ] 103 p = subprocess.run(makecmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 104 105 if p.returncode != 0: 106 print("make returned an error ({}); exiting...".format(p.returncode)) 107 sys.exit(p.returncode) 108 109 if (test_num >= i): 110 111 print("Running tests for Karatsuba Num: {}\n".format(i)) 112 113 for test in tests: 114 115 cmd = [ "{}/tests/test.sh".format(testdir), "bc", test, "0", exe ] 116 117 p = subprocess.run(cmd + sys.argv[3:], stderr=subprocess.PIPE) 118 119 if p.returncode != 0: 120 print("{} test failed:\n".format(test, p.returncode)) 121 print(p.stderr.decode()) 122 print("\nexiting...") 123 sys.exit(p.returncode) 124 125 print("") 126 127 for script in scripts: 128 129 cmd = [ "{}/tests/script.sh".format(testdir), "bc", script + ".bc", 130 "0", "1", "0", exe ] 131 132 p = subprocess.run(cmd + sys.argv[3:], stderr=subprocess.PIPE) 133 134 if p.returncode != 0: 135 print("{} test failed:\n".format(test, p.returncode)) 136 print(p.stderr.decode()) 137 print("\nexiting...") 138 sys.exit(p.returncode) 139 140 print("") 141 142 elif test_num == 0: 143 144 print("Timing Karatsuba Num: {}".format(i), end='', flush=True) 145 146 for j in range(0, nruns): 147 148 cmd = [ exe, "{}/tests/bc/power.txt".format(testdir) ] 149 150 start = time.perf_counter() 151 p = subprocess.run(cmd, input=indata.encode(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) 152 end = time.perf_counter() 153 154 if p.returncode != 0: 155 print("bc returned an error; exiting...") 156 sys.exit(p.returncode) 157 158 runs[j] = end - start 159 160 run_times = runs[1:] 161 avg = sum(run_times) / len(run_times) 162 163 times.append(avg) 164 nums.append(i) 165 print(", Time: {}".format(times[i - mn])) 166 167except KeyboardInterrupt: 168 nums = nums[0:i] 169 times = times[0:i] 170 171if test_num == 0: 172 173 opt = nums[times.index(min(times))] 174 175 print("\n\nOptimal Karatsuba Num (for this machine): {}".format(opt)) 176 print("Run the following:\n") 177 print("./configure.sh -O3 -k {}".format(opt)) 178 print("make") 179