From 9164a8c1c2aab6f3de377a9598826bf7a1a880d0 Mon Sep 17 00:00:00 2001 From: victoryang00 Date: Thu, 29 Feb 2024 21:16:44 -0800 Subject: [PATCH] performance mac result --- artifact/bench_comparison_gapbs_mac.py | 323 +++++++++++++++ artifact/bench_comparison_mac.py | 380 ++++++++++++++++++ artifact/bench_policy_mac.py | 299 ++++++++++++++ .../result/performance_comparison_mac.pdf | Bin 0 -> 16957 bytes .../result/performance_singlethread_mac.pdf | Bin 0 -> 19284 bytes 5 files changed, 1002 insertions(+) create mode 100644 artifact/bench_comparison_gapbs_mac.py create mode 100644 artifact/bench_comparison_mac.py create mode 100644 artifact/bench_policy_mac.py create mode 100644 artifact/result/performance_comparison_mac.pdf create mode 100644 artifact/result/performance_singlethread_mac.pdf diff --git a/artifact/bench_comparison_gapbs_mac.py b/artifact/bench_comparison_gapbs_mac.py new file mode 100644 index 0000000..6215d54 --- /dev/null +++ b/artifact/bench_comparison_gapbs_mac.py @@ -0,0 +1,323 @@ +import csv +import common_util +from multiprocessing import Pool +from matplotlib import pyplot as plt +import numpy as np +from collections import defaultdict + +cmd = [ + "bc", + "bfs", + "cc", + "cc_sv", + "pr", + "pr_spmv", + "sssp", + "tc", +] +folder = [ + "gapbs", + "gapbs", + "gapbs", + "gapbs", + "gapbs", + "gapbs", + "gapbs", + "gapbs", + +] +arg = [ + ["-g20", "-vn300"], + ["-g20", "-vn300"], + ["-g20", "-vn300"], + ["-g20", "-vn300"], + ["-g20", "-vn300"], + ["-g20", "-vn300"], + ["-g20", "-vn300"], + ["-g20", "-n1"], + +] +envs = [ + "OMP_NUM_THREADS=4", + "OMP_NUM_THREADS=4", + "OMP_NUM_THREADS=4", + "OMP_NUM_THREADS=4", + "OMP_NUM_THREADS=4", + "OMP_NUM_THREADS=4", + "OMP_NUM_THREADS=4", + "OMP_NUM_THREADS=4", +] +pool = Pool(processes=20) + +def run_mvvm(): + results = [] + results1 = [] + for _ in range(common_util.trial): + for i in range(len(cmd)): + aot = cmd[i] + ".aot" + results1.append(pool.apply_async(common_util.run, (aot, arg[i], envs[i]))) + # print the results + results1 = [x.get() for x in results1] + for exec, output in results1: + lines = output.split("\n") + for line in lines: + if line.__contains__("Execution time:"): + exec_time = line.split(" ")[-2] + print(exec, exec_time) + results.append((exec, exec_time)) # discover 4 aot_variant + return results + + +def run_qemu_x86_64(): + results = [] + results1 = [] + for _ in range(common_util.trial): + for i in range(len(cmd)): + aot = cmd[i] + results1.append( + pool.apply_async( + common_util.run_qemu_x86_64, + (aot, folder[i], arg[i], envs[i]), + ) + ) + # print the results + results1 = [x.get() for x in results1] + for exec, output in results1: + print(exec, output) + lines = output.split("\n") + for line in lines: + if line.__contains__("elapsed"): + try: + minutes, seconds = line.split()[2].replace("elapsed", "").split(":") + seconds, milliseconds = seconds.split(".") + + # Convert each part to seconds (note that milliseconds are converted and added as a fraction of a second) + total_seconds = ( + int(minutes) * 60 + int(seconds) + int(milliseconds) / 1000 + ) + + print(total_seconds) + exec_time = total_seconds + except: + try: + from datetime import datetime + + time_object = datetime.strptime(line.split()[2].replace("elapsed", ""), "%H:%M:%S").time() + print(time_object) + except: + exec_time = float(line.split()[0].replace("user", "")) + results.append((exec, exec_time)) + return results + + +def run_qemu_aarch64(): + results = [] + results1 = [] + for _ in range(common_util.trial): + for i in range(len(cmd)): + aot = cmd[i] + results1.append( + pool.apply_async( + common_util.run_qemu_aarch64, + (aot, folder[i], arg[i], envs[i]), + ) + ) + results1 = [x.get() for x in results1] + for exec, output in results1: + print(exec, output) + lines = output.split("\n") + for line in lines: + if line.__contains__("elapsed"): + try: + minutes, seconds = line.split()[2].replace("elapsed", "").split(":") + seconds, milliseconds = seconds.split(".") + + # Convert each part to seconds (note that milliseconds are converted and added as a fraction of a second) + total_seconds = ( + int(minutes) * 60 + int(seconds) + int(milliseconds) / 1000 + ) + + print(total_seconds) + exec_time = total_seconds + except: + try: + from datetime import datetime + + time_object = datetime.strptime(line.split()[2].replace("elapsed", ""), "%H:%M:%S").time() + print(time_object) + except: + exec_time = float(line.split()[0].replace("user", "")) + results.append((exec, exec_time)) + return results + + +def run_native(): + results = [] + results1 = [] + for _ in range(common_util.trial): + for i in range(len(cmd)): + aot = cmd[i] + results1.append( + pool.apply_async( + common_util.run_native, + (aot, folder[i], arg[i], envs[i]), + ) + ) + results1 = [x.get() for x in results1] + for exec, output in results1: + print(exec, output) + lines = output.split("\n") + for line in lines: + if line.__contains__("elapsed"): + try: + minutes, seconds = line.split()[2].replace("elapsed", "").split(":") + seconds, milliseconds = seconds.split(".") + + # Convert each part to seconds (note that milliseconds are converted and added as a fraction of a second) + total_seconds = ( + int(minutes) * 60 + int(seconds) + int(milliseconds) / 1000 + ) + + print(total_seconds) + exec_time = total_seconds + except: + try: + from datetime import datetime + + time_object = datetime.strptime(line.split()[2].replace("elapsed", ""), "%H:%M:%S").time() + print(time_object) + except: + exec_time = float(line.split()[0].replace("user", "")) + results.append((exec, exec_time)) + return results + + +def write_to_csv(filename): + # 'data' is a list of tuples, e.g., [(checkpoint_result_0, checkpoint_result_1, restore_result_2), ...] + + with open(filename, "a+", newline="") as csvfile: + writer = csv.writer(csvfile) + # Optionally write headers + writer.writerow( + ["name", "mvvm", "qemu_x86_64", "qemu_aach64", "native"] + ) + + # Write the data + for idx, row in enumerate(mvvm_results): + writer.writerow( + [ + row[0], + row[1], + qemu_x86_64_results[idx][1], + qemu_aarch64_results[idx][1], + native_results[idx][1], + ] + ) +def read_from_csv(filename): + with open(filename, "r") as csvfile: + reader = csv.reader(csvfile) + next(reader) + results = [] + for row in reader: + results.append((row[0], float(row[1]), float(row[2]), float(row[3]), float(row[4]))) + return results + +def plot(results): + font = {'size': 18} + + plt.rc('font', **font) + workloads = defaultdict(list) + for workload, mvvm_values, qemu_x86_64_values,qemu_aarch64_values,native_values in results: + workloads[ + workload.replace("OMP_NUM_THREADS=", "") + .replace("-g20", "") + .replace("-n300", "") + .replace(" -f ", "") + .replace("-vn300", "") + .replace("maze-6404.txt", "") + .replace("stories110M.bin", "") + .replace("-z tokenizer.bin -t 0.0", "") + .strip() + ].append(( mvvm_values, qemu_x86_64_values,qemu_aarch64_values,native_values)) + + statistics = {} + for workload, times in workloads.items(): + mvvm_values, qemu_x86_64_values,qemu_aarch64_values,native_values= zip(*times) + statistics[workload] = { + "mvvm_median": np.median(mvvm_values), + "qemu_x86_64_median" :np.median(qemu_x86_64_values), + "qemu_aarch64_median" :np.median(qemu_aarch64_values), + "native_median" :np.median(native_values), + "mvvm_std": np.std(mvvm_values), + "qemu_x86_64_std" :np.std(qemu_x86_64_values), + "qemu_aarch64_std" :np.std(qemu_aarch64_values), + "native_std" :np.std(native_values), + } + + fig, ax = plt.subplots(figsize=(20, 10)) + index = np.arange(len(statistics)) + bar_width = 0.7/5 + + for i, (workload, stats) in enumerate(statistics.items()): + ax.bar( + index[i], + stats["mvvm_median"], + bar_width, + yerr=stats["mvvm_std"], + capsize=5, + color="red", + label="mvvm" if i == 0 else "", + ) + ax.bar( + index[i]+ bar_width *1, + stats["qemu_x86_64_median"], + bar_width, + yerr=stats["qemu_x86_64_std"], + capsize=5, + color="brown", + label="qemu_x86_64" if i == 0 else "", + ) + ax.bar( + index[i]+ bar_width*2, + stats["qemu_aarch64_median"], + bar_width, + yerr=stats["qemu_aarch64_std"], + capsize=5, + color="purple", + label="qemu_aarch64" if i == 0 else "", + ) + ax.bar( + index[i]+ bar_width*3, + stats["native_median"], + bar_width, + yerr=stats["native_std"], + capsize=5, + color="cyan", + label="native" if i == 0 else "", + ) + # ax.set_xlabel(workload) + ticklabel = (x for x in list(statistics.keys())) + print(statistics.keys()) + ax.set_xticks(index) + + ax.set_xticklabels(ticklabel,fontsize =10) + ax.set_ylabel("Execution time (s)") + ax.legend() + + # add text at upper left + ax.legend(loc="upper right") + + plt.savefig("performance_comparison_gapbs.pdf") + + +if __name__ == "__main__": + mvvm_results = run_mvvm() + native_results = run_native() + qemu_x86_64_results = run_qemu_x86_64() + # # print the results + qemu_aarch64_results = run_qemu_aarch64() + + write_to_csv("comparison_gapbs.csv") + + results = read_from_csv("comparison_gapbs.csv") + plot(results) diff --git a/artifact/bench_comparison_mac.py b/artifact/bench_comparison_mac.py new file mode 100644 index 0000000..9d7b007 --- /dev/null +++ b/artifact/bench_comparison_mac.py @@ -0,0 +1,380 @@ +import csv +import common_util +from multiprocessing import Pool +from matplotlib import pyplot as plt +import numpy as np +from collections import defaultdict + +cmd = [ + "linpack", + "llama", + "rgbd_tum", + "tc", + "bt", + "cg", + "ft", + "lu", + "mg", + "sp", + "redis", + "hdastar", +] +folder = [ + "linpack", + "llama", + "ORB_SLAM2" + "nas", + "nas", + "nas", + "nas", + "nas", + "nas", + "redis", + "hdastar", +] +arg = [ + [], + ["stories110M.bin", "-z", "tokenizer.bin", "-t", "0.0"], + ["./ORBvoc.txt,", "./TUM3.yaml", "./", "./associations/fr1_xyz.txt"], + [], + [], + [], + [], + [], + [], + [], + ["maze-6404.txt", "8"], +] +envs = [ + "a=b", + "OMP_NUM_THREADS=1", + "a=b", + "OMP_NUM_THREADS=1", + "OMP_NUM_THREADS=1", + "OMP_NUM_THREADS=1", + "OMP_NUM_THREADS=1", + "OMP_NUM_THREADS=1", + "OMP_NUM_THREADS=1", + "a=b", + "a=b", +] + +pool = Pool(processes=10) + + +def run_mvvm(): + results = [] + results1 = [] + for _ in range(common_util.trial): + for i in range(len(cmd)): + aot = cmd[i] + ".aot" + results1.append(pool.apply_async(common_util.run, (aot, arg[i], envs[i]))) + # print the results + results1 = [x.get() for x in results1] + for exec, output in results1: + lines = output.split("\n") + for line in lines: + if line.__contains__("Execution time:"): + exec_time = line.split()[-2] + print(exec, exec_time) + results.append((exec, exec_time)) # discover 4 aot_variant + return results + + +def run_hcontainer(): + results = [] + results1 = [] + for _ in range(common_util.trial): + for i in range(len(cmd)): + aot = cmd[i] + results1.append( + pool.apply_async( + common_util.run_hcontainer, + (aot, folder[i], arg[i], envs[i]), + ) + ) + # print the results + results1 = [x.get() for x in results1] + for exec, output in results1: + print(exec, output) + lines = output.split("\n") + for line in lines: + if line.__contains__("elapsed"): + try: + minutes, seconds = line.split()[2].replace("elapsed", "").split(":") + seconds, milliseconds = seconds.split(".") + + # Convert each part to seconds (note that milliseconds are converted and added as a fraction of a second) + total_seconds = ( + int(minutes) * 60 + int(seconds) + int(milliseconds) / 1000 + ) + + print(total_seconds) + exec_time = total_seconds + except: + if line.__contains__("user"): + exec_time = float(line.split()[0].replace("user", "")) + results.append((exec, exec_time)) + + return results + + +def run_qemu_x86_64(): + results = [] + results1 = [] + for _ in range(common_util.trial): + for i in range(len(cmd)): + aot = cmd[i] + results1.append( + pool.apply_async( + common_util.run_qemu_x86_64, + (aot, folder[i], arg[i], envs[i]), + ) + ) + # print the results + results1 = [x.get() for x in results1] + for exec, output in results1: + print(exec, output) + lines = output.split("\n") + for line in lines: + if line.__contains__("elapsed"): + try: + minutes, seconds = line.split()[2].replace("elapsed", "").split(":") + seconds, milliseconds = seconds.split(".") + + # Convert each part to seconds (note that milliseconds are converted and added as a fraction of a second) + total_seconds = ( + int(minutes) * 60 + int(seconds) + int(milliseconds) / 1000 + ) + + print(total_seconds) + exec_time = total_seconds + except: + try: + from datetime import datetime + + time_object = datetime.strptime(line.split()[2].replace("elapsed", ""), "%H:%M:%S").time() + print(time_object) + except: + exec_time = float(line.split()[0].replace("user", "")) + results.append((exec, exec_time)) + + return results + + +def run_qemu_aarch64(): + results = [] + results1 = [] + for _ in range(common_util.trial): + for i in range(len(cmd)): + aot = cmd[i] + results1.append( + pool.apply_async( + common_util.run_qemu_aarch64, + (aot, folder[i], arg[i], envs[i]), + ) + ) + results1 = [x.get() for x in results1] + for exec, output in results1: + print(exec, output) + lines = output.split("\n") + for line in lines: + if line.__contains__("elapsed"): + try: + minutes, seconds = line.split()[2].replace("elapsed", "").split(":") + seconds, milliseconds = seconds.split(".") + + # Convert each part to seconds (note that milliseconds are converted and added as a fraction of a second) + total_seconds = ( + int(minutes) * 60 + int(seconds) + int(milliseconds) / 1000 + ) + + print(total_seconds) + exec_time = total_seconds + except: + if line.__contains__("user"): + exec_time = float(line.split()[0].replace("user", "")) + results.append((exec, exec_time)) + + return results + + +def run_native(): + results = [] + results1 = [] + for _ in range(common_util.trial): + for i in range(len(cmd)): + aot = cmd[i] + results1.append( + pool.apply_async( + common_util.run_native, + (aot, folder[i], arg[i], envs[i]), + ) + ) + results1 = [x.get() for x in results1] + for exec, output in results1: + print(exec, output) + lines = output.split("\n") + for line in lines: + if line.__contains__("elapsed"): + try: + minutes, seconds = line.split()[2].replace("elapsed", "").split(":") + seconds, milliseconds = seconds.split(".") + + # Convert each part to seconds (note that milliseconds are converted and added as a fraction of a second) + total_seconds = ( + int(minutes) * 60 + int(seconds) + int(milliseconds) / 1000 + ) + + print(total_seconds) + exec_time = total_seconds + except: + if line.__contains__("user"): + exec_time = float(line.split()[0].replace("user", "")) + results.append((exec, exec_time)) + + return results + + +def write_to_csv(filename): + # 'data' is a list of tuples, e.g., [(checkpoint_result_0, checkpoint_result_1, restore_result_2), ...] + + with open(filename, "a+", newline="") as csvfile: + writer = csv.writer(csvfile) + # Optionally write headers + writer.writerow( + ["name", "mvvm", "hcontainer", "qemu_x86_64", "qemu_aach64", "native"] + ) + + # Write the data + for idx, row in enumerate(mvvm_results): + writer.writerow( + [ + row[0], + row[1], + hcontainer_results[idx][1], + qemu_x86_64_results[idx][1], + qemu_aarch64_results[idx][1], + native_results[idx][1], + ] + ) +def read_from_csv(filename): + with open(filename, "r") as csvfile: + reader = csv.reader(csvfile) + next(reader) + results = [] + for row in reader: + results.append((row[0], float(row[1]), float(row[2]), float(row[3]), float(row[4]), float(row[5]))) + return results + +def plot(results): + font = {'size': 18} + + plt.rc('font', **font) + workloads = defaultdict(list) + for workload,hcontainer_values, mvvm_values, qemu_x86_64_values,qemu_aarch64_values,native_values in results: + workloads[ + workload.replace("OMP_NUM_THREADS=", "") + .replace("-g20", "") + .replace("-n300", "") + .replace(" -f ", "") + .replace("-vn300", "") + .replace("maze-6404.txt", "") + .replace("stories110M.bin", "") + .replace("-z tokenizer.bin -t 0.0", "") + .strip() + ].append(( hcontainer_values, mvvm_values, qemu_x86_64_values,qemu_aarch64_values,native_values)) + + statistics = {} + for workload, times in workloads.items(): + hcontainer_values, mvvm_values, qemu_x86_64_values,qemu_aarch64_values,native_values= zip(*times) + statistics[workload] = { + "hcontainer_median": np.median(hcontainer_values), + "mvvm_median": np.median(mvvm_values), + "qemu_x86_64_median" :np.median(qemu_x86_64_values), + "qemu_aarch64_median" :np.median(qemu_aarch64_values), + "native_median" :np.median(native_values), + "hcontainer_std": np.std(hcontainer_values), + "mvvm_std": np.std(mvvm_values), + "qemu_x86_64_std" :np.std(qemu_x86_64_values), + "qemu_aarch64_std" :np.std(qemu_aarch64_values), + "native_std" :np.std(native_values), + } + + fig, ax = plt.subplots(figsize=(20, 10)) + index = np.arange(len(statistics)) + bar_width = 0.7/5 + + for i, (workload, stats) in enumerate(statistics.items()): + ax.bar( + index[i], + stats["hcontainer_median"], + bar_width, + yerr=stats["hcontainer_std"], + capsize=5, + color="blue", + label="hcontainer" if i == 0 else "", + ) + ax.bar( + index[i] + bar_width, + stats["mvvm_median"], + bar_width, + yerr=stats["mvvm_std"], + capsize=5, + color="red", + label="mvvm" if i == 0 else "", + ) + ax.bar( + index[i]+ bar_width *2, + stats["qemu_x86_64_median"], + bar_width, + yerr=stats["qemu_x86_64_std"], + capsize=5, + color="brown", + label="qemu_x86_64" if i == 0 else "", + ) + ax.bar( + index[i]+ bar_width*3, + stats["qemu_aarch64_median"], + bar_width, + yerr=stats["qemu_aarch64_std"], + capsize=5, + color="purple", + label="qemu_aarch64" if i == 0 else "", + ) + ax.bar( + index[i]+ bar_width*4, + stats["native_median"], + bar_width, + yerr=stats["native_std"], + capsize=5, + color="cyan", + label="native" if i == 0 else "", + ) + # ax.set_xlabel(workload) + ticklabel = (x for x in list(statistics.keys())) + print(statistics.keys()) + ax.set_xticks(index) + + ax.set_xticklabels(ticklabel,fontsize =10) + ax.set_ylabel("Execution time (s)") + ax.legend() + + # add text at upper left + ax.legend(loc="upper right") + + plt.savefig("performance_comparison.pdf") + + +if __name__ == "__main__": + # mvvm_results = run_mvvm() + # native_results = run_native() + # qemu_x86_64_results = run_qemu_x86_64() + # # # print the results + # qemu_aarch64_results = run_qemu_aarch64() + # hcontainer_results = run_hcontainer() + + # write_to_csv("comparison.csv") + + results = read_from_csv("comparison.csv") + plot(results) diff --git a/artifact/bench_policy_mac.py b/artifact/bench_policy_mac.py new file mode 100644 index 0000000..ff1384c --- /dev/null +++ b/artifact/bench_policy_mac.py @@ -0,0 +1,299 @@ +import csv +import common_util +from multiprocessing import Pool +import matplotlib.pyplot as plt +import numpy as np +from collections import defaultdict + +cmd = [ + "llama", + "bc", + "bfs", + "cc", + "cc_sv", + "pr", + "pr_spmv", + "sssp", + "tc", + "bt", + "cg", + "ep", + "ft", + "lu", + "mg", + "sp", + "redis", + "hdastar", +] +folder = [ + "llama", + "gapbs", + "gapbs", + "gapbs", + "gapbs", + "gapbs", + "gapbs", + "gapbs", + "gapbs", + "nas", + "nas", + "nas", + "nas", + "nas", + "nas", + "nas", + "redis", + "hdastar", +] +arg = [ + ["stories110M.bin", "-z", "tokenizer.bin", "-t", "0.0"], + ["-g20", "-vn300"], + ["-g20", "-vn300"], + ["-g20", "-vn300"], + ["-g20", "-vn300"], + ["-g20", "-vn300"], + ["-g20", "-vn300"], + ["-g20", "-vn300"], + ["-g20", "-n1"], + [], + [], + [], + [], + [], + [], + [], + [], + ["maze-6404.txt", "8"], +] +envs = [ + "OMP_NUM_THREADS=1", + "OMP_NUM_THREADS=1", + "OMP_NUM_THREADS=1", + "OMP_NUM_THREADS=1", + "OMP_NUM_THREADS=1", + "OMP_NUM_THREADS=1", + "OMP_NUM_THREADS=1", + "OMP_NUM_THREADS=1", + "OMP_NUM_THREADS=1", + "OMP_NUM_THREADS=1", + "OMP_NUM_THREADS=1", + "OMP_NUM_THREADS=1", + "OMP_NUM_THREADS=1", + "OMP_NUM_THREADS=1", + "OMP_NUM_THREADS=1", + "OMP_NUM_THREADS=1", + "OMP_NUM_THREADS=1", + "a=b", + "a=b", +] + +pool = Pool(processes=6) + + +def run_mvvm(): + results_0 = [] + results_1 = [] + results_2 = [] + results_3 = [] + results_4 = [] + results_5 = [] + results = [] + name = [] + results1 = [] + for _ in range(common_util.trial): + for i in range(len(cmd)): + for j in range(len(common_util.aot_variant)): + aot = cmd[i] + common_util.aot_variant[j] + results1.append( + pool.apply_async(common_util.run, (aot, arg[i], envs[i])) + ) + # print the results + results1 = [x.get() for x in results1] + exec_time = "" + for exec, output in results1: + lines = output.split("\n") + for line in lines: + if line.__contains__("Execution time:"): + exec_time = line.split(" ")[-2] + # print(exec, exec_time) + + for a in common_util.aot_variant: + if exec.__contains__(a): + if a == "-pure.aot": + results_1.append(exec_time) + elif a == "-stack.aot": + results_2.append(exec_time) + elif a == "-ckpt-every-dirty.aot": + results_3.append(exec_time) + elif a == "-ckpt-loop.aot": + results_4.append(exec_time) + elif a == "-ckpt-loop-dirty.aot": + results_5.append(exec_time) + elif a==".aot" and not exec.__contains__("-pure.aot") and not exec.__contains__("-stack.aot")and not exec.__contains__("-ckpt-every-dirty.aot") and not exec.__contains__("-ckpt-loop.aot") and not exec.__contains__("-ckpt-loop-dirty.aot"): + results_0.append(exec_time) + name.append(exec) + results = list( + zip( + name, + results_0, + results_1, + results_2, + results_3, + results_4, + results_5, + ) + ) + print(results) + print("results_0",results_0) + print("results_1",results_1) + return results + + +def write_to_csv(filename): + # 'data' is a list of tuples, e.g., [(checkpoint_result_0, checkpoint_result_1, restore_result_2), ...] + with open(filename, "a+", newline="") as csvfile: + writer = csv.writer(csvfile) + # Optionally write headers + writer.writerow( + [ + "name", + "aot", + "pure.aot", + "stack.aot", + "ckpt-every-dirty.aot", + "ckpt-loop.aot", + "ckpt-loop-dirty.aot", + ] + ) + + # Write the data + for idx, row in enumerate(mvvm_results): + writer.writerow( + [row[0], row[1], row[2], row[3], row[4], row[5], row[6]] + ) + +def read_from_csv(filename): + with open(filename, "r") as csvfile: + reader = csv.reader(csvfile) + next(reader) + results = [] + for row in reader: + results.append((row[0], float(row[1]), float(row[2]), float(row[3]), float(row[4]), float(row[5]), float(row[6]))) + return results + + +def plot(results): + font = {'size': 18} + + plt.rc('font', **font) + workloads = defaultdict(list) + for workload,pure, aot, stack,ckpt_every,loop,loop_dirty in results: + workloads[ + workload.replace("OMP_NUM_THREADS=", "") + .replace("-g20", "") + .replace("-n300", "") + .replace(" -f ", "") + .replace("-vn300", "") + .replace("maze-6404.txt", "") + .replace("stories110M.bin", "") + .replace("-z tokenizer.bin -t 0.0", "") + .strip() + ].append(( pure,aot, stack,loop,loop_dirty,ckpt_every)) + + statistics = {} + for workload, times in workloads.items(): + pures,aots, stacks,loops,loop_dirtys,ckpt_everys= zip(*times) + statistics[workload] = { + "pure_median": np.median(pures), + "aot_median": np.median(aots), + "loop_median" :np.median(loops), + "loop_dirty_median" :np.median(loop_dirtys), + "ckpt_every_median" :np.median(ckpt_everys), + "stack_median": np.median(stacks), + "pure_std": np.std(pures), + "aot_std": np.std(aots), + "loop_std" :np.std(loops), + "loop_dirty_std" :np.std(loop_dirtys), + "ckpt_every_std" :np.std(ckpt_everys), + "stack_std": np.std(stacks), + } + + fig, ax = plt.subplots(figsize=(20, 10)) + index = np.arange(len(statistics)) + bar_width = 0.7 + + for i, (workload, stats) in enumerate(statistics.items()): + ax.bar( + index[i], + stats["ckpt_every_median"], + bar_width, + yerr=stats["ckpt_every_std"], + capsize=5, + color="blue", + label="ckpt_every" if i == 0 else "", + ) + ax.bar( + index[i], + stats["loop_dirty_median"], + bar_width, + yerr=stats["loop_dirty_std"], + capsize=5, + color="red", + label="loop_dirty" if i == 0 else "", + ) + ax.bar( + index[i], + stats["loop_median"], + bar_width, + yerr=stats["loop_std"], + capsize=5, + color="brown", + label="loop" if i == 0 else "", + ) + ax.bar( + index[i], + stats["stack_median"], + bar_width, + yerr=stats["stack_std"], + capsize=5, + color="purple", + label="stack" if i == 0 else "", + ) + ax.bar( + index[i], + stats["aot_median"], + bar_width, + yerr=stats["aot_std"], + capsize=5, + color="cyan", + label="aot" if i == 0 else "", + ) + ax.bar( + index[i], + stats["pure_median"], + bar_width, + yerr=stats["pure_std"], + capsize=5, + color="green", + label="pure" if i == 0 else "", + ) + # ax.set_xlabel(workload) + ticklabel = (x for x in list(statistics.keys())) + print(statistics.keys()) + ax.set_xticks(index) + + ax.set_xticklabels(ticklabel,fontsize =10) + ax.set_ylabel("Execution time (s)") + ax.legend() + + # add text at upper left + ax.legend(loc="upper right") + + plt.savefig("performance_singlethread.pdf") + + +if __name__ == "__main__": + # mvvm_results = run_mvvm() + # write_to_csv("policy.csv") + mvvm_results = read_from_csv("policy.csv") + plot(mvvm_results) \ No newline at end of file diff --git a/artifact/result/performance_comparison_mac.pdf b/artifact/result/performance_comparison_mac.pdf new file mode 100644 index 0000000000000000000000000000000000000000..d5e0f5f19c35bd9eb68fc48a7c3bf16b957744bc GIT binary patch literal 16957 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 literal 0 HcmV?d00001 diff --git a/artifact/result/performance_singlethread_mac.pdf b/artifact/result/performance_singlethread_mac.pdf new file mode 100644 index 0000000000000000000000000000000000000000..b149c15316ab2daa112dc31c8f8882c23ed11e43 GIT binary patch literal 19284 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