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