xref: /llvm-project/polly/lib/External/isl/imath/tools/findthreshold.py (revision 658eb9e14264d48888ade0e3daf0b648f76c3f0e)
1*658eb9e1SMichael Kruse#!/usr/bin/env python
2*658eb9e1SMichael Kruse##
3*658eb9e1SMichael Kruse## Name:     findthreshold.py
4*658eb9e1SMichael Kruse## Purpose:  Find a good threshold for recursive multiplication.
5*658eb9e1SMichael Kruse## Author:   M. J. Fromberger
6*658eb9e1SMichael Kruse##
7*658eb9e1SMichael Kruse## This tool computes some timing statistics to help you select a suitable
8*658eb9e1SMichael Kruse## recursive multiplication breakpoint.  It uses the imtimer tool to run a
9*658eb9e1SMichael Kruse## series of tests varying precision and breakpoint, and prints out a summary
10*658eb9e1SMichael Kruse## of the "best" values for each category.  Each summary line contains the
11*658eb9e1SMichael Kruse## following fields, tab-separated:
12*658eb9e1SMichael Kruse##
13*658eb9e1SMichael Kruse## prec     -- the precision of the operands (in digits).
14*658eb9e1SMichael Kruse## thresh   -- the threshold for recursive multiplication (digits).
15*658eb9e1SMichael Kruse## trec     -- total time using recursive algorithm (sec).
16*658eb9e1SMichael Kruse## tnorm    -- total time without recursive algorithm (sec).
17*658eb9e1SMichael Kruse## ratio    -- speedup (ratio of tnorm/trec).
18*658eb9e1SMichael Kruse##
19*658eb9e1SMichael Kruse## You are responsible for reading and interpreting the resulting table to
20*658eb9e1SMichael Kruse## obtain a useful value for your workload.  Change the default in imath.c, or
21*658eb9e1SMichael Kruse## call mp_int_multiply_threshold(n) during program initialization, to
22*658eb9e1SMichael Kruse## establish a satisfactory result.
23*658eb9e1SMichael Kruse##
24*658eb9e1SMichael Kruseimport math, os, random, sys, time
25*658eb9e1SMichael Kruse
26*658eb9e1SMichael Kruse
27*658eb9e1SMichael Krusedef get_timing_stats(num_tests, precision, threshold, seed=None):
28*658eb9e1SMichael Kruse    """Obtain timing statistics for multiplication.
29*658eb9e1SMichael Kruse
30*658eb9e1SMichael Kruse    num_tests      -- number of tests to run.
31*658eb9e1SMichael Kruse    precision      -- number of digits per operand.
32*658eb9e1SMichael Kruse    threshold      -- threshold in digits for recursive multiply.
33*658eb9e1SMichael Kruse    seed           -- random seed; if None, the clock is used.
34*658eb9e1SMichael Kruse
35*658eb9e1SMichael Kruse    Returns a tuple of (seed, bits, time) where seed is the random seed used,
36*658eb9e1SMichael Kruse    bits is the number of bits per operand, and time is a float giving the
37*658eb9e1SMichael Kruse    total time taken for the test run.
38*658eb9e1SMichael Kruse    """
39*658eb9e1SMichael Kruse    if seed is None:
40*658eb9e1SMichael Kruse        seed = int(time.time())
41*658eb9e1SMichael Kruse
42*658eb9e1SMichael Kruse    line = os.popen(
43*658eb9e1SMichael Kruse        './imtimer -mn -p %d -t %d -s %d %d' % (precision, threshold, seed,
44*658eb9e1SMichael Kruse                                                num_tests), 'r').readline()
45*658eb9e1SMichael Kruse
46*658eb9e1SMichael Kruse    count, prec, bits, thresh, status = line.strip().split('\t')
47*658eb9e1SMichael Kruse    kind, total, unit = status.split()
48*658eb9e1SMichael Kruse
49*658eb9e1SMichael Kruse    return seed, int(bits), float(total)
50*658eb9e1SMichael Kruse
51*658eb9e1SMichael Kruse
52*658eb9e1SMichael Krusedef check_binary(name):
53*658eb9e1SMichael Kruse    if not os.path.exists(name):
54*658eb9e1SMichael Kruse        os.system('make %s' % name)
55*658eb9e1SMichael Kruse        if not os.path.exists(name):
56*658eb9e1SMichael Kruse            raise ValueError("Unable to build %s" % name)
57*658eb9e1SMichael Kruse    elif not os.path.isfile(name):
58*658eb9e1SMichael Kruse        raise ValueError("Path exists with wrong type")
59*658eb9e1SMichael Kruse
60*658eb9e1SMichael Kruse
61*658eb9e1SMichael Krusedef compute_stats():
62*658eb9e1SMichael Kruse    check_binary('imtimer')
63*658eb9e1SMichael Kruse    seed = int(time.time())
64*658eb9e1SMichael Kruse
65*658eb9e1SMichael Kruse    print >> sys.stderr, "Computing timer statistics (this may take a while)"
66*658eb9e1SMichael Kruse    stats = {}
67*658eb9e1SMichael Kruse    for prec in (32, 40, 64, 80, 128, 150, 256, 384, 512, 600, 768, 1024):
68*658eb9e1SMichael Kruse        sys.stderr.write('%-4d ' % prec)
69*658eb9e1SMichael Kruse        stats[prec] = (None, 1000000., 0.)
70*658eb9e1SMichael Kruse
71*658eb9e1SMichael Kruse        for thresh in xrange(8, 65, 2):
72*658eb9e1SMichael Kruse            s, b, t = get_timing_stats(1000, prec, thresh, seed)
73*658eb9e1SMichael Kruse            sp, bp, tp = get_timing_stats(1000, prec, prec + 1, seed)
74*658eb9e1SMichael Kruse
75*658eb9e1SMichael Kruse            if t < stats[prec][1]:
76*658eb9e1SMichael Kruse                stats[prec] = (thresh, t, tp)
77*658eb9e1SMichael Kruse                sys.stderr.write('+')
78*658eb9e1SMichael Kruse            else:
79*658eb9e1SMichael Kruse                sys.stderr.write('.')
80*658eb9e1SMichael Kruse        sys.stderr.write('\n')
81*658eb9e1SMichael Kruse
82*658eb9e1SMichael Kruse    return list((p, h, t, tp) for p, (h, t, tp) in stats.iteritems())
83*658eb9e1SMichael Kruse
84*658eb9e1SMichael Kruse
85*658eb9e1SMichael Kruseif __name__ == "__main__":
86*658eb9e1SMichael Kruse    stats = compute_stats()
87*658eb9e1SMichael Kruse    stats.sort(key=lambda s: s[3] / s[2])
88*658eb9e1SMichael Kruse    for prec, thresh, trec, tnorm in stats:
89*658eb9e1SMichael Kruse        print "%d\t%d\t%.3f\t%.3f\t%.4f" % (prec, thresh, trec, tnorm,
90*658eb9e1SMichael Kruse                                            tnorm / trec)
91*658eb9e1SMichael Kruse
92*658eb9e1SMichael Kruse    print
93*658eb9e1SMichael Kruse
94*658eb9e1SMichael Kruse# Here there be dragons
95