diff options
| -rw-r--r-- | .gitignore | 2 | ||||
| -rwxr-xr-x | bench.py | 37 | ||||
| -rw-r--r-- | src/Makefile | 6 | ||||
| -rw-r--r-- | src/allocators.py | 22 | ||||
| -rw-r--r-- | src/benchmark.py | 114 | ||||
| -rw-r--r-- | src/chattymalloc.c | 161 | ||||
| -rw-r--r-- | src/chattyparser.py | 165 | ||||
| -rw-r--r-- | src/dj_trace.py | 56 | ||||
| -rw-r--r-- | src/falsesharing.py | 12 | ||||
| -rw-r--r-- | src/mysql.py | 95 | ||||
| -rw-r--r-- | src/targets.py | 19 | ||||
| -rw-r--r-- | targets/Makefile | 22 |
12 files changed, 131 insertions, 580 deletions
@@ -1,6 +1,6 @@ build/* results/* -targets/*.so +allocators/*.so dj_workloads/* *.png *__pycache__* @@ -7,23 +7,22 @@ import os import subprocess import src.facter -import src.targets +import src.allocators benchmarks = ["loop", "mysql", "falsesharing", "dj_trace", "larson"] parser = argparse.ArgumentParser(description="benchmark memory allocators") parser.add_argument("-s", "--save", help="save benchmark results to disk", action='store_true') parser.add_argument("-l", "--load", help="load benchmark results from directory", type=str) -parser.add_argument("-t", "--targets", help="load target definitions from file", type=str) +parser.add_argument("-a", "--allocators", help="load allocator definitions from file", type=str) parser.add_argument("-r", "--runs", help="how often the benchmarks run", default=3, type=int) parser.add_argument("-v", "--verbose", help="more output", action='store_true') parser.add_argument("-b", "--benchmarks", help="benchmarks to run", nargs='+') parser.add_argument("-ns", "--nosum", help="don't produce plots", action='store_true') parser.add_argument("-sd", "--resultdir", help="directory where all results go", type=str) -parser.add_argument("-a", "--analyse", help="collect allocation sizes", action='store_true') -parser.add_argument("--nolibmemusage", help="don't use libmemusage to analyse", action='store_true') parser.add_argument("--license", help="print license info and exit", action='store_true') + def main(): args = parser.parse_args() if args.license: @@ -31,6 +30,9 @@ def main(): print("License GPLv3: GNU GPL version 3 <http://gnu.org/licenses/gpl.html>") return + if args.verbose: + print(args) + # Prepare allocbench print("Building allocbench") make_cmd = ["make"] @@ -39,16 +41,18 @@ def main(): subprocess.run(make_cmd) - if args.verbose: - print(args) + allocators_file = os.path.join("build", "allocators", "allocators.py") + + if args.allocators or os.path.isfile(allocators_file): + allocators_files = args.allocators or allocators_file - if args.targets: - with open(args.targets, "r") as f: - g = {} + with open(allocators_files, "r") as f: + g = {"verbose": args.verbose} exec(f.read(), g) - src.targets.targets = g["targets"] - if args.verbose: - print("Targets:", src.targets.targets.keys()) + src.allocators.allocators = g["allocators"] + + if args.verbose: + print("Allocators:", *src.allocators.allocators.keys()) if args.save or not args.nosum and not (args.runs < 1 and not args.load): if args.resultdir: @@ -68,17 +72,12 @@ def main(): if args.load: bench.load(path=args.load) - if args.runs > 0 or args.analyse: + if args.runs > 0: print("Preparing", bench.name, "...") if not bench.prepare(): print("Preparing", bench.name, "failed!") return - if args.analyse and hasattr(bench, "analyse") and callable(bench.analyse): - print("Analysing", bench.name, "...") - analyse_args = {"nolibmemusage": args.nolibmemusage, "verbose": args.verbose} - bench.analyse(**analyse_args) - if not bench.run(runs=args.runs, verbose=args.verbose): continue @@ -100,7 +99,7 @@ def main(): os.chdir(old_cwd) - if (args.runs > 0 or args.analyse) and hasattr(bench, "cleanup"): + if args.runs > 0 and hasattr(bench, "cleanup"): print("Cleaning up", bench.name, "...") bench.cleanup() diff --git a/src/Makefile b/src/Makefile index 7173c99..e29af5d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -12,16 +12,12 @@ LDFLAGS ?= -pthread -static-libgcc .PHONY: all clean -all: $(OBJDIR)/print_status_on_exit.so $(OBJDIR)/chattymalloc.so +all: $(OBJDIR)/print_status_on_exit.so $(OBJDIR)/print_status_on_exit.so: print_status_on_exit.c | $(OBJDIR) @echo "Compiling $@..."; $(CC) $(LDFLAGS) -shared $(CFLAGS) -o $@ $< -$(OBJDIR)/chattymalloc.so: chattymalloc.c | $(OBJDIR) - @echo "Compiling $@..."; - $(CC) $(LDFLAGS) -shared $(CFLAGS) -o $@ $< - $(OBJDIR): mkdir $@ diff --git a/src/allocators.py b/src/allocators.py new file mode 100644 index 0000000..dbf26d1 --- /dev/null +++ b/src/allocators.py @@ -0,0 +1,22 @@ +"""Default allocators using system libraries""" + +import os +import subprocess + +maybe_allocators = ["tcmalloc", "jemalloc", "hoard"] + +allocators = {"libc": {"cmd_prefix" : "", + "binary_suffix" : "", + "LD_PRELOAD" : "", + "color" : "C1"}} + +for i, t in enumerate(maybe_allocators): + try: + path = subprocess.check_output('whereis lib{} | cut -d":" -f2'.format(t), + shell=True, text=True).strip() + + if path != "": + allocators[t] = {"cmd_prefix": "", "binary_suffix": "", + "LD_PRELOAD": path, "color": "C"+str(i+2)} + except: + pass diff --git a/src/benchmark.py b/src/benchmark.py index e29df66..1aa5cf9 100644 --- a/src/benchmark.py +++ b/src/benchmark.py @@ -8,7 +8,7 @@ import pickle import shutil import subprocess -from src.targets import targets +from src.allocators import allocators class Benchmark (object): @@ -19,9 +19,8 @@ class Benchmark (object): "your own useful one."), "measure_cmd": "perf stat -x, -d", - "analyse_cmd": "memusage -p {} -t", "cmd": "true", - "targets": targets, + "allocators": allocators, } def __init__(self): @@ -39,8 +38,8 @@ class Benchmark (object): if not hasattr(self, "results"): self.results = {} self.results["args"] = self.args - self.results["targets"] = self.targets - self.results.update({t: {} for t in self.targets}) + self.results["allocators"] = self.allocators + self.results.update({t: {} for t in self.allocators}) if not hasattr(self, "requirements"): self.requirements = [] @@ -53,11 +52,11 @@ class Benchmark (object): # into lists of dicts. save_data = {} save_data.update(self.results) - for target in self.results["targets"]: + for allocator in self.results["allocators"]: tmp_list = [] - for ntuple, measures in self.results[target].items(): + for ntuple, measures in self.results[allocator].items(): tmp_list.append((ntuple._asdict(), measures)) - save_data[target] = tmp_list + save_data[allocator] = tmp_list with open(f, "wb") as f: pickle.dump(save_data, f) @@ -75,11 +74,11 @@ class Benchmark (object): with open(f, "rb") as f: self.results = pickle.load(f) # Build new named tuples - for target in self.results["targets"]: + for allocator in self.results["allocators"]: d = {} - for dic, measures in self.results[target]: + for dic, measures in self.results[allocator]: d[self.Perm(**dic)] = measures - self.results[target] = d + self.results[allocator] = d def prepare(self, verbose=False): def is_exe(fpath): @@ -128,76 +127,23 @@ class Benchmark (object): if is_fixed: yield p - def analyse(self, verbose=False, nolibmemusage=True): - if not nolibmemusage and not shutil.which("memusage"): - print("memusage not found. Using chattymalloc.") - nolibmemusage = True - - if nolibmemusage: - import chattyparser - actual_cmd = "" - old_preload = os.environ.get("LD_PRELOAD", None) - os.environ["LD_PRELOAD"] = "build/chattymalloc.so" - - n = len(list(self.iterate_args())) - for i, perm in enumerate(self.iterate_args()): - print(i + 1, "of", n, "\r", end='') - perm = perm._asdict() - file_name = self.name + "." - file_name += ".".join([str(x) for x in perm.values()]) - file_name += ".memusage" - - if not nolibmemusage: - actual_cmd = self.analyse_cmd.format(file_name + ".png") + " " - - if "binary_suffix" in self.cmd: - perm["binary_suffix"] = "" - actual_cmd += self.cmd.format(**perm) - - res = subprocess.run(actual_cmd.split(), - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - universal_newlines=True) - - if res.returncode != 0: - print(actual_cmd, "failed.") - print("Stdout:", res.stdout) - print("Stderr:", res.stderr) - print("Aborting analysing.") - return - - if nolibmemusage: - try: - chattyparser.plot() - except MemoryError: - print("Can't Analyse", actual_cmd, "with chattymalloc", - "because to much memory would be needed.") - continue - else: - with open(file_name + ".hist", "w") as f: - f.write(res.stderr) - - if nolibmemusage: - os.environ["LD_PRELOAD"] = old_preload or "" - print() - def run(self, verbose=False, runs=5): if runs > 0: print("Running", self.name, "...") - n = len(list(self.iterate_args())) * len(self.targets) + n = len(list(self.iterate_args())) * len(self.allocators) for run in range(1, runs + 1): print(str(run) + ". run") i = 0 - for tname, t in self.targets.items(): + for tname, t in self.allocators.items(): if tname not in self.results: self.results[tname] = {} os.environ["LD_PRELOAD"] = "build/print_status_on_exit.so " os.environ["LD_PRELOAD"] += t["LD_PRELOAD"] - if hasattr(self, "pretarget_hook"): - if self.pretarget_hook((tname, t), run, verbose): + if hasattr(self, "preallocator_hook"): + if self.preallocator_hook((tname, t), run, verbose): return False for perm in self.iterate_args(): @@ -261,8 +207,8 @@ class Benchmark (object): self.results[tname][perm] = [] self.results[tname][perm].append(result) - if hasattr(self, "posttarget_hook"): - if self.posttarget_hook((tname, t), run, verbose): + if hasattr(self, "postallocator_hook"): + if self.postallocator_hook((tname, t), run, verbose): return False print() os.environ["PATH"] = os.environ["PATH"].replace(":build/" + self.name, "") @@ -273,15 +219,15 @@ class Benchmark (object): sumdir="", arg=""): args = self.results["args"] - targets = self.results["targets"] + allocators = self.results["allocators"] arg = arg or list(args.keys())[0] - for target in targets: + for allocator in allocators: y_vals = [] for perm in self.iterate_args(args=args): d = [] - for m in self.results[target][perm]: + for m in self.results[allocator][perm]: d.append(eval(yval.format(**m))) y_vals.append(np.mean(d)) if not autoticks: @@ -289,7 +235,7 @@ class Benchmark (object): else: x_vals = args[arg] plt.plot(x_vals, y_vals, marker='.', linestyle='-', - label=target, color=targets[target]["color"]) + label=allocator, color=allocators[allocator]["color"]) plt.legend() if not autoticks: @@ -305,16 +251,16 @@ class Benchmark (object): sumdir="", fixed=[]): args = self.results["args"] - targets = self.results["targets"] + allocators = self.results["allocators"] for arg in fixed or args: loose_arg = [a for a in args if a != arg][0] for arg_value in args[arg]: - for target in targets: + for allocator in allocators: y_vals = [] for perm in self.iterate_args_fixed({arg: arg_value}, args=args): d = [] - for m in self.results[target][perm]: + for m in self.results[allocator][perm]: d.append(eval(yval.format(**m))) y_vals.append(np.mean(d)) if not autoticks: @@ -322,7 +268,7 @@ class Benchmark (object): else: x_vals = args[loose_arg] plt.plot(x_vals, y_vals, marker='.', linestyle='-', - label=target, color=targets[target]["color"]) + label=allocator, color=allocators[allocator]["color"]) plt.legend() if not autoticks: @@ -338,7 +284,7 @@ class Benchmark (object): filepostfix="", sumdir="", std=False): args = self.results["args"] keys = list(args.keys()) - targets = self.results["targets"] + allocators = self.results["allocators"] header_arg = keys[0] if len(args[keys[0]]) < len(args[keys[1]]) else keys[1] row_arg = [arg for arg in args if arg != header_arg][0] @@ -352,20 +298,20 @@ class Benchmark (object): for perm in self.iterate_args_fixed({row_arg: av}, args=args): best = [] best_val = None - for target in targets: + for allocator in allocators: d = [] - for m in self.results[target][perm]: + for m in self.results[allocator][perm]: d.append(eval(evaluation.format(**m))) mean = np.mean(d) if not best_val: - best = [target] + best = [allocator] best_val = mean elif ((sort == ">" and mean > best_val) or (sort == "<" and mean < best_val)): - best = [target] + best = [allocator] best_val = mean elif mean == best_val: - best.append(target) + best.append(allocator) row.append("{}: {:.3f}".format(best[0], best_val)) cell_text.append(row) diff --git a/src/chattymalloc.c b/src/chattymalloc.c deleted file mode 100644 index 54708d6..0000000 --- a/src/chattymalloc.c +++ /dev/null @@ -1,161 +0,0 @@ -#define _GNU_SOURCE -#include <dlfcn.h> -#include <errno.h> -#include <fcntl.h> -#include <stdarg.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -static char tmpbuff[1024]; -static unsigned long tmppos = 0; -static unsigned long tmpallocs = 0; - -static int out = -1; -static int prevent_recursion = 0; - -/*========================================================= - * * interception points - * */ - -static void * (*myfn_malloc)(size_t size); -static void (*myfn_free)(void* ptr); -static void * (*myfn_calloc)(size_t nmemb, size_t size); -static void * (*myfn_realloc)(void* ptr, size_t size); -static void * (*myfn_memalign)(size_t alignment, size_t size); - -static void write_output(const char* fmt, ...) -{ - if (!prevent_recursion) - { - prevent_recursion = 1; - - /* lockf(out, F_LOCK, 0); */ - - va_list args; - va_start(args, fmt); - vdprintf(out, fmt, args); - va_end(args); - - /* lockf(out, F_ULOCK, 0); */ - prevent_recursion = 0; - } -} - -static void init() -{ - out = open("chattymalloc.data", O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - if (out == -1) - { - fprintf(stderr, "failed to open output file with %d\n", errno); - exit(1); - } - - myfn_malloc = dlsym(RTLD_NEXT, "malloc"); - myfn_free = dlsym(RTLD_NEXT, "free"); - myfn_calloc = dlsym(RTLD_NEXT, "calloc"); - myfn_realloc = dlsym(RTLD_NEXT, "realloc"); - myfn_memalign = dlsym(RTLD_NEXT, "memalign"); - - if (!myfn_malloc || !myfn_free || !myfn_calloc || !myfn_realloc || !myfn_memalign) - { - fprintf(stderr, "Error in `dlsym`: %s\n", dlerror()); - exit(1); - } -} - -void *malloc(size_t size) -{ - static int initializing = 0; - - if (myfn_malloc == NULL) - { - if (!initializing) - { - initializing = 1; - init(); - initializing = 0; - - } - else - { - if (tmppos + size < sizeof(tmpbuff)) - { - void *retptr = tmpbuff + tmppos; - tmppos += size; - ++tmpallocs; - return retptr; - } - else - { - fprintf(stderr, "%d in %d allocs\n", tmppos, tmpallocs); - fprintf(stderr, "jcheck: too much memory requested during initialisation - increase tmpbuff size\n"); - exit(1); - } - } - } - - void *ptr = myfn_malloc(size); - write_output("m %zu %p\n", size, ptr); - return ptr; -} - -void free(void *ptr) -{ - // something wrong if we call free before one of the allocators! - if (myfn_malloc == NULL) - init(); - if (!(ptr >= (void*) tmpbuff && ptr <= (void*)(tmpbuff + tmppos))) - { - write_output("f %p\n", ptr); - myfn_free(ptr); - } -} - -void* realloc(void *ptr, size_t size) -{ - if (myfn_realloc == NULL) - { - void *nptr = malloc(size); - if (nptr && ptr) - { - memmove(nptr, ptr, size); - free(ptr); - } - return nptr; - } - - void* nptr = myfn_realloc(ptr, size); - write_output("r %p %zu %p\n", ptr, size, nptr); - return nptr; -} - -void* calloc(size_t nmemb, size_t size) -{ - if (myfn_calloc == NULL) - { - void *ptr = malloc(nmemb*size); - if (ptr) - memset(ptr, 0, nmemb*size); - return ptr; - } - - void* ptr = myfn_calloc(nmemb, size); - write_output("c %zu %zu %p\n", nmemb, size, ptr); - return ptr; -} - -void* memalign(size_t alignment, size_t size) -{ - if (myfn_memalign == NULL) - { - fprintf(stderr, "called memalign before or during init"); - exit(1); - } - - void* ptr = myfn_memalign(alignment, size); - write_output("mm %zu %zu %p\n", alignment, size, ptr); - return ptr; -} diff --git a/src/chattyparser.py b/src/chattyparser.py deleted file mode 100644 index b1b6cd0..0000000 --- a/src/chattyparser.py +++ /dev/null @@ -1,165 +0,0 @@ -import re -import matplotlib.pyplot as plt - -ptr = "(?:0x)?(?P<ptr>(?:\w+)|(?:\(nil\)))" -size = "(?P<size>\d+)" - -malloc_re = re.compile("^m {} {}$".format(size, ptr)) -free_re = re.compile("^f {}$".format(ptr)) -calloc_re = re.compile("^c (?P<nmemb>\d+) {} {}$".format(size, ptr)) -realloc_re = re.compile("^r {} {} {}$".format(ptr, size, ptr.replace("ptr", "nptr"))) -memalign_re = re.compile("^mm (?P<alignment>\d+) {} {}$".format(size, ptr)) - - -def record_allocation(hist, total_size, allocations, ptr, size, coll_size, - req_size, nohist, optr=None, add=True): - size = int(size) - if add: - if optr and optr in allocations: - size -= allocations[optr] - del(allocations[optr]) - - allocations[ptr] = size - if not nohist: - hist[size] = hist.get(size, 0) + 1 - - if type(total_size[-1]) != int or type(size) != int: - print("invalid type", type(total_size[-1]), type(size)) - return - - if coll_size: - if not req_size or size == req_size: - total_size.append(total_size[-1] + size) - elif req_size: - total_size.append(total_size[-1]) - - elif ptr != "(nil)" and ptr in allocations: - size = allocations[ptr] - if coll_size: - if not req_size or size == req_size: - total_size.append(total_size[-1] - size) - elif req_size: - total_size.append(total_size[-1]) - - del(allocations[ptr]) - elif coll_size: - total_size.append(total_size[-1]) - - -def parse(path="chattymalloc.data", coll_size=True, req_size=None, nohist=False): - tmalloc, tcalloc, trealloc, tfree, tmemalign = 0, 0, 0, 0, 0 - allocations = {} - requested_size = [0] - hist = {} - ln = 0 - - with open(path, "r") as f: - for i, l in enumerate(f.readlines()): - ln += 1 - res = malloc_re.match(l) - if res is not None: - res = res.groupdict() - record_allocation(hist, requested_size, allocations, res["ptr"], - res["size"], coll_size, req_size, nohist) - tmalloc += 1 - continue - - res = free_re.match(l) - if res is not None: - res = res.groupdict() - record_allocation(hist, requested_size, allocations, res["ptr"], - 0, coll_size, req_size, nohist, add=False) - tfree += 1 - continue - - res = calloc_re.match(l) - if res is not None: - res = res.groupdict() - size = int(res["nmemb"]) * int(res["size"]) - record_allocation(hist, requested_size, allocations, res["ptr"], - size, coll_size, req_size, nohist) - tcalloc += 1 - continue - - res = realloc_re.match(l) - if res is not None: - res = res.groupdict() - record_allocation(hist, requested_size, allocations, res["nptr"], - res["size"], coll_size, req_size, nohist, - optr=res["ptr"]) - trealloc += 1 - continue - - res = memalign_re.match(l) - if res is not None: - res = res.groupdict() - record_allocation(hist, requested_size, allocations, res["ptr"], - res["size"], coll_size, req_size, nohist) - tmemalign += 1 - continue - - print("\ninvalid line at", ln, ":", l) - calls = {"malloc": tmalloc, "free": tfree, "calloc": tcalloc, - "realloc": trealloc, "memalign": tmemalign} - return hist, calls, requested_size - - -def plot(path): - hist, calls, _ = parse(req_size=None) - plot_hist_ascii(path+".hist", hist, calls) - top5 = [t[1] for t in sorted([(n, s) for s, n in hist.items()])[-5:]] - - del(hist) - del(calls) - plot_profile(path+".profile.png", top5) - - -def plot_profile(path, top5): - _, calls, total_size = parse(nohist=True) - x_vals = range(0, sum(calls.values()) + 1) - - plt.plot(x_vals, total_size, marker='', - linestyle='-', label="Total requested") - - for s in top5: - _, calls, total_size = parse(nohist=True, req_size=s) - plt.plot(x_vals, total_size, label=s) - - plt.legend() - plt.xlabel("Allocations") - plt.ylabel("mem in kb") - plt.title("Memusage profile") - plt.savefig(path) - plt.clf() - - -def plot_hist_ascii(path, hist, calls): - bins = {} - for size in sorted(hist): - bin = int(size / 16) - bins[bin] = bins.get(bin, 0) + hist[size] - - total = sum(calls.values()) - calls["free"] - with open(path, "w") as f: - print("Total function calls:", total, file=f) - print("malloc:", calls["malloc"], file=f) - print("calloc:", calls["calloc"], file=f) - print("realloc:", calls["realloc"], file=f) - print("free:", calls["free"], file=f) - print("memalign:", calls["memalign"], file=f) - print(file=f) - - print("< 1024", sum([n for s, n in hist.items() if s < 1024]), file=f) - print("< 4096", sum([n for s, n in hist.items() if s < 4096]), file=f) - print(file=f) - - print("Histogram of sizes:", file=f) - sbins = sorted(bins) - binmaxlength = str(len(str(sbins[-1])) + 1) - amountmaxlength = str(len(str(sorted(bins.values())[-1]))) - for b in sbins: - perc = bins[b]/total*100 - binsize = "{:<" + binmaxlength + "} - {:>" + binmaxlength + "}" - print(binsize.format((b)*16, (b+1)*16-1), end=" ", file=f) - amount = "{:<" + amountmaxlength + "} {:.2f}% {}" - print(amount.format(bins[b], perc, '*'*int(perc/2)), file=f) diff --git a/src/dj_trace.py b/src/dj_trace.py index 784b16f..d61e64d 100644 --- a/src/dj_trace.py +++ b/src/dj_trace.py @@ -109,7 +109,7 @@ class Benchmark_DJ_Trace(Benchmark): sys.stderr.write("\n") return True - def process_output(self, result, stdout, stderr, target, perm, verbose): + def process_output(self, result, stdout, stderr, allocator, perm, verbose): def to_int(s): return int(s.replace(',', "")) @@ -129,14 +129,14 @@ class Benchmark_DJ_Trace(Benchmark): def summary(self): args = self.results["args"] - targets = self.results["targets"] + allocators = self.results["allocators"] # Total times for perm in self.iterate_args(args=args): - for i, target in enumerate(targets): - d = [float(x["cputime"]) for x in self.results[target][perm]] + for i, allocator in enumerate(allocators): + d = [float(x["cputime"]) for x in self.results[allocator][perm]] y_val = np.mean(d)/1000 - plt.bar([i], y_val, label=target, color=targets[target]["color"]) + plt.bar([i], y_val, label=allocator, color=allocators[allocator]["color"]) # ticks_y = ticker.FuncFormatter(lambda x, pos: '{0:g}'.format(x/1000)) # plt.gca().yaxis.set_major_formatter(ticks_y) @@ -150,18 +150,18 @@ class Benchmark_DJ_Trace(Benchmark): # Function Times xa = np.arange(0, 6, 1.5) for perm in self.iterate_args(args=args): - for i, target in enumerate(targets): - x_vals = [x+i/len(targets) for x in xa] + for i, allocator in enumerate(allocators): + x_vals = [x+i/len(allocators) for x in xa] y_vals = [0] * 4 - y_vals[0] = np.mean([x["avg_malloc"] for x in self.results[target][perm]]) - y_vals[1] = np.mean([x["avg_calloc"] for x in self.results[target][perm]]) - y_vals[2] = np.mean([x["avg_realloc"] for x in self.results[target][perm]]) - y_vals[3] = np.mean([x["avg_free"] for x in self.results[target][perm]]) + y_vals[0] = np.mean([x["avg_malloc"] for x in self.results[allocator][perm]]) + y_vals[1] = np.mean([x["avg_calloc"] for x in self.results[allocator][perm]]) + y_vals[2] = np.mean([x["avg_realloc"] for x in self.results[allocator][perm]]) + y_vals[3] = np.mean([x["avg_free"] for x in self.results[allocator][perm]]) plt.bar(x_vals, y_vals, width=0.25, align="center", - label=target, color=targets[target]["color"]) + label=allocator, color=allocators[allocator]["color"]) plt.legend(loc="best") - plt.xticks(xa + 1/len(targets)*2, + plt.xticks(xa + 1/len(allocators)*2, ["malloc\n" + str(self.results[perm.workload]["malloc"]) + "\ncalls", "calloc\n" + str(self.results[perm.workload]["calloc"]) + "\ncalls", "realloc\n" + str(self.results[perm.workload]["realloc"]) + "\ncalls", @@ -173,14 +173,14 @@ class Benchmark_DJ_Trace(Benchmark): # Memusage for perm in self.iterate_args(args=args): - for i, target in enumerate(targets): - d = [x["Max_RSS"] for x in self.results[target][perm]] + for i, allocator in enumerate(allocators): + d = [x["Max_RSS"] for x in self.results[allocator][perm]] y_val = np.mean(d)/1000 - plt.bar([i], y_val, label=target, color=targets[target]["color"]) + plt.bar([i], y_val, label=allocator, color=allocators[allocator]["color"]) # add ideal rss - y_val = self.results[list(targets.keys())[0]][perm][0]["Ideal_RSS"]/1000 - plt.bar([len(targets)], y_val, label="Ideal RSS") + y_val = self.results[list(allocators.keys())[0]][perm][0]["Ideal_RSS"]/1000 + plt.bar([len(allocators)], y_val, label="Ideal RSS") # ticks_y = ticker.FuncFormatter(lambda x, pos: '{0:g}'.format(x/1000)) # plt.gca().yaxis.set_major_formatter(ticks_y) @@ -194,16 +194,16 @@ class Benchmark_DJ_Trace(Benchmark): # Tables for perm in self.iterate_args(args=args): # collect data - d = {target: {} for target in targets} - for i, target in enumerate(targets): - d[target]["time"] = [float(x["cputime"]) for x in self.results[target][perm]] - d[target]["rss"] = [x["Max_RSS"] for x in self.results[target][perm]] + d = {allocator: {} for allocator in allocators} + for i, allocator in enumerate(allocators): + d[allocator]["time"] = [float(x["cputime"]) for x in self.results[allocator][perm]] + d[allocator]["rss"] = [x["Max_RSS"] for x in self.results[allocator][perm]] - times = [np.mean(d[target]["time"]) for target in targets] + times = [np.mean(d[allocator]["time"]) for allocator in allocators] tmin = min(times) tmax = max(times) - rss = [np.mean(d[target]["rss"]) for target in targets] + rss = [np.mean(d[allocator]["rss"]) for allocator in allocators] rssmin = min(rss) rssmax = max(rss) @@ -213,10 +213,10 @@ class Benchmark_DJ_Trace(Benchmark): print("& Zeit (ms) / $\\sigma$ (\\%) & VmHWM (KB) / $\\sigma$ (\\%) \\\\", file=f) print("\\hline", file=f) - for target in targets: - print(target, end=" & ", file=f) + for allocator in allocators: + print(allocator, end=" & ", file=f) - t = d[target]["time"] + t = d[allocator]["time"] m = np.mean(t) s = "\\textcolor{{{}}}{{{:.3f}}} / {:.3f}" if m == tmin: @@ -227,7 +227,7 @@ class Benchmark_DJ_Trace(Benchmark): color = "black" print(s.format(color, m, np.std(t)/m), end=" & ", file=f) - t = d[target]["rss"] + t = d[allocator]["rss"] m = np.mean(t) if m == rssmin: color = "green" diff --git a/src/falsesharing.py b/src/falsesharing.py index 79de436..859db03 100644 --- a/src/falsesharing.py +++ b/src/falsesharing.py @@ -26,31 +26,31 @@ class Benchmark_Falsesharing(Benchmark): self.requirements = ["cache-thrash", "cache-scratch"] super().__init__() - def process_output(self, result, stdout, stderr, target, perm, verbose): + def process_output(self, result, stdout, stderr, allocator, perm, verbose): result["time"] = time_re.match(stdout).group("time") def summary(self): # Speedup thrash args = self.results["args"] nthreads = args["threads"] - targets = self.results["targets"] + allocators = self.results["allocators"] for bench in self.results["args"]["bench"]: - for target in targets: + for allocator in allocators: y_vals = [] single_threaded_perm = self.Perm(bench=bench, threads=1) single_threaded = np.mean([float(m["time"]) - for m in self.results[target][single_threaded_perm]]) + for m in self.results[allocator][single_threaded_perm]]) for perm in self.iterate_args_fixed({"bench": bench}, args=args): - d = [float(m["time"]) for m in self.results[target][perm]] + d = [float(m["time"]) for m in self.results[allocator][perm]] y_vals.append(single_threaded / np.mean(d)) plt.plot(nthreads, y_vals, marker='.', linestyle='-', - label=target, color=targets[target]["color"]) + label=allocator, color=allocators[allocator]["color"]) plt.legend() plt.xlabel("threads") diff --git a/src/mysql.py b/src/mysql.py index ac2ee34..f93a24d 100644 --- a/src/mysql.py +++ b/src/mysql.py @@ -10,7 +10,7 @@ from subprocess import PIPE from time import sleep from src.benchmark import Benchmark -from src.targets import targets +from src.allocators import allocators cwd = os.getcwd() @@ -32,9 +32,9 @@ class Benchmark_MYSQL(Benchmark): self.descrition = """See sysbench documentation.""" # mysqld fails with hoard somehow - self.targets = copy.copy(targets) - if "hoard" in self.targets: - del(self.targets["hoard"]) + self.allocators = copy.copy(allocators) + if "hoard" in self.allocators: + del(self.allocators["hoard"]) self.args = {"nthreads": range(1, multiprocessing.cpu_count() + 1)} self.cmd = cmd @@ -88,7 +88,7 @@ class Benchmark_MYSQL(Benchmark): return False # Create sbtest TABLE - p = subprocess.run("mysql -u root -S "+cwd+"/mysql_test/socket".split(" "), + p = subprocess.run(("mysql -u root -S "+cwd+"/mysql_test/socket").split(" "), input=b"CREATE DATABASE sbtest;\n", stdout=PIPE, stderr=PIPE) @@ -118,20 +118,20 @@ class Benchmark_MYSQL(Benchmark): print("Delete mysqld directory") shutil.rmtree("mysql_test") - def pretarget_hook(self, target, run, verbose): + def preallocator_hook(self, allocator, run, verbose): if not self.start_and_wait_for_server(verbose, - cmd_prefix=target[1]["cmd_prefix"]): - print("Can't start server for", target[0] + ".") + cmd_prefix=allocator[1]["cmd_prefix"]): + print("Can't start server for", allocator[0] + ".") print("Aborting Benchmark.") - print(target[1]["cmd_prefix"]) + print(allocator[1]["cmd_prefix"]) print(self.server.stderr.read()) return False - def posttarget_hook(self, target, run, verbose): + def postallocator_hook(self, allocator, run, verbose): self.server.kill() self.server.wait() - def process_output(self, result, stdout, stderr, target, perm, verbose): + def process_output(self, result, stdout, stderr, allocator, perm, verbose): result["transactions"] = re.search("transactions:\s*(\d*)", stdout).group(1) result["queries"] = re.search("queries:\s*(\d*)", stdout).group(1) # Latency @@ -145,53 +145,8 @@ class Benchmark_MYSQL(Benchmark): result["rssmax"] = int(l.replace("VmHWM:", "").strip().split()[0]) break - def analyse(self, verbose=False, nolibmemusage=""): - import chattyparser - - nthreads = [0] + list(self.args["nthreads"]) - failed = False - - runs = len(nthreads) - for i, t in enumerate(nthreads): - print("analysing", i + 1, "of", runs, "\r", end='') - - os.environ["LD_PRELOAD"] = "build/chattymalloc.so" - if not self.start_and_wait_for_server(verbose): - print("Can't start server.") - print("Aborting analysing.") - failed = True - os.environ["LD_PRELOAD"] = "" - - if not failed and t != 0: - target_cmd = self.cmd.format(nthreads=t).split(" ") - p = subprocess.run(target_cmd, - stderr=PIPE, - stdout=PIPE, - universal_newlines=True) - - if p.returncode != 0: - print("\n" + " ".join(target_cmd), "exited with", - p.returncode, ".\n Aborting analysing.") - print(p.stderr) - print(p.stdout) - failed = True - - self.server.kill() - self.server.wait() - - hist, calls, reqsize, top5reqsize = chattyparser.parse() - chattyparser.plot_hist_ascii(hist, calls, - ".".join([self.name, str(t), - "memusage", "hist"])) - - if failed: - print(self.server.stdout.read()) - print(self.server.stderr.read()) - return False - print() - def summary(self): - targets = self.results["targets"] + allocators = self.results["allocators"] args = self.results["args"] # linear plot @@ -202,14 +157,14 @@ class Benchmark_MYSQL(Benchmark): filepostfix="l.ro") # bar plot - for i, target in enumerate(targets): + for i, allocator in enumerate(allocators): y_vals = [] for perm in self.iterate_args(args=self.results["args"]): - d = [int(m["transactions"]) for m in self.results[target][perm]] + d = [int(m["transactions"]) for m in self.results[allocator][perm]] y_vals.append(np.mean(d)) x_vals = [x-i/8 for x in range(1, len(y_vals) + 1)] - plt.bar(x_vals, y_vals, width=0.2, label=target, align="center", - color=targets[target]["color"]) + plt.bar(x_vals, y_vals, width=0.2, label=allocator, align="center", + color=allocators[allocator]["color"]) plt.legend() plt.xlabel("threads") @@ -227,21 +182,21 @@ class Benchmark_MYSQL(Benchmark): filepostfix="ro.mem") # Colored latex table showing transactions count - d = {target: {} for target in targets} + d = {allocator: {} for allocator in allocators} for perm in self.iterate_args(args=args): - for i, target in enumerate(targets): - t = [float(x["transactions"]) for x in self.results[target][perm]] + for i, allocator in enumerate(allocators): + t = [float(x["transactions"]) for x in self.results[allocator][perm]] m = np.mean(t) s = np.std(t)/m - d[target][perm] = {"mean": m, "std": s} + d[allocator][perm] = {"mean": m, "std": s} mins = {} maxs = {} for perm in self.iterate_args(args=args): cmax = None cmin = None - for i, target in enumerate(targets): - m = d[target][perm]["mean"] + for i, allocator in enumerate(allocators): + m = d[allocator][perm]["mean"] if not cmax or m > cmax: cmax = m if not cmin or m < cmin: @@ -258,10 +213,10 @@ class Benchmark_MYSQL(Benchmark): print("& {}".format(head), end=" ", file=f) print("\\\\\n\\hline", file=f) - for target in targets: - print(target, end=" ", file=f) + for allocator in allocators: + print(allocator, end=" ", file=f) for perm in self.iterate_args(args=args): - m = d[target][perm]["mean"] + m = d[allocator][perm]["mean"] s = "& \\textcolor{{{}}}{{{:.3f}}}" if m == maxs[perm]: color = "green" diff --git a/src/targets.py b/src/targets.py deleted file mode 100644 index 50fcab0..0000000 --- a/src/targets.py +++ /dev/null @@ -1,19 +0,0 @@ -targets = {"glibc" : { - "cmd_prefix" : "", - "binary_suffix" : "", - "LD_PRELOAD" : "", - "color" : "C1" - }, - "tcmalloc" : { - "cmd_prefix" : "", - "binary_suffix" : "", - "LD_PRELOAD" : "targets/libtcmalloc.so", - "color" : "C2" - }, - "jemalloc" : { - "cmd_prefix" : "", - "binary_suffix" : "", - "LD_PRELOAD" : "targets/libjemalloc.so", - "color" : "C3" - }, - } diff --git a/targets/Makefile b/targets/Makefile deleted file mode 100644 index 5df4999..0000000 --- a/targets/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -OBJDIR ?= obj - -CC ?= gcc - -WARNFLAGS ?= -Wall -Wextra -COMMONFLAGS ?= -fno-builtin -fPIC -DPIC -pthread -OPTFLAGS ?= -O3 -DNDEBUG - -CFLAGS ?= $(OPTFLAGS) $(WARNFLAGS) $(COMMONFLAGS) - -LDFLAGS ?= -pthread -static-libgcc - -.PHONY: all clean - -all: $(OBJDIR) - @echo building targets - -$(OBJDIR): - mkdir $@ - -clean: - rm -rf $(OBJDIR) |
