2014-09-02 22:03:20 -04:00
|
|
|
from __future__ import division
|
|
|
|
|
2014-10-27 03:28:46 -04:00
|
|
|
import argparse, itertools, os, sys, json
|
2014-10-28 01:10:14 -04:00
|
|
|
import misc_tools, optimize, dataset
|
2014-10-15 04:24:25 -04:00
|
|
|
import pyatidlas as atd
|
2014-10-27 03:28:46 -04:00
|
|
|
import numpy as np
|
2014-09-02 22:03:20 -04:00
|
|
|
|
2014-10-15 04:24:25 -04:00
|
|
|
from numpy import random
|
|
|
|
from model import train_model
|
|
|
|
|
2014-10-27 03:28:46 -04:00
|
|
|
|
2015-01-12 13:20:53 -05:00
|
|
|
TYPES = { 'vaxpy': {'template':atd.vaxpy,
|
2014-11-02 10:05:14 -05:00
|
|
|
'perf-index':lambda x: 3*x[0]*x[1][0]/x[2]*1e-9,
|
2014-09-06 00:39:38 -04:00
|
|
|
'perf-measure':'GB/s'},
|
2014-09-29 03:01:33 +02:00
|
|
|
|
2015-01-12 13:20:53 -05:00
|
|
|
'maxpy': {'template':atd.maxpy,
|
2014-11-02 10:05:14 -05:00
|
|
|
'perf-index':lambda x: 3*x[0]*x[1][0]*x[1][1]/x[2]*1e-9,
|
2014-09-06 00:39:38 -04:00
|
|
|
'perf-measure':'GB/s'},
|
2014-09-29 03:01:33 +02:00
|
|
|
|
2015-01-12 13:20:53 -05:00
|
|
|
'dot': {'template':atd.reduction,
|
2014-10-05 06:33:50 +02:00
|
|
|
'perf-index':lambda x: 2*x[0]*x[1][0]/x[2]*1e-9,
|
2014-09-06 00:39:38 -04:00
|
|
|
'perf-measure':'GB/s'},
|
2014-09-29 03:01:33 +02:00
|
|
|
|
2015-01-12 13:20:53 -05:00
|
|
|
'gemv': {'template': {'N': atd.mreduction_rows, 'T': atd.mreduction_cols},
|
2014-09-06 00:39:38 -04:00
|
|
|
'perf-index':lambda x: x[0]*x[1][0]*x[1][1]/x[2]*1e-9,
|
|
|
|
'perf-measure':'GB/s'},
|
2014-09-29 03:01:33 +02:00
|
|
|
|
2015-01-12 13:20:53 -05:00
|
|
|
'gemm': {'template': {('N','N'): atd.mproduct_nn, ('T','N'): atd.mproduct_tn,
|
|
|
|
('N','T'): atd.mproduct_nt, ('T','T'): atd.mproduct_tt},
|
2014-09-06 00:39:38 -04:00
|
|
|
'perf-index': lambda x: 2*x[1][0]*x[1][1]*x[1][2]/x[2]*1e-9,
|
|
|
|
'perf-measure': 'GFLOP/s'} }
|
2014-09-29 03:01:33 +02:00
|
|
|
|
2014-10-27 03:28:46 -04:00
|
|
|
|
2014-10-31 18:12:55 -04:00
|
|
|
def do_tuning(args):
|
2014-10-31 18:56:33 -04:00
|
|
|
device = args.device
|
2015-01-21 20:08:52 -05:00
|
|
|
context = atd.context(device)
|
|
|
|
context.queues.append(atd.command_queue(context, device))
|
2014-10-31 18:56:33 -04:00
|
|
|
if os.path.isfile(args.json_file):
|
|
|
|
json_out = json.load(open(args.json_file, 'r'))
|
2014-10-29 23:38:22 -04:00
|
|
|
else:
|
|
|
|
json_out = {}
|
|
|
|
json_out["version"] = "1.0"
|
|
|
|
|
2014-10-03 09:29:45 +02:00
|
|
|
def map_to_list(T, x):
|
|
|
|
return list(map(T, x if isinstance(x, list) else [x]))
|
2014-10-27 03:28:46 -04:00
|
|
|
|
2014-10-31 18:56:33 -04:00
|
|
|
if(args.method=='simple'):
|
2015-01-12 13:20:53 -05:00
|
|
|
default_tuning_sizes = {'vaxpy': args.blas1_size, 'dot': args.blas1_size,
|
|
|
|
'maxpy' : args.blas2_size, 'gemv' : args.blas2_size,
|
|
|
|
'gemm': args.blas3_size}
|
2014-10-27 03:28:46 -04:00
|
|
|
|
2015-01-12 13:20:53 -05:00
|
|
|
for operation in ['vaxpy', 'dot', 'maxpy', 'gemv', 'gemm']:
|
2014-11-06 16:14:46 -05:00
|
|
|
|
2015-01-12 13:20:53 -05:00
|
|
|
for datatype in [atd.float32, atd.float64]:
|
|
|
|
|
|
|
|
dtypestr = datatype.__name__
|
|
|
|
|
|
|
|
if operation not in args.operations and operation + '-' + dtypestr not in args.operations:
|
2014-10-29 17:01:57 +01:00
|
|
|
continue
|
|
|
|
|
2014-10-27 22:22:07 -04:00
|
|
|
#Check data-type
|
2015-01-12 13:20:53 -05:00
|
|
|
if datatype is atd.float64 and not device.double_fp_config:
|
2014-10-27 22:22:07 -04:00
|
|
|
sys.stderr.write('Warning : The device ' + device.name + ' does not support double precision! Skipping ...')
|
|
|
|
continue
|
|
|
|
|
2015-01-12 13:20:53 -05:00
|
|
|
#~ #Helper for execution
|
|
|
|
def execute(symbolic, sizes, Template, parameters = None, fname = os.devnull):
|
|
|
|
if parameters is not None:
|
|
|
|
return misc_tools.benchmark(Template(*parameters), symbolic)
|
|
|
|
with open(fname, "w+") as archive:
|
|
|
|
return optimize.genetic(symbolic, Template, lambda t: TYPES[operation]['perf-index']([datatype(0).size, sizes, t]),
|
|
|
|
TYPES[operation]['perf-measure'], archive)
|
|
|
|
|
2014-10-29 23:38:22 -04:00
|
|
|
def log_uniform_sample(a,b):
|
|
|
|
return np.exp(np.random.uniform(low=np.log(a), high=np.log(b), size=1)).astype(int)
|
|
|
|
|
2014-11-06 16:14:46 -05:00
|
|
|
def space_gen_product(a,b,N,dim,method):
|
2014-10-29 23:38:22 -04:00
|
|
|
N = int(N**(1.0/dim))
|
2014-11-06 16:14:46 -05:00
|
|
|
def space_gen(a,b,method):
|
2014-10-29 23:38:22 -04:00
|
|
|
for i in range(N):
|
2014-11-06 16:14:46 -05:00
|
|
|
if method == 'linear':
|
|
|
|
v = int(a + (b-a)*i/N)
|
|
|
|
if method == 'log':
|
|
|
|
v = int(np.exp(np.log(a) + (np.log(b) - np.log(a))*i/N))
|
2014-10-29 23:38:22 -04:00
|
|
|
yield (v//64 + 1)*64
|
2014-11-06 16:14:46 -05:00
|
|
|
return tuple(itertools.product(*[space_gen(a,b,method) for i in range(dim)]))
|
2014-10-29 23:38:22 -04:00
|
|
|
|
|
|
|
|
2014-10-27 22:22:07 -04:00
|
|
|
#Helper for tuning
|
2014-11-06 16:14:46 -05:00
|
|
|
def tune(execution_handler, a, b, dimsample, layouts, sample_method_profiles, sample_method_dataset):
|
2014-10-29 17:01:57 +01:00
|
|
|
print('-----')
|
2015-01-12 13:20:53 -05:00
|
|
|
print(' '.join(map(str, ("Now tuning:", dtypestr, '-', operation, '-'.join(layouts), '[' + device.name, '(' + device.platform.name + ')]'))))
|
2014-10-27 22:22:07 -04:00
|
|
|
#Update JSON
|
2014-11-06 16:14:46 -05:00
|
|
|
full_operation = operation + ''.join(layouts)
|
2014-10-27 22:22:07 -04:00
|
|
|
if full_operation not in json_out:
|
|
|
|
json_out[full_operation] = {}
|
2015-01-12 13:20:53 -05:00
|
|
|
json_out[full_operation][dtypestr] = {}
|
|
|
|
D = json_out[full_operation][dtypestr]
|
2014-10-27 22:22:07 -04:00
|
|
|
|
2014-10-31 18:56:33 -04:00
|
|
|
if args.method == 'simple':
|
2015-01-21 20:08:52 -05:00
|
|
|
print 'Size : ', ','.join(map(str, default_tuning_sizes[operation]))
|
2014-10-27 22:22:07 -04:00
|
|
|
profiles = [execution_handler(map(int,default_tuning_sizes[operation]))]
|
|
|
|
else:
|
|
|
|
def compute_perf(x, t):
|
2015-01-12 13:20:53 -05:00
|
|
|
return TYPES[operation]['perf-index']([datatype(0).size, x, t])
|
2014-11-06 16:14:46 -05:00
|
|
|
profiles_generator = space_gen_product(a, b, args.sample_size, dimsample, sample_method_profiles)
|
|
|
|
profiles = dataset.sample_profiles(execution_handler, profiles_generator)
|
2014-10-31 18:56:33 -04:00
|
|
|
if args.build_model:
|
2014-11-06 16:14:46 -05:00
|
|
|
dataset_generator = space_gen_product(a, b, 1000, dimsample, sample_method_dataset)
|
2015-01-12 13:20:53 -05:00
|
|
|
X, Y, profiles = dataset.sample_dataset(os.path.join(full_operation,dtypestr), profiles, execution_handler, dataset_generator)
|
|
|
|
# profiles = np.loadtxt('data/'+full_operation+'/'+datatype+'/profiles.csv')
|
|
|
|
# X = np.loadtxt('data/'+full_operation+'/'+datatype+'/X.csv',ndmin=2)
|
|
|
|
# Y = np.loadtxt('data/'+full_operation+'/'+datatype+'/Y.csv',ndmin=2)
|
2014-10-28 01:10:14 -04:00
|
|
|
clf = train_model(X, Y, profiles, TYPES[operation]['perf-measure'])
|
|
|
|
D['predictor'] = [{'children_left': e.tree_.children_left.tolist(),
|
|
|
|
'children_right': e.tree_.children_right.tolist(),
|
2014-10-31 18:12:55 -04:00
|
|
|
'threshold': e.tree_.threshold.astype('float64').tolist(),
|
|
|
|
'feature': e.tree_.feature.astype('float64').tolist(),
|
|
|
|
'value': e.tree_.value[:,:,0].astype('float64').tolist()} for e in clf.estimators_]
|
2014-10-29 17:01:57 +01:00
|
|
|
D['profiles'] = [map(int, x) for x in profiles]
|
2014-10-27 22:22:07 -04:00
|
|
|
|
|
|
|
|
2015-01-12 13:20:53 -05:00
|
|
|
Template = TYPES[operation]['template']
|
2015-01-21 20:08:52 -05:00
|
|
|
|
2014-10-27 22:22:07 -04:00
|
|
|
#Vector AXPY
|
2015-01-12 13:20:53 -05:00
|
|
|
if operation=='vaxpy':
|
2014-10-27 22:22:07 -04:00
|
|
|
def execution_handler(sizes, fname=os.devnull, parameters=None):
|
2015-01-21 20:08:52 -05:00
|
|
|
x = atd.empty(sizes[0], datatype, context=context)
|
|
|
|
y = atd.empty(sizes[0], datatype, context=context)
|
2015-01-12 13:20:53 -05:00
|
|
|
return execute(x + y, sizes, Template, parameters, fname)
|
2014-11-06 16:14:46 -05:00
|
|
|
tune(execution_handler, 1e3, 2e7, 1, (),'log', 'log')
|
2015-01-12 13:20:53 -05:00
|
|
|
#dot
|
|
|
|
if operation=='dot':
|
2014-10-27 22:22:07 -04:00
|
|
|
def execution_handler(sizes, fname=os.devnull, parameters=None):
|
2015-01-21 20:08:52 -05:00
|
|
|
x = atd.empty(sizes[0], datatype, context=context)
|
|
|
|
y = atd.empty(sizes[0], datatype, context=context)
|
2015-01-12 13:20:53 -05:00
|
|
|
s = atd.scalar(datatype)
|
|
|
|
return execute(atd.dot(x, y), sizes, Template, parameters, fname)
|
2014-11-06 16:14:46 -05:00
|
|
|
tune(execution_handler, 1e3, 2e7, 1, (),'log', 'log')
|
2014-10-27 22:22:07 -04:00
|
|
|
#Matrix AXPY
|
2015-01-12 13:20:53 -05:00
|
|
|
if operation=='maxpy':
|
2014-10-27 22:22:07 -04:00
|
|
|
def execution_handler(sizes, fname=os.devnull, parameters=None):
|
2015-01-21 20:08:52 -05:00
|
|
|
A = atd.empty(sizes, datatype, context=context)
|
|
|
|
C = atd.empty(sizes, datatype, context=context)
|
2015-01-12 13:20:53 -05:00
|
|
|
return execute(A + C, sizes, Template, parameters, fname)
|
2014-11-06 16:14:46 -05:00
|
|
|
tune(execution_handler, 100, 5000, 2, (),'log', 'log')
|
2015-01-12 13:20:53 -05:00
|
|
|
#Row-wise dot
|
|
|
|
if operation=='gemv':
|
2014-10-31 18:56:33 -04:00
|
|
|
for A_trans in args.gemv_layouts:
|
2014-10-27 22:22:07 -04:00
|
|
|
def execution_handler(sizes, fname=os.devnull, parameters=None):
|
2015-01-21 20:08:52 -05:00
|
|
|
A = atd.empty(sizes if A_trans=='N' else sizes[::-1], datatype, context=context)
|
|
|
|
x = atd.empty(sizes[1], datatype, context=context)
|
2014-10-27 22:22:07 -04:00
|
|
|
LHS = A if A_trans=='N' else A.T
|
2015-01-24 14:51:48 -05:00
|
|
|
return execute(atd.dot(LHS, x), sizes, Template[A_trans], parameters, fname)
|
2014-11-06 16:14:46 -05:00
|
|
|
tune(execution_handler, 100, 5000, 2, (A_trans,),'log', 'log')
|
2014-10-27 22:22:07 -04:00
|
|
|
#Matrix Product
|
2015-01-12 13:20:53 -05:00
|
|
|
if operation=='gemm':
|
2014-10-31 18:56:33 -04:00
|
|
|
for L in args.gemm_layouts:
|
2014-10-31 18:12:55 -04:00
|
|
|
A_trans = L[0]
|
|
|
|
B_trans = L[1]
|
2014-10-27 22:22:07 -04:00
|
|
|
def execution_handler(sizes, fname=os.devnull, parameters=None):
|
2015-01-21 20:08:52 -05:00
|
|
|
A = atd.empty((sizes[0], sizes[2]) if A_trans=='N' else (sizes[2], sizes[0]), datatype, context=context)
|
|
|
|
B = atd.empty((sizes[2], sizes[1]) if B_trans=='N' else (sizes[1], sizes[2]), datatype, context=context)
|
2014-10-27 22:22:07 -04:00
|
|
|
LHS = A if A_trans=='N' else A.T
|
|
|
|
RHS = B if B_trans=='N' else B.T
|
2015-01-24 14:51:48 -05:00
|
|
|
return execute(atd.dot(LHS, RHS), sizes, Template[(A_trans, B_trans)], parameters, fname)
|
2015-01-21 20:08:52 -05:00
|
|
|
tune(execution_handler, 100, 2000, 3,(A_trans,B_trans), 'linear', 'linear')
|
2014-10-29 17:01:57 +01:00
|
|
|
|
2014-10-31 18:56:33 -04:00
|
|
|
json.dump(json_out, open(args.json_file,'w'))
|
2014-10-29 17:01:57 +01:00
|
|
|
|
|
|
|
|
2014-09-29 03:01:33 +02:00
|
|
|
|
2014-10-27 03:28:46 -04:00
|
|
|
|
2014-10-31 18:56:33 -04:00
|
|
|
class ArgumentsHandler:
|
2015-01-26 00:56:02 -05:00
|
|
|
def __init__(self, devices):
|
2015-01-21 20:08:52 -05:00
|
|
|
#Command line arguments
|
|
|
|
parser = argparse.ArgumentParser()
|
|
|
|
subparsers = parser.add_subparsers(dest='action')
|
|
|
|
print_devices_parser = subparsers.add_parser('list-devices', help='List the devices available')
|
|
|
|
tune_parser = subparsers.add_parser('tune', help='Auto-tuning')
|
|
|
|
tune_parser.add_argument("--device", default=0, type=int)
|
|
|
|
tune_parser.add_argument("--operations", default = 'vaxpy,maxpy,dot,gemv,gemm-float32', type=str)
|
|
|
|
tune_parser.add_argument("--gemm-layouts", default='NN,NT,TN,TT', type=str)
|
|
|
|
tune_parser.add_argument("--gemv-layouts", default='N,T', type=str)
|
|
|
|
tune_parser.add_argument("--json-file", default='', type=str)
|
|
|
|
tune_parser.add_argument("--viennacl-src-path", default='', type=str)
|
|
|
|
|
|
|
|
tune_subparsers = tune_parser.add_subparsers(dest='method')
|
|
|
|
simple_parser = tune_subparsers.add_parser('simple', help = 'Tune each operation for unique sizes')
|
|
|
|
|
|
|
|
simple_parser.add_argument("--blas1-size", default = 10e6, type=int)
|
|
|
|
simple_parser.add_argument("--blas2-size", nargs=2, default=[2560,2560], type=int)
|
|
|
|
simple_parser.add_argument("--blas3-size", nargs=3, default=[1536,1536,1536],type=int)
|
|
|
|
|
|
|
|
full_parser = tune_subparsers.add_parser('full', help = 'Tune each operation for randomly chosen sizes')
|
|
|
|
full_parser.add_argument("--build-model", default=True, type=bool)
|
|
|
|
full_parser.add_argument("--sample-size", default=30, type=int)
|
|
|
|
|
|
|
|
args = parser.parse_args()
|
|
|
|
self.__dict__ = args.__dict__.copy()
|
2015-01-26 00:56:02 -05:00
|
|
|
|
|
|
|
if self.action == 'tune':
|
|
|
|
#Retypes
|
|
|
|
self.device = devices[int(self.device)]
|
|
|
|
if not self.json_file:
|
|
|
|
self.json_file = misc_tools.sanitize_string(self.device.name) + '.json'
|
|
|
|
self.operations = self.operations.split(',')
|
|
|
|
self.gemm_layouts = self.gemm_layouts.split(',')
|
|
|
|
self.gemv_layouts = self.gemv_layouts.split(',')
|
|
|
|
if self.method == 'simple':
|
|
|
|
self.blas1_size = [int(float(self.blas1_size))]
|
|
|
|
self.blas2_size = map(int, self.blas2_size)
|
|
|
|
self.blas3_size = map(int, self.blas3_size)
|
2014-09-02 22:03:20 -04:00
|
|
|
|
|
|
|
if __name__ == "__main__":
|
2014-10-31 18:56:33 -04:00
|
|
|
|
2015-01-21 20:08:52 -05:00
|
|
|
platforms = atd.get_platforms()
|
|
|
|
devices = [d for platform in platforms for d in platform.get_devices()]
|
2015-01-26 00:56:02 -05:00
|
|
|
|
|
|
|
args = ArgumentsHandler(devices)
|
|
|
|
|
2014-10-31 18:12:55 -04:00
|
|
|
print("----------------")
|
|
|
|
print("Devices available:")
|
|
|
|
print("----------------")
|
|
|
|
for (i, d) in enumerate(devices):
|
2015-01-21 20:08:52 -05:00
|
|
|
print 'Device', i, '|', atd.device_type_to_string(d.type), '|', d.name, 'on', d.platform.name
|
2014-10-31 18:12:55 -04:00
|
|
|
print("----------------")
|
2015-01-26 00:56:02 -05:00
|
|
|
|
|
|
|
if args.action=='tune':
|
|
|
|
print("------")
|
|
|
|
print("Auto-tuning")
|
|
|
|
print("------")
|
|
|
|
do_tuning(args)
|