diff --git a/llvm/projects/soc_simulator/src/driver_new_config.py b/llvm/projects/soc_simulator/src/driver_new_config.py new file mode 100644 index 0000000000000000000000000000000000000000..115237dac51c96b47d02c84a603d98bdcf0b84a4 --- /dev/null +++ b/llvm/projects/soc_simulator/src/driver_new_config.py @@ -0,0 +1,328 @@ +from collections import defaultdict +import os +import subprocess +import sys + +class Driver: + fp16_swing = 8 + + class ApproxTypes: + FP16 = 0 + FP32 = 1 + PROMISE = 2 + PERF = 3 + + results_time_key = "Time" + results_energy_key = "Energy" + + + def driver(self): + self.__parse_tensor_layer_file() + self.__parse_tensor_table() + self.__run_simulations() + self.__display_results() + + + def __init__(self, layer_filename, table_filename, config_filename, results_filename): + self.__layer_filename = layer_filename + self.__table_filename = table_filename + self.__config_filename = config_filename + self.__results_filename = results_filename + + # NOTE: Use an OrderedDict if we want to search by operation name + # Using a list bc we care about the order the data is read in + # since it corresponds to the data in the configuration file + self.__tensor_layers = [] + + # [layer_name][operation_name][cols] + # Operation names need to be stored in order of insertion + self.__tensor_table = defaultdict(lambda: list(defaultdict(str))) + + # [Time/Energy][number corresponding to order the layer config was read in] = time/energy + self.__aggregate_results = defaultdict(lambda: defaultdict(float)) + self.__config_count = 0 + + + @staticmethod + def is_conv(operation_name): + return operation_name.startswith("Conv") + + + @staticmethod + def is_nml(operation_name): + return operation_name.startswith("NML") + + + @staticmethod + def is_fc(operation_name): + return operation_name.startswith("FC") + + + def __parse_tensor_layer_file(self): + if not os.path.isfile(self.__layer_filename): + print("ERROR: %s was not found." % self.__layer_filename) + exit(1) + + layer_file = open(self.__layer_filename, "r") + for line in layer_file: + layer_data = line.strip().split(',') + layer_name = layer_data[0] + + tensor_layer = defaultdict(str) + tensor_layer["Name"] = layer_name + + if Driver.is_conv(layer_name): + tensor_layer["N"] = float(layer_data[1]) + tensor_layer["Cin"] = float(layer_data[2]) + tensor_layer["H"] = float(layer_data[3]) + tensor_layer["W"] = float(layer_data[4]) + tensor_layer["Cout"] = float(layer_data[5]) + tensor_layer["Kh"] = float(layer_data[7]) + tensor_layer["Kw"] = float(layer_data[8]) + tensor_layer["Sh"] = float(layer_data[9]) + tensor_layer["Sw"] = float(layer_data[10]) + + elif Driver.is_fc(layer_name): + tensor_layer["RA"] = float(layer_data[1]) + tensor_layer["CA"] = float(layer_data[2]) + tensor_layer["RB"] = float(layer_data[3]) + tensor_layer["CB"] = float(layer_data[4]) + + elif not Driver.is_nml(layer_name): # TODO should we store data for NMLs? + print("ERROR: Invalid layer name %s" % layer_name) + exit(1) + + self.__tensor_layers.append(tensor_layer) + layer_file.close() + + + def __parse_tensor_table(self): + if not os.path.isfile(self.__table_filename): + print("ERROR: %s was not found." % self.__table_filename) + exit(1) + table_file = open(self.__table_filename, "r") + line = table_file.readline().strip() + + while line: + # Line here MUST be a header or there's a bug + # Get the description of the layer + assert(line.startswith("**")) + + header_contents = line.split(' ')[1:] + layer_name = header_contents[0] + num_ops = int(header_contents[1]) + col_names = header_contents[2:] + + layer_operations = [] + + # Go through all operations in the layer + for op_count in range(num_ops): + operation_data = defaultdict(str) + + line = table_file.readline().strip() + op_data = line.split(' ') + op_name = op_data[0] + operation_data["Name"] = op_name + + # Number of data items (#s) needs to match up with the # of cols + assert(len(op_data) - 1 == len(col_names)) + + # Go through all data items (each col element) per operation + for i in range(len(col_names)): + operation_data[col_names[i]] = float(op_data[i + 1]) + + layer_operations.append(operation_data) + + self.__tensor_table[layer_name] = layer_operations + line = table_file.readline().strip() + table_file.close() + + + @staticmethod + def is_promise(config_layer): + return float(config_layer.split(' ')[0]) < Driver.fp16_swing + + + def __quantize(self, curr_layer, prev_layer, h2f_f2h_operation_ind, layer_data): + if curr_layer == prev_layer or curr_layer == Driver.ApproxTypes.PROMISE \ + or prev_layer == Driver.ApproxTypes.PROMISE: # No quantization needed + return 0.0, 0.0 + + layer_name = layer_data["Name"] + + # NOTE: Ignoring logic where curr == promise or prev == promise bc + # smartDMA is always true so we'd return near the beginning of the method + + # Get h2f/f2h data using the first tensor operation in the layer + # (which is why order matters in the tensor table) + print(layer_name, self.__tensor_table[layer_name]) + tensor_op_row = self.__tensor_table[layer_name][h2f_f2h_operation_ind] + if curr_layer == Driver.ApproxTypes.FP32: + time = tensor_op_row["h2f_time"] + energy = tensor_op_row["h2f_energy"] + elif curr_layer == Driver.ApproxTypes.FP16: + time = tensor_op_row["f2h_time"] + energy = tensor_op_row["f2h_energy"] + + print("Quantization: (%f, %f)" % (time, energy)) + return (time, energy) + + + def __run_promise_simulation(self, swing, layer_data): + layer_name = layer_data["Name"] + patch_factor = 1 + + if Driver.is_conv(layer_name): + rows_a = layer_data["N"] * layer_data["H"] * layer_data["W"] \ + / (layer_data["Sh"] * layer_data["Sw"]) + cols_a = layer_data["Cin"] * layer_data["Kh"] * layer_data["Kw"] + rows_b = cols_a + cols_b = layer_data["Cout"] + patch_factor = layer_data["Kh"] * layer_data["Kw"] + elif Driver.is_fc(layer_name): + rows_a = layer_data["RA"] + cols_a = layer_data["CA"] + rows_b = cols_a + cols_b = layer_data["CB"] + else: + print("PROMISE can't run whatever this layer is.") + exit(1) + # Run promise simulator + # TODO need to print time and energy in the ptm runner so we can pipe it + output = subprocess.Popen(["./ptm", str(rows_a), str(cols_a), str(rows_b), \ + str(cols_b), str(patch_factor), str(swing)], \ + stdout = subprocess.PIPE, stderr = subprocess.PIPE).communicate()[0] + total_time_energy = output.strip().split(',') + + assert(len(total_time_energy) == 2) + print("PROMISE: (%s, %s)" % (total_time_energy[0], total_time_energy[1])) + return float(total_time_energy[0]), float(total_time_energy[1]) + + + def __run_simulations(self): + if not os.path.isfile(self.__config_filename): + print("ERROR: %s was not found" % self.__config_filename) + exit(1) + + config_file = open(self.__config_filename, "r") + + line = config_file.readline().strip() + + while line: + assert(line.startswith("+++++")) + config_name = config_file.readline().strip().split(' ')[0] # Next line = configuration name + print("CONFIGURATION") + + line = config_file.readline().strip() + layer_ind = 0 # NOTE can also use the leftmost number in the currl ine + + prev_layer = Driver.ApproxTypes.FP32 + curr_layer = None + + while not line.startswith("-----"): + layer_info = line.split(' ') + layer_data = self.__tensor_layers[layer_ind] + layer_name = layer_data["Name"] + + if layer_info[1] == "promise": + print("Running layer %s on PROMISE" % layer_name) + curr_layer = Driver.ApproxTypes.PROMISE + + swing = int(layer_info[3]) + time, energy = self.__run_promise_simulation(swing, layer_data) + print(time, energy) + self.__aggregate_results[Driver.results_time_key][self.__config_count] += time + self.__aggregate_results[Driver.results_energy_key][self.__config_count] += energy + + elif layer_info[1] == "gpu": + # Parse each individual tensor operation + # TODO not portable bc there can be multiple numbers after each approx later on + total_time = 0 + total_energy = 0 + + tensor_ind = 0 + for i in range(2, len(layer_info), 3): + tensor_op = layer_info[i] + approx_type = layer_info[i + 1] + approx_num = layer_info[i + 2] # only matters if perf + + if approx_type == "fp16": + curr_layer = Driver.ApproxTypes.FP16 + elif approx_type == "fp32": + curr_layer = Driver.ApproxTypes.FP32 + elif approx_type == "perf": + curr_layer = DriverApproxTypes.PERF + else: + assert(False) + + quant_time, quant_energy = self.__quantize(curr_layer, prev_layer, tensor_ind, layer_data) + time, energy = self.__run_gpu_simulation(curr_layer, layer_name, tensor_ind, approx_num) + total_time += time + total_energy += energy + + tensor_ind += 1 + + self.__aggregate_results[Driver.results_time_key][self.__config_count] += total_time + self.__aggregate_results[Driver.results_energy_key][self.__config_count] += total_energy + + layer_ind += 1 + line = config_file.readline().strip() + + self.__config_count += 1 + line = config_file.readline().strip() + + config_file.close() + + + def __run_gpu_simulation(self, curr_layer, layer_name, tensor_ind, approx_num): + tensor_info = self.__tensor_table[layer_name][tensor_ind] + + if curr_layer == Driver.ApproxTypes.FP32: + time = tensor_info["fp32_time"] + energy = tensor_info["fp32_energy"] + + elif curr_layer == Driver.ApproxTypes.FP16: + time = tensor_info["fp16_time"] + energy = tensor_info["fp16_energy"] + + elif curr_layer == Driver.ApproxTypes.PERF: + time = tensor_info["perf%s_energy" % approx_num] + energy = tensor_info["perf%s_energy" % approx_num] + + print("GPU: (%f, %f)" % (time, energy)) + return time, energy + + + def __display_results(self): + results_file = open(self.__results_filename, "w") + attributes_to_print = [Driver.results_time_key, Driver.results_energy_key] + + for attribute in attributes_to_print: + results_file.write("%s\n" % attribute) + results_file.write("Configuration,Total,Improvement\n") + + baseline_val = self.__aggregate_results[attribute][0] + print(baseline_val) + best_config = None + best_result = None + + for config_ind in range(self.__config_count): + results_file.write("c%d" % config_ind) + time_or_energy_val = self.__aggregate_results[attribute][config_ind] + + # Using repr to keep all decimal digits when writing to file + results_file.write(",%s" % repr(time_or_energy_val)) + results_file.write(",%s\n" % repr(baseline_val / (time_or_energy_val + 0.0001))) + + if not best_result or time_or_energy_val < best_result: + best_result = time_or_energy_val + best_config = config_ind + results_file.write("\nc%d,%s\n\n" % (best_config, repr(self.__aggregate_results[attribute][best_config]))) + results_file.close() + + +if __name__ == "__main__": + if len(sys.argv) != 5: + print("Usage: python driver.py <layer info> <tensor info> <configurations> <results file>") + exit(1) + Driver(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4]).driver()