1*5a6e8488SAndroid Build Coastguard Worker#! /usr/bin/python3 -B 2*5a6e8488SAndroid Build Coastguard Worker# 3*5a6e8488SAndroid Build Coastguard Worker# SPDX-License-Identifier: BSD-2-Clause 4*5a6e8488SAndroid Build Coastguard Worker# 5*5a6e8488SAndroid Build Coastguard Worker# Copyright (c) 2018-2024 Gavin D. Howard and contributors. 6*5a6e8488SAndroid Build Coastguard Worker# 7*5a6e8488SAndroid Build Coastguard Worker# Redistribution and use in source and binary forms, with or without 8*5a6e8488SAndroid Build Coastguard Worker# modification, are permitted provided that the following conditions are met: 9*5a6e8488SAndroid Build Coastguard Worker# 10*5a6e8488SAndroid Build Coastguard Worker# * Redistributions of source code must retain the above copyright notice, this 11*5a6e8488SAndroid Build Coastguard Worker# list of conditions and the following disclaimer. 12*5a6e8488SAndroid Build Coastguard Worker# 13*5a6e8488SAndroid Build Coastguard Worker# * Redistributions in binary form must reproduce the above copyright notice, 14*5a6e8488SAndroid Build Coastguard Worker# this list of conditions and the following disclaimer in the documentation 15*5a6e8488SAndroid Build Coastguard Worker# and/or other materials provided with the distribution. 16*5a6e8488SAndroid Build Coastguard Worker# 17*5a6e8488SAndroid Build Coastguard Worker# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18*5a6e8488SAndroid Build Coastguard Worker# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19*5a6e8488SAndroid Build Coastguard Worker# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20*5a6e8488SAndroid Build Coastguard Worker# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21*5a6e8488SAndroid Build Coastguard Worker# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22*5a6e8488SAndroid Build Coastguard Worker# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23*5a6e8488SAndroid Build Coastguard Worker# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24*5a6e8488SAndroid Build Coastguard Worker# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25*5a6e8488SAndroid Build Coastguard Worker# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26*5a6e8488SAndroid Build Coastguard Worker# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27*5a6e8488SAndroid Build Coastguard Worker# POSSIBILITY OF SUCH DAMAGE. 28*5a6e8488SAndroid Build Coastguard Worker# 29*5a6e8488SAndroid Build Coastguard Worker 30*5a6e8488SAndroid Build Coastguard Workerimport os 31*5a6e8488SAndroid Build Coastguard Workerimport sys 32*5a6e8488SAndroid Build Coastguard Workerimport subprocess 33*5a6e8488SAndroid Build Coastguard Workerimport time 34*5a6e8488SAndroid Build Coastguard Worker 35*5a6e8488SAndroid Build Coastguard Worker# Print the usage and exit with an error. 36*5a6e8488SAndroid Build Coastguard Workerdef usage(): 37*5a6e8488SAndroid Build Coastguard Worker print("usage: {} [num_iterations test_num exe]".format(script)) 38*5a6e8488SAndroid Build Coastguard Worker print("\n num_iterations is the number of times to run each karatsuba number; default is 4") 39*5a6e8488SAndroid Build Coastguard Worker print("\n test_num is the last Karatsuba number to run through tests") 40*5a6e8488SAndroid Build Coastguard Worker sys.exit(1) 41*5a6e8488SAndroid Build Coastguard Worker 42*5a6e8488SAndroid Build Coastguard Worker# Run a command. This is basically an alias. 43*5a6e8488SAndroid Build Coastguard Workerdef run(cmd, env=None): 44*5a6e8488SAndroid Build Coastguard Worker return subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) 45*5a6e8488SAndroid Build Coastguard Worker 46*5a6e8488SAndroid Build Coastguard Workerscript = sys.argv[0] 47*5a6e8488SAndroid Build Coastguard Workertestdir = os.path.dirname(script) 48*5a6e8488SAndroid Build Coastguard Worker 49*5a6e8488SAndroid Build Coastguard Workerif testdir == "": 50*5a6e8488SAndroid Build Coastguard Worker testdir = os.getcwd() 51*5a6e8488SAndroid Build Coastguard Worker 52*5a6e8488SAndroid Build Coastguard Workerprint("\nWARNING: This script is for distro and package maintainers.") 53*5a6e8488SAndroid Build Coastguard Workerprint("It is for finding the optimal Karatsuba number.") 54*5a6e8488SAndroid Build Coastguard Workerprint("Though it only needs to be run once per release/platform,") 55*5a6e8488SAndroid Build Coastguard Workerprint("it takes forever to run.") 56*5a6e8488SAndroid Build Coastguard Workerprint("You have been warned.\n") 57*5a6e8488SAndroid Build Coastguard Workerprint("Note: If you send an interrupt, it will report the current best number.\n") 58*5a6e8488SAndroid Build Coastguard Worker 59*5a6e8488SAndroid Build Coastguard Worker# This script has to be run by itself. 60*5a6e8488SAndroid Build Coastguard Workerif __name__ != "__main__": 61*5a6e8488SAndroid Build Coastguard Worker usage() 62*5a6e8488SAndroid Build Coastguard Worker 63*5a6e8488SAndroid Build Coastguard Worker# These constants can be changed, but I found they work well enough. 64*5a6e8488SAndroid Build Coastguard Workermx = 520 65*5a6e8488SAndroid Build Coastguard Workermx2 = mx // 2 66*5a6e8488SAndroid Build Coastguard Workermn = 16 67*5a6e8488SAndroid Build Coastguard Worker 68*5a6e8488SAndroid Build Coastguard Workernum = "9" * mx 69*5a6e8488SAndroid Build Coastguard Worker 70*5a6e8488SAndroid Build Coastguard Workerargs_idx = 4 71*5a6e8488SAndroid Build Coastguard Worker 72*5a6e8488SAndroid Build Coastguard Worker# Command-line processing. 73*5a6e8488SAndroid Build Coastguard Workerif len(sys.argv) >= 2: 74*5a6e8488SAndroid Build Coastguard Worker num_iterations = int(sys.argv[1]) 75*5a6e8488SAndroid Build Coastguard Workerelse: 76*5a6e8488SAndroid Build Coastguard Worker num_iterations = 4 77*5a6e8488SAndroid Build Coastguard Worker 78*5a6e8488SAndroid Build Coastguard Workerif len(sys.argv) >= 3: 79*5a6e8488SAndroid Build Coastguard Worker test_num = int(sys.argv[2]) 80*5a6e8488SAndroid Build Coastguard Workerelse: 81*5a6e8488SAndroid Build Coastguard Worker test_num = 0 82*5a6e8488SAndroid Build Coastguard Worker 83*5a6e8488SAndroid Build Coastguard Workerif len(sys.argv) >= args_idx: 84*5a6e8488SAndroid Build Coastguard Worker exe = sys.argv[3] 85*5a6e8488SAndroid Build Coastguard Workerelse: 86*5a6e8488SAndroid Build Coastguard Worker exe = testdir + "/bin/bc" 87*5a6e8488SAndroid Build Coastguard Worker 88*5a6e8488SAndroid Build Coastguard Workerexedir = os.path.dirname(exe) 89*5a6e8488SAndroid Build Coastguard Worker 90*5a6e8488SAndroid Build Coastguard Worker# Some basic tests. 91*5a6e8488SAndroid Build Coastguard Workerindata = "for (i = 0; i < 100; ++i) {} * {}\n" 92*5a6e8488SAndroid Build Coastguard Workerindata += "1.23456789^100000\n1.23456789^100000\nhalt" 93*5a6e8488SAndroid Build Coastguard Workerindata = indata.format(num, num).encode() 94*5a6e8488SAndroid Build Coastguard Worker 95*5a6e8488SAndroid Build Coastguard Workertimes = [] 96*5a6e8488SAndroid Build Coastguard Workernums = [] 97*5a6e8488SAndroid Build Coastguard Workerruns = [] 98*5a6e8488SAndroid Build Coastguard Workernruns = num_iterations + 1 99*5a6e8488SAndroid Build Coastguard Worker 100*5a6e8488SAndroid Build Coastguard Worker# We build the list first because I want to just edit slots. 101*5a6e8488SAndroid Build Coastguard Workerfor i in range(0, nruns): 102*5a6e8488SAndroid Build Coastguard Worker runs.append(0) 103*5a6e8488SAndroid Build Coastguard Worker 104*5a6e8488SAndroid Build Coastguard Workertests = [ "multiply", "modulus", "power", "sqrt" ] 105*5a6e8488SAndroid Build Coastguard Workerscripts = [ "multiply" ] 106*5a6e8488SAndroid Build Coastguard Worker 107*5a6e8488SAndroid Build Coastguard Worker# Test Link-Time Optimization. 108*5a6e8488SAndroid Build Coastguard Workerprint("Testing CFLAGS=\"-flto\"...") 109*5a6e8488SAndroid Build Coastguard Worker 110*5a6e8488SAndroid Build Coastguard Workerflags = dict(os.environ) 111*5a6e8488SAndroid Build Coastguard Workertry: 112*5a6e8488SAndroid Build Coastguard Worker flags["CFLAGS"] = flags["CFLAGS"] + " " + "-flto" 113*5a6e8488SAndroid Build Coastguard Workerexcept KeyError: 114*5a6e8488SAndroid Build Coastguard Worker flags["CFLAGS"] = "-flto" 115*5a6e8488SAndroid Build Coastguard Worker 116*5a6e8488SAndroid Build Coastguard Workerp = run([ "{}/../configure.sh".format(testdir), "-O3" ], flags) 117*5a6e8488SAndroid Build Coastguard Workerif p.returncode != 0: 118*5a6e8488SAndroid Build Coastguard Worker print("configure.sh returned an error ({}); exiting...".format(p.returncode)) 119*5a6e8488SAndroid Build Coastguard Worker sys.exit(p.returncode) 120*5a6e8488SAndroid Build Coastguard Worker 121*5a6e8488SAndroid Build Coastguard Workerp = run([ "make" ]) 122*5a6e8488SAndroid Build Coastguard Worker 123*5a6e8488SAndroid Build Coastguard Workerif p.returncode == 0: 124*5a6e8488SAndroid Build Coastguard Worker config_env = flags 125*5a6e8488SAndroid Build Coastguard Worker print("Using CFLAGS=\"-flto\"") 126*5a6e8488SAndroid Build Coastguard Workerelse: 127*5a6e8488SAndroid Build Coastguard Worker config_env = os.environ 128*5a6e8488SAndroid Build Coastguard Worker print("Not using CFLAGS=\"-flto\"") 129*5a6e8488SAndroid Build Coastguard Worker 130*5a6e8488SAndroid Build Coastguard Workerp = run([ "make", "clean" ]) 131*5a6e8488SAndroid Build Coastguard Worker 132*5a6e8488SAndroid Build Coastguard Worker# Test parallel build. My machine has 16 cores. 133*5a6e8488SAndroid Build Coastguard Workerprint("Testing \"make -j16\"") 134*5a6e8488SAndroid Build Coastguard Worker 135*5a6e8488SAndroid Build Coastguard Workerif p.returncode != 0: 136*5a6e8488SAndroid Build Coastguard Worker print("make returned an error ({}); exiting...".format(p.returncode)) 137*5a6e8488SAndroid Build Coastguard Worker sys.exit(p.returncode) 138*5a6e8488SAndroid Build Coastguard Worker 139*5a6e8488SAndroid Build Coastguard Workerp = run([ "make", "-j16" ]) 140*5a6e8488SAndroid Build Coastguard Worker 141*5a6e8488SAndroid Build Coastguard Workerif p.returncode == 0: 142*5a6e8488SAndroid Build Coastguard Worker makecmd = [ "make", "-j16" ] 143*5a6e8488SAndroid Build Coastguard Worker print("Using \"make -j16\"") 144*5a6e8488SAndroid Build Coastguard Workerelse: 145*5a6e8488SAndroid Build Coastguard Worker makecmd = [ "make" ] 146*5a6e8488SAndroid Build Coastguard Worker print("Not using \"make -j16\"") 147*5a6e8488SAndroid Build Coastguard Worker 148*5a6e8488SAndroid Build Coastguard Worker# Set the max if the user did. 149*5a6e8488SAndroid Build Coastguard Workerif test_num != 0: 150*5a6e8488SAndroid Build Coastguard Worker mx2 = test_num 151*5a6e8488SAndroid Build Coastguard Worker 152*5a6e8488SAndroid Build Coastguard Worker# This is the meat here. 153*5a6e8488SAndroid Build Coastguard Workertry: 154*5a6e8488SAndroid Build Coastguard Worker 155*5a6e8488SAndroid Build Coastguard Worker # For each possible KARATSUBA_LEN... 156*5a6e8488SAndroid Build Coastguard Worker for i in range(mn, mx2 + 1): 157*5a6e8488SAndroid Build Coastguard Worker 158*5a6e8488SAndroid Build Coastguard Worker # Configure and compile. 159*5a6e8488SAndroid Build Coastguard Worker print("\nCompiling...\n") 160*5a6e8488SAndroid Build Coastguard Worker 161*5a6e8488SAndroid Build Coastguard Worker p = run([ "{}/../configure.sh".format(testdir), "-O3", "-k{}".format(i) ], config_env) 162*5a6e8488SAndroid Build Coastguard Worker 163*5a6e8488SAndroid Build Coastguard Worker if p.returncode != 0: 164*5a6e8488SAndroid Build Coastguard Worker print("configure.sh returned an error ({}); exiting...".format(p.returncode)) 165*5a6e8488SAndroid Build Coastguard Worker sys.exit(p.returncode) 166*5a6e8488SAndroid Build Coastguard Worker 167*5a6e8488SAndroid Build Coastguard Worker p = run(makecmd) 168*5a6e8488SAndroid Build Coastguard Worker 169*5a6e8488SAndroid Build Coastguard Worker if p.returncode != 0: 170*5a6e8488SAndroid Build Coastguard Worker print("make returned an error ({}); exiting...".format(p.returncode)) 171*5a6e8488SAndroid Build Coastguard Worker sys.exit(p.returncode) 172*5a6e8488SAndroid Build Coastguard Worker 173*5a6e8488SAndroid Build Coastguard Worker # Test if desired. 174*5a6e8488SAndroid Build Coastguard Worker if (test_num >= i): 175*5a6e8488SAndroid Build Coastguard Worker 176*5a6e8488SAndroid Build Coastguard Worker print("Running tests for Karatsuba Num: {}\n".format(i)) 177*5a6e8488SAndroid Build Coastguard Worker 178*5a6e8488SAndroid Build Coastguard Worker for test in tests: 179*5a6e8488SAndroid Build Coastguard Worker 180*5a6e8488SAndroid Build Coastguard Worker cmd = [ "{}/../tests/test.sh".format(testdir), "bc", test, "0", "0", exe ] 181*5a6e8488SAndroid Build Coastguard Worker 182*5a6e8488SAndroid Build Coastguard Worker p = subprocess.run(cmd + sys.argv[args_idx:], stderr=subprocess.PIPE) 183*5a6e8488SAndroid Build Coastguard Worker 184*5a6e8488SAndroid Build Coastguard Worker if p.returncode != 0: 185*5a6e8488SAndroid Build Coastguard Worker print("{} test failed:\n".format(test, p.returncode)) 186*5a6e8488SAndroid Build Coastguard Worker print(p.stderr.decode()) 187*5a6e8488SAndroid Build Coastguard Worker print("\nexiting...") 188*5a6e8488SAndroid Build Coastguard Worker sys.exit(p.returncode) 189*5a6e8488SAndroid Build Coastguard Worker 190*5a6e8488SAndroid Build Coastguard Worker print("") 191*5a6e8488SAndroid Build Coastguard Worker 192*5a6e8488SAndroid Build Coastguard Worker for script in scripts: 193*5a6e8488SAndroid Build Coastguard Worker 194*5a6e8488SAndroid Build Coastguard Worker cmd = [ "{}/../tests/script.sh".format(testdir), "bc", script + ".bc", 195*5a6e8488SAndroid Build Coastguard Worker "0", "1", "1", "0", exe ] 196*5a6e8488SAndroid Build Coastguard Worker 197*5a6e8488SAndroid Build Coastguard Worker p = subprocess.run(cmd + sys.argv[args_idx:], stderr=subprocess.PIPE) 198*5a6e8488SAndroid Build Coastguard Worker 199*5a6e8488SAndroid Build Coastguard Worker if p.returncode != 0: 200*5a6e8488SAndroid Build Coastguard Worker print("{} test failed:\n".format(test, p.returncode)) 201*5a6e8488SAndroid Build Coastguard Worker print(p.stderr.decode()) 202*5a6e8488SAndroid Build Coastguard Worker print("\nexiting...") 203*5a6e8488SAndroid Build Coastguard Worker sys.exit(p.returncode) 204*5a6e8488SAndroid Build Coastguard Worker 205*5a6e8488SAndroid Build Coastguard Worker print("") 206*5a6e8488SAndroid Build Coastguard Worker 207*5a6e8488SAndroid Build Coastguard Worker # If testing was *not* desired, assume the user wanted to time it. 208*5a6e8488SAndroid Build Coastguard Worker elif test_num == 0: 209*5a6e8488SAndroid Build Coastguard Worker 210*5a6e8488SAndroid Build Coastguard Worker print("Timing Karatsuba Num: {}".format(i), end='', flush=True) 211*5a6e8488SAndroid Build Coastguard Worker 212*5a6e8488SAndroid Build Coastguard Worker for j in range(0, nruns): 213*5a6e8488SAndroid Build Coastguard Worker 214*5a6e8488SAndroid Build Coastguard Worker cmd = [ exe, "{}/../tests/bc/power.txt".format(testdir) ] 215*5a6e8488SAndroid Build Coastguard Worker 216*5a6e8488SAndroid Build Coastguard Worker start = time.perf_counter() 217*5a6e8488SAndroid Build Coastguard Worker p = subprocess.run(cmd, input=indata, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 218*5a6e8488SAndroid Build Coastguard Worker end = time.perf_counter() 219*5a6e8488SAndroid Build Coastguard Worker 220*5a6e8488SAndroid Build Coastguard Worker if p.returncode != 0: 221*5a6e8488SAndroid Build Coastguard Worker print("bc returned an error; exiting...") 222*5a6e8488SAndroid Build Coastguard Worker sys.exit(p.returncode) 223*5a6e8488SAndroid Build Coastguard Worker 224*5a6e8488SAndroid Build Coastguard Worker runs[j] = end - start 225*5a6e8488SAndroid Build Coastguard Worker 226*5a6e8488SAndroid Build Coastguard Worker run_times = runs[1:] 227*5a6e8488SAndroid Build Coastguard Worker avg = sum(run_times) / len(run_times) 228*5a6e8488SAndroid Build Coastguard Worker 229*5a6e8488SAndroid Build Coastguard Worker times.append(avg) 230*5a6e8488SAndroid Build Coastguard Worker nums.append(i) 231*5a6e8488SAndroid Build Coastguard Worker print(", Time: {}".format(times[i - mn])) 232*5a6e8488SAndroid Build Coastguard Worker 233*5a6e8488SAndroid Build Coastguard Workerexcept KeyboardInterrupt: 234*5a6e8488SAndroid Build Coastguard Worker # When timing, we want to quit when the user tells us to. However, we also 235*5a6e8488SAndroid Build Coastguard Worker # want to report the best run, so we make sure to grab the times here before 236*5a6e8488SAndroid Build Coastguard Worker # moving on. 237*5a6e8488SAndroid Build Coastguard Worker nums = nums[0:i] 238*5a6e8488SAndroid Build Coastguard Worker times = times[0:i] 239*5a6e8488SAndroid Build Coastguard Worker 240*5a6e8488SAndroid Build Coastguard Worker# If running timed tests... 241*5a6e8488SAndroid Build Coastguard Workerif test_num == 0: 242*5a6e8488SAndroid Build Coastguard Worker 243*5a6e8488SAndroid Build Coastguard Worker # Report the optimal KARATSUBA_LEN 244*5a6e8488SAndroid Build Coastguard Worker opt = nums[times.index(min(times))] 245*5a6e8488SAndroid Build Coastguard Worker 246*5a6e8488SAndroid Build Coastguard Worker print("\n\nOptimal Karatsuba Num (for this machine): {}".format(opt)) 247*5a6e8488SAndroid Build Coastguard Worker print("Run the following:\n") 248*5a6e8488SAndroid Build Coastguard Worker if "-flto" in config_env["CFLAGS"]: 249*5a6e8488SAndroid Build Coastguard Worker print("CFLAGS=\"-flto\" ./configure.sh -O3 -k {}".format(opt)) 250*5a6e8488SAndroid Build Coastguard Worker else: 251*5a6e8488SAndroid Build Coastguard Worker print("./configure.sh -O3 -k {}".format(opt)) 252*5a6e8488SAndroid Build Coastguard Worker print("make") 253