diff --git a/scripts/database/database.py b/scripts/database/database.py index e115d68c..0ce89d03 100755 --- a/scripts/database/database.py +++ b/scripts/database/database.py @@ -98,7 +98,8 @@ def main(argv): database_best_results = bests.get_best_results(database) # Determines the defaults for other vendors and per vendor - database_defaults = defaults.calculate_defaults(database_best_results) + print("[database] Calculating the default values...") + database_defaults = defaults.calculate_defaults(database) database_best_results = db.concatenate_database(database_best_results, database_defaults) # Outputs the database as a C++ database diff --git a/scripts/database/database/defaults.py b/scripts/database/database/defaults.py index 357c3a3a..fca793ea 100644 --- a/scripts/database/database/defaults.py +++ b/scripts/database/database/defaults.py @@ -6,7 +6,9 @@ # Cedric Nugteren import pandas as pd + import clblast +import bests def set_default_device(database_entry): @@ -23,16 +25,18 @@ def set_default_time(database_entry): return database_entry -def calculate_defaults(df): - """# Sets defaults for devices of the same type/vendor based on the smallest values of all known entries. The average - might be better for performance but some parameters might not be supported on other devices.""" +def calculate_defaults(database, calculate_common_best=True): + """Sets defaults for devices of the same type/vendor. An option determines how to compute the defaults.""" database_defaults = pd.DataFrame() # Defaults per combination of device vendors and device types (e.g. AMD GPU) - database_type_vendor = df.groupby(clblast.DEVICE_TYPE_ATTRIBUTES + clblast.KERNEL_ATTRIBUTES + ["kernel"] + - clblast.ARGUMENT_ATTRIBUTES) + database_type_vendor = database.groupby(clblast.DEVICE_TYPE_ATTRIBUTES + clblast.KERNEL_ATTRIBUTES + ["kernel"] + + clblast.ARGUMENT_ATTRIBUTES) for group_name, database_group in database_type_vendor: - default_values = database_group.min(axis=0) + if calculate_common_best: + default_values = get_common_best(database_group, group_name) + else: + default_values = get_smallest_best(database_group) default_values = set_default_device(default_values) default_values = set_default_time(default_values) database_defaults = database_defaults.append(default_values, ignore_index=True) @@ -45,9 +49,9 @@ def calculate_defaults(df): print("[WARNING] Entries for a single kernel with multiple argument values: " + description) # Defaults over all device types and vendors - groups = df.groupby(clblast.KERNEL_ATTRIBUTES + ["kernel"] + clblast.ARGUMENT_ATTRIBUTES) + groups = database.groupby(clblast.KERNEL_ATTRIBUTES + ["kernel"] + clblast.ARGUMENT_ATTRIBUTES) for group_name, database_group in groups: - default_values = database_group.min(axis=0) + default_values = get_smallest_best(database_group) default_values["device_vendor"] = clblast.VENDOR_DEFAULT default_values["device_type"] = clblast.DEVICE_TYPE_DEFAULT default_values = set_default_device(default_values) @@ -56,3 +60,53 @@ def calculate_defaults(df): # Database with both types of defaults only return database_defaults + + +def get_smallest_best(database): + """Sets defaults based on the smallest values of all known entries. The average might be better for performance but + some parameters might not be supported on other devices.""" + database_best_results = bests.get_best_results(database) + return database_best_results.min(axis=0) + + +def get_common_best(database, group_name): + """Sets defaults based on the best values of entries supported by all devices. This might cause a problem in case + not every device was tuned with the same parameters.""" + # TODO: Quite a bit slower than the above `get_smallest_best` method + + # Counts the number of devices in this group + num_devices = len(database.groupby(clblast.DEVICE_ATTRIBUTES)) + + # Removes columns without any values + database = database.dropna(axis=1, how='all') + + # Retrieves the parameter names for this kernel + all_column_names = list(database.columns.values) + parameter_column_names = [c for c in all_column_names if "parameters." in c] + + # Removes entries which are not available for all devices + database_common = pd.DataFrame() + database_by_parameters = database.groupby(parameter_column_names) + for parameter_values, database_parameters in database_by_parameters: + num_entries = database_parameters.shape[0] + if num_entries == num_devices: + database_common = database_common.append(database_parameters) + + # Fall back to another method in case there are no shared entries at all across devices + if database_common.shape[0] == 0: + # print("Skipping: " + str(group_name) + " with devices: " + str(num_devices) + " " + str(database.shape[0])) + return get_smallest_best(database) + + # Computes the sum of the execution times over the different devices + database_common['time'] = database_common.groupby(parameter_column_names)['time'].transform(sum) + + # Retrieves the entries with the best execution time + best_time = database_common["time"].min() + database_bests = database_common[database_common["time"] == best_time] + + # Retrieves one example only (the parameters are the same anyway) + database_bests = database_bests.drop_duplicates(["time"]) + # print(str(group_name) + " with num devices: " + str(num_devices) + " " + str(database_bests.shape)) + assert database_bests.shape[0] == 1 + + return database_bests