Skip to content

Commit

Permalink
Add utils/scale-test
Browse files Browse the repository at this point in the history
  • Loading branch information
graydon committed Oct 11, 2016
1 parent 66da45b commit 0b3e451
Showing 1 changed file with 120 additions and 0 deletions.
120 changes: 120 additions & 0 deletions utils/scale-test
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#!/usr/bin/env python
#
# -*- python -*-
#
# Runs a .gyb scale-testing file repeatedly through swiftc while varying a
# scaling variable 'N', collects json stats from the compiler, transforms the
# problem to log-space and runs a linear regression to estimate the exponent on
# the stat's growth curve relative to N.
#
# The estimate will be more accurate as N increases, so if you get a
# not-terribly-convincing estimate, try increasing --begin and --end to larger
# values.
#

import gyb, os, os.path


def write_input_file(args, ast, d, n):
ifile = os.path.join(d, "in%d.swift" % n)
with open(ifile,'w+') as f:
f.write(gyb.execute_template(ast, '', N=n))
return ifile


def run_once(args, ast, rng):
import sys, shutil, tempfile, subprocess, json
try:
d = tempfile.mkdtemp()
stats = os.path.join(d, "stats.json")
inputs = [write_input_file(args, ast, d, i) for i in rng]
primary = inputs[-1]
ofile = os.path.join(d, "out.o")
mode = "-c"
if args.parse:
mode = "-parse"
subprocess.check_call([args.swiftc_binary,
"-frontend", mode,
"-o", ofile,
"-Xllvm", "-stats",
"-Xllvm", "-stats-json",
"-Xllvm", "-info-output-file=" + stats,
"-primary-file", primary] + inputs)
with open(stats) as f:
return json.load(f)
finally:
shutil.rmtree(d)


def run_many(args):
ast = gyb.parse_template(args.file.name, args.file.read())
rng = range(args.begin, args.end, args.step)
if args.multi_file:
return (rng, [run_once(args, ast, rng[0:i+1]) for i in range(len(rng))])
else:
return (rng, [run_once(args, ast, [r]) for r in rng])


def report(args, rng, runs):
import numpy as np
bad = False
keys = set.intersection(*[set(j.keys()) for j in runs])
A = np.vstack([np.log(rng), np.ones(len(rng))]).T
rows = []
for k in keys:
vals = [r[k] for r in runs]
bounded = [max(v, 1) for v in vals]
b, a = np.linalg.lstsq(A, np.log(bounded))[0]
b = 0 if np.isclose(b, 0) else b
rows.append((b, k, vals))
rows.sort()
tolerance = 1.2
for (b, k, vals) in rows:
if b >= tolerance:
bad = True
if not args.quiet or b >= tolerance:
print "O(n^%1.1f) : %s" % (b, k)
if args.values:
print " = ", vals
return bad

def main():
import argparse, sys
parser = argparse.ArgumentParser()
parser.add_argument(
'file', type=argparse.FileType(),
help='Path to GYB template file (defaults to stdin)', nargs='?',
default=sys.stdin)
parser.add_argument(
'--values', action='store_true',
default=False, help='print stat values')
parser.add_argument(
'--quiet', action='store_true',
default=False, help='only print superlinear stats')
parser.add_argument(
'--parse', action='store_true',
default=False, help='only run compiler with -parse')
parser.add_argument(
'--multi-file', action='store_true',
default=False, help='vary number of input files as well')
parser.add_argument(
'--begin', type=int,
default=10, help='first value for N')
parser.add_argument(
'--end', type=int,
default=100, help='last value for N')
parser.add_argument(
'--step', type=int,
default=10, help='step value for N')
parser.add_argument(
'--swiftc-binary',
default="swiftc", help='swift binary to execute')

args = parser.parse_args(sys.argv[1:])
(rng, runs) = run_many(args)
if report(args, rng, runs):
exit(1)
exit(0)

if __name__ == '__main__':
main()

0 comments on commit 0b3e451

Please sign in to comment.