| #! /usr/bin/python3 -B |
| # |
| # Copyright (c) 2018-2020 Gavin D. Howard and contributors. |
| # |
| # All rights reserved. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions are met: |
| # |
| # * Redistributions of source code must retain the above copyright notice, this |
| # list of conditions and the following disclaimer. |
| # |
| # * Redistributions in binary form must reproduce the above copyright notice, |
| # this list of conditions and the following disclaimer in the documentation |
| # and/or other materials provided with the distribution. |
| # |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| # POSSIBILITY OF SUCH DAMAGE. |
| # |
| |
| import os |
| import sys |
| import subprocess |
| import time |
| |
| def usage(): |
| print("usage: {} [num_iterations test_num exe]".format(script)) |
| print("\n num_iterations is the number of times to run each karatsuba number; default is 4") |
| print("\n test_num is the last Karatsuba number to run through tests") |
| sys.exit(1) |
| |
| def run(cmd, env=None): |
| return subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) |
| |
| script = sys.argv[0] |
| testdir = os.path.dirname(script) |
| |
| print("\nWARNING: This script is for distro and package maintainers.") |
| print("It is for finding the optimal Karatsuba number.") |
| print("Though it only needs to be run once per release/platform,") |
| print("it takes forever to run.") |
| print("You have been warned.\n") |
| print("Note: If you send an interrupt, it will report the current best number.\n") |
| |
| if __name__ != "__main__": |
| usage() |
| |
| mx = 520 |
| mx2 = mx // 2 |
| mn = 16 |
| |
| num = "9" * mx |
| |
| args_idx = 4 |
| |
| if len(sys.argv) >= 2: |
| num_iterations = int(sys.argv[1]) |
| else: |
| num_iterations = 4 |
| |
| if len(sys.argv) >= 3: |
| test_num = int(sys.argv[2]) |
| else: |
| test_num = 0 |
| |
| if len(sys.argv) >= args_idx: |
| exe = sys.argv[3] |
| else: |
| exe = testdir + "/bin/bc" |
| |
| exedir = os.path.dirname(exe) |
| |
| indata = "for (i = 0; i < 100; ++i) {} * {}\n" |
| indata += "1.23456789^100000\n1.23456789^100000\nhalt" |
| indata = indata.format(num, num).encode() |
| |
| times = [] |
| nums = [] |
| runs = [] |
| nruns = num_iterations + 1 |
| |
| for i in range(0, nruns): |
| runs.append(0) |
| |
| tests = [ "multiply", "modulus", "power", "sqrt" ] |
| scripts = [ "multiply" ] |
| |
| print("Testing CFLAGS=\"-flto\"...") |
| |
| flags = dict(os.environ) |
| try: |
| flags["CFLAGS"] = flags["CFLAGS"] + " " + "-flto" |
| except KeyError: |
| flags["CFLAGS"] = "-flto" |
| |
| p = run([ "./configure.sh", "-O3" ], flags) |
| if p.returncode != 0: |
| print("configure.sh returned an error ({}); exiting...".format(p.returncode)) |
| sys.exit(p.returncode) |
| |
| p = run([ "make" ]) |
| |
| if p.returncode == 0: |
| config_env = flags |
| print("Using CFLAGS=\"-flto\"") |
| else: |
| config_env = os.environ |
| print("Not using CFLAGS=\"-flto\"") |
| |
| p = run([ "make", "clean" ]) |
| |
| print("Testing \"make -j4\"") |
| |
| if p.returncode != 0: |
| print("make returned an error ({}); exiting...".format(p.returncode)) |
| sys.exit(p.returncode) |
| |
| p = run([ "make", "-j4" ]) |
| |
| if p.returncode == 0: |
| makecmd = [ "make", "-j4" ] |
| print("Using \"make -j4\"") |
| else: |
| makecmd = [ "make" ] |
| print("Not using \"make -j4\"") |
| |
| if test_num != 0: |
| mx2 = test_num |
| |
| try: |
| |
| for i in range(mn, mx2 + 1): |
| |
| print("\nCompiling...\n") |
| |
| p = run([ "./configure.sh", "-O3", "-k{}".format(i) ], config_env) |
| |
| if p.returncode != 0: |
| print("configure.sh returned an error ({}); exiting...".format(p.returncode)) |
| sys.exit(p.returncode) |
| |
| p = run(makecmd) |
| |
| if p.returncode != 0: |
| print("make returned an error ({}); exiting...".format(p.returncode)) |
| sys.exit(p.returncode) |
| |
| if (test_num >= i): |
| |
| print("Running tests for Karatsuba Num: {}\n".format(i)) |
| |
| for test in tests: |
| |
| cmd = [ "{}/tests/test.sh".format(testdir), "bc", test, "0", "0", exe ] |
| |
| p = subprocess.run(cmd + sys.argv[args_idx:], stderr=subprocess.PIPE) |
| |
| if p.returncode != 0: |
| print("{} test failed:\n".format(test, p.returncode)) |
| print(p.stderr.decode()) |
| print("\nexiting...") |
| sys.exit(p.returncode) |
| |
| print("") |
| |
| for script in scripts: |
| |
| cmd = [ "{}/tests/script.sh".format(testdir), "bc", script + ".bc", |
| "0", "1", "1", "0", exe ] |
| |
| p = subprocess.run(cmd + sys.argv[args_idx:], stderr=subprocess.PIPE) |
| |
| if p.returncode != 0: |
| print("{} test failed:\n".format(test, p.returncode)) |
| print(p.stderr.decode()) |
| print("\nexiting...") |
| sys.exit(p.returncode) |
| |
| print("") |
| |
| elif test_num == 0: |
| |
| print("Timing Karatsuba Num: {}".format(i), end='', flush=True) |
| |
| for j in range(0, nruns): |
| |
| cmd = [ exe, "{}/tests/bc/power.txt".format(testdir) ] |
| |
| start = time.perf_counter() |
| p = subprocess.run(cmd, input=indata, stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
| end = time.perf_counter() |
| |
| if p.returncode != 0: |
| print("bc returned an error; exiting...") |
| sys.exit(p.returncode) |
| |
| runs[j] = end - start |
| |
| run_times = runs[1:] |
| avg = sum(run_times) / len(run_times) |
| |
| times.append(avg) |
| nums.append(i) |
| print(", Time: {}".format(times[i - mn])) |
| |
| except KeyboardInterrupt: |
| nums = nums[0:i] |
| times = times[0:i] |
| |
| if test_num == 0: |
| |
| opt = nums[times.index(min(times))] |
| |
| print("\n\nOptimal Karatsuba Num (for this machine): {}".format(opt)) |
| print("Run the following:\n") |
| if "-flto" in config_env["CFLAGS"]: |
| print("CFLAGS=\"-flto\" ./configure.sh -O3 -k {}".format(opt)) |
| else: |
| print("./configure.sh -O3 -k {}".format(opt)) |
| print("make") |