def plot(data=None, output=None): WORKDIR = os.getcwd() RESULTSDIR = data RESULTEXT = '.csv' GROUP_BAR_WIDTH = .8 MEAN_KEY = 'mean' MEDIAN_KEY = 'median' AMAX_KEY = 'amax' AMIN_KEY = 'amin' BASE_ALLOC = 'mimalloc' files = [] apps = [] base_stats = {} other_stats = {} speedup_max = 0 # maximum observed rx mpps bar_colors = { 'mimalloc': '#ddcae3', 'tinyalloc': '#ededed', 'tlsf': '#618c84', 'buddy': '#49687c' } labels = { 'mimalloc': 'mimalloc', 'tinyalloc': 'tinyalloc', 'tlsf': 'TLSF', 'buddy': 'Binary Buddy' } for f in os.listdir(RESULTSDIR): if f.endswith(RESULTEXT): index = f.replace(RESULTEXT,'') files.append(f) alloc = index with open(os.path.join(RESULTSDIR, f), 'r') as csvfile: csvdata = csv.reader(csvfile, delimiter="\t") next(csvdata) # skip header speedups = {} for row in csvdata: if int(row[0]) not in speedups: speedups[int(row[0])] = [] speedups[int(row[0])].append(float(row[1])) if float(row[1]) > speedup_max: speedup_max = float(row[1]) for num_queries in speedups.keys(): if alloc == BASE_ALLOC and num_queries not in base_stats: base_stats[num_queries] = { MEAN_KEY: 0, MEDIAN_KEY: 0, AMAX_KEY: 0, AMIN_KEY: 0, } else: if num_queries not in other_stats: other_stats[num_queries] = { alloc: { MEAN_KEY: 0, MEDIAN_KEY: 0, AMAX_KEY: 0, AMIN_KEY: 0, } } speedup_num_queries = np.array(speedups[num_queries]) mean = float(np.average(speedup_num_queries)) median = float(np.median(speedup_num_queries)) amax = float(np.amax(speedup_num_queries)) amin = float(np.amin(speedup_num_queries)) save_stats = { MEAN_KEY: mean, MEDIAN_KEY: median, AMAX_KEY: amax, AMIN_KEY: amin, } if alloc == BASE_ALLOC: base_stats[num_queries] = save_stats else: other_stats[num_queries][alloc] = save_stats # Calculate the relativity relative_stats = {} for num_queries in other_stats.keys(): allocators = other_stats[num_queries] if num_queries not in relative_stats: relative_stats[num_queries] = {} for allocator in allocators: other_val = other_stats[num_queries][allocator][MEAN_KEY] base_val = base_stats[num_queries][MEAN_KEY] relative_stats[num_queries][allocator] = ((base_val - other_val)/float(other_val))*100 # General style common_style(plt) # speedup_max += KBYTES * KBYTES * 4 # add "margin" above tallest bar # Setup matplotlib axis fig = plt.figure(figsize=(8, 5)) renderer = fig.canvas.get_renderer() # image size axis ax1 = fig.add_subplot(1,1,1) ax1.set_ylabel("Relative Execution Speedup") # ax1.set_xlabel("Number of SQL INSERT Queries") ax1.grid(which='major', axis='y', linestyle=':', alpha=0.5, zorder=0) ax1_yticks = np.arange(-100, 50, step=20) print(ax1_yticks) ax1.set_yticks(ax1_yticks, minor=False) ax1.set_yticklabels([str(ytick) for ytick in ax1_yticks]) # ax1_yticks = np.arange(0, speedup_max, step=1000000000) # ax1.set_yticks(ax1_yticks, minor=False) # ax1.set_yticklabels(ax1_yticks) ax1.set_ylim(-100, 50) # Plot coordinates scale = 1. / len(relative_stats.keys()) xlabels = [] # Adjust margining # fig.subplots_adjust(bottom=.15) #, top=1) # x = range(7) # negative_data = [-1,-4,-3,-2,-6,-2,-8] # positive_data = [4,2,3,1,4,6,7,] # fig = plt.figure() # ax = plt.subplot(111) # ax.bar(x, negative_data, width=1, color='r') # ax.bar(x, positive_data, width=1, color='b') i = 0 line_offset = 0 for numqueries in relative_stats.keys(): # Plot a line beteween numqueries if i > 0: line = plt.Line2D([i * scale, i * scale], [-.02, 1], transform=ax1.transAxes, color='black', linewidth=1) line.set_clip_on(False) ax1.add_line(line) xlabels.append(numqueries) allocators = relative_stats[numqueries] # Plot a line beteween unikernel applications # if i > 0: # line = plt.Line2D([i * scale, i * scale], [-.02, 1], # transform=ax1.transAxes, color='black', # linewidth=1) # line.set_clip_on(False) # ax1.add_line(line) j = 0 bar_width = GROUP_BAR_WIDTH / len(allocators.keys()) bar_offset = (bar_width / 2) - (GROUP_BAR_WIDTH / 2) # Plot each allocator for allocator_label in sorted(allocators): # app = stats[unikernel] relativity = relative_stats[numqueries][allocator_label] print(numqueries, allocator_label, relativity) bar = ax1.bar([i + 1 + bar_offset], relativity, label=labels[allocator_label], align='center', zorder=3, width=bar_width, color=bar_colors[allocator_label], linewidth=.5 ) ax1.text(i + 1 + bar_offset, relativity, ("%3.1f" % relativity), ha='center', va= 'bottom' if relativity > 0 else 'top', fontsize=LARGE_SIZE, linespacing=0, bbox=dict(pad=-.6, facecolor='white', linewidth=0), rotation='vertical' ) bar_offset += bar_width j += 1 i += 1 # set up x-axis labels xticks = range(1, len(xlabels) + 1) ax1.set_xticks(xticks) # ax1.set_xticklabels(xlabels, fontsize=LARGE_SIZE, rotation=45, ha='right', rotation_mode='anchor') ax1.set_xticklabels(xlabels, fontsize=LARGE_SIZE, fontweight='bold') ax1.set_xlim(.5, len(xlabels) + .5) ax1.yaxis.grid(True, zorder=0, linestyle=':') ax1.tick_params(axis='both', which='both', length=0) # Create a unique legend handles, labels = plt.gca().get_legend_handles_labels() by_label = dict(zip(labels, handles)) leg = plt.legend(by_label.values(), by_label.keys(), loc='lower left', ncol=1, fontsize=LARGE_SIZE, ) leg.get_frame().set_linewidth(0.0) plt.setp(ax1.lines, linewidth=.5) # Save to file fig.tight_layout() fig.savefig(output) #, bbox_extra_artists=(ax1,), bbox_inches='tight')
def plot(data=None, output=None): WORKDIR = os.getcwd() RESULTSDIR = data RESULTEXT = '.csv' IMAGESTAT = 'imagestats' IMAGE_SIZE_KEY = 'image_size' NUMSYMS_KEY = 'number_symbols' GROUP_BAR_WIDTH = .8 DEFAULT = '_' files = [] apps = [] stats = {} throughput_max = 0 # maximum observed rx mpps bar_colors = { 'linux-dpdk-vhost-user': '******', 'linux-dpdk-vhost-net': '#000000', 'unikraft-vhost-user': '******', 'unikraft-vhost-net': '#8000CA' } markers = { 'linux-dpdk-vhost-user': '******', 'linux-dpdk-vhost-net': ',', 'unikraft-vhost-user': '******', 'unikraft-vhost-net': '4' } labels = { 'linux-dpdk-vhost-user': '******', 'linux-dpdk-vhost-net': 'Linux DPDK with vhost-net', 'unikraft-vhost-user': '******', 'unikraft-vhost-net': 'Rhea with vhost-net' } for f in os.listdir(RESULTSDIR): if f.endswith(RESULTEXT): index = f.replace(RESULTEXT,'') files.append(f) unikernel = index with open(os.path.join(RESULTSDIR, f), 'r') as csvfile: csvdata = csv.reader(csvfile, delimiter="\t") next(csvdata) # skip header for row in csvdata: if unikernel not in stats: stats[unikernel] = {} throughput = float(row[1]) * KBYTES * KBYTES stats[unikernel][str(row[0])] = throughput if throughput > throughput_max: throughput_max = throughput # General style common_style(plt) throughput_max += KBYTES * KBYTES * 1 # add "margin" above tallest bar # Setup matplotlib axis fig = plt.figure(figsize=(8, 4)) renderer = fig.canvas.get_renderer() # image size axis ax1 = fig.add_subplot(1,1,1) ax1.set_ylabel("Throughout (Mp/s)") ax1.set_xlabel("Packet Size (Bytes)") ax1.grid(which='major', axis='y', linestyle=':', alpha=0.5, zorder=0) ax1_yticks = np.arange(0, throughput_max, step=KBYTES * KBYTES * 2) ax1.set_yticks(ax1_yticks, minor=False) ax1.set_yticklabels([sizeof_fmt(ytick, suffix='') for ytick in ax1_yticks]) ax1.set_ylim(0, throughput_max) # Plot coordinates xlabels = list(stats[list(stats.keys())[0]].keys()) # Adjust margining fig.subplots_adjust(bottom=.15) #, top=1) for unikernel in stats.keys(): ax1.plot(list(stats[unikernel].keys()), list(stats[unikernel].values()), marker=markers[unikernel], label=labels[unikernel], zorder=3, linewidth=3, markersize=9, markeredgewidth=4, color=bar_colors[unikernel], ) # set up x-axis labels xticks = range(0, len(xlabels)) ax1.set_xticks(xticks) ax1.margins(x=.05) # Create a unique legend handles, labels = plt.gca().get_legend_handles_labels() by_label = dict(zip(labels, handles)) leg = plt.legend(by_label.values(), by_label.keys(), fontsize=LARGE_SIZE, loc='upper right', ncol=1) leg.get_frame().set_linewidth(0.0) # Save to file fig.tight_layout() fig.savefig(output) #, bbox_extra_artists=(ax1,), bbox_inches='tight')
def plot(data=None, output=None): WORKDIR = os.getcwd() RESULTSDIR = data RESULTEXT = '.csv' MINUTES = 60 MEAN_KEY = 'mean' MEDIAN_KEY = 'median' AMAX_KEY = 'amax' AMIN_KEY = 'amin' BAR_WIDTH = 0.6 files = [] labels = [] apps = [] boottimes = {} boottime_max = 0 # maximum observed build time stack_max = 1 # number of bars to be stacked total_vmms = 0 component_colors = { "vmm": '#9774a7', # dark purple "guest": '#fff3cd', # yellow } component_labels = { 'vmm': 'VMM', "guest": 'Unikraft Guest', } colors = [ # 'black', # 'dimgray', # 'lightcoral', # 'orangered', # 'sandybrown', # 'darkorange', # 'gold', # 'darkkhaki', # 'yellowgreen', # 'seagreen', # 'turquoise', # 'teal', # 'deepskyblue', # 'royalblue', # 'mediumpurple', # 'orchid', # 'lightskyblue', '#91c6e7', # blue '#d18282', # red '#ddcae3', # lavender '#a2d9d1', # thyme '#ededed', # gray '#fff3cd', # yellow '#91c6e7', # light blue '#618c84', # dark green '#49687c', # dark blue '#7c4f4f', # dark yellow ] text_xlabels = { 'qemu': 'QEMU', 'qemu1nic': "QEMU (1NIC)", 'qemumicrovm': 'QEMU\n(MicroVM)', 'solo5': 'Solo5', 'firecracker': 'Firecracker', } for f in os.listdir(RESULTSDIR): if f.endswith(RESULTEXT): unikernel = f.replace(RESULTEXT, '') files.append(f) if unikernel not in boottimes: total_vmms += 1 boottimes[unikernel] = { "guest": { MEAN_KEY: 0, MEDIAN_KEY: 0, AMAX_KEY: 0, AMIN_KEY: 0 } } with open(os.path.join(RESULTSDIR, f), 'r') as csvfile: csvdata = csv.reader(csvfile, delimiter="\t") next(csvdata) # skip header vmm_times = [] guest_times = [] for row in csvdata: vmm_times.append(float(row[0]) / 1000.0) guest_times.append(float(row[1]) / 1000.0) if len(vmm_times) == 0 or len(vmm_times) != len(guest_times): print("Could not parse empty data set: %s" % f) continue guest_times = np.array(guest_times) vmm_times = np.array(vmm_times) mean_vmm = float(np.average(vmm_times)) median_vmm = float(np.median(vmm_times)) amax_vmm = float(np.amax(vmm_times)) amin_vmm = float(np.amin(vmm_times)) mean_guest = float(np.average(guest_times)) median_guest = float(np.median(guest_times)) amax_guest = float(np.amax(guest_times)) amin_guest = float(np.amin(guest_times)) if amax_guest + amax_vmm > boottime_max: boottime_max = amax_guest + amax_vmm boottimes[unikernel]["guest"] = { MEAN_KEY: mean_guest, MEDIAN_KEY: median_guest, AMAX_KEY: amax_guest, AMIN_KEY: amin_guest, } boottimes[unikernel]["vmm"] = { MEAN_KEY: mean_vmm, MEDIAN_KEY: median_vmm, AMAX_KEY: amax_vmm, AMIN_KEY: amin_vmm, } if len(boottimes[unikernel]) > stack_max: stack_max = len(boottimes[unikernel]) # General style common_style(plt) boottime_max += 700 # margin above biggest bar # Setup matplotlib fig = plt.figure(figsize=(8, 5)) ax = fig.add_subplot(1, 1, 1) ax.grid(which='major', axis='y', linestyle=':', alpha=0.5) # This plot: # ax.set_title('Unikernel Build Times', pad=35) ax.set_ylabel("Total Boot Time (ms)") # ax.set_xlabel('Applications', labelpad=10) # Add padding above tallest bar plt.ylim(0, boottime_max) renderer = fig.canvas.get_renderer() ax.set_yscale('symlog') # ax.set_yticks(np.arange(0, (boottime_max / MINUTES) + 10, step=2), minor=False) # Adjust margining fig.subplots_adjust(bottom=.1) #, top=1) # Plot coordinates yticks = 0 scale = 1. / len(text_xlabels) xlabels = [] # Create a blank matrix where we'll align bar sizes for matplotlib means = np.zeros((stack_max, total_vmms), dict) labels = np.zeros((stack_max, total_vmms), dict) i = 0 for vmm in text_xlabels.keys(): # Write unikernel project on top as "header" lxpos = (i + .5 * len(boottimes[vmm].keys())) * scale xlabels.append(text_xlabels[vmm]) # ax.text(lxpos, 1.04, r'\textbf{%s}' % unikernel, ha='center', transform=ax.transAxes, fontweight='bold') # # Plot a line beteween unikernel applications # if i > 0: # line = plt.Line2D([i * scale, i * scale], [0, 1.02], # transform=ax.transAxes, color='black', # linewidth=1) # line.set_clip_on(False) # ax.add_line(line) components = list(boottimes[vmm].items()) total_time = 0. # Plot each vmm's as a multi-bar j = 0 for component_label in sorted(boottimes[vmm]): component = boottimes[vmm][component_label] means[j][i] = (component[MEAN_KEY]) total_time += component[MEAN_KEY] bottom_offset = 0 # Increase y-axis distance for the component's bar for k in range(j, 0, -1): bottom_offset += means[k - 1][i] # Save the component label labels[j][i] = (component_label) # Plot the bar at the correct matrix location bar = ax.bar([i + 1], component[MEAN_KEY], bottom=bottom_offset, label=component_labels[component_label], align='center', zorder=3, width=BAR_WIDTH, color=component_colors[component_label], linewidth=.5) # Write total time label if last bar if j == len(components) - 1: bottom_offset += component[MEAN_KEY] # + .28 # + spacing print_total_time = "%-.01fms" % (total_time) #if total_time < 1: # print_total_time = "%-.0fms" % (total_time * 1000) #elif total_time < MINUTES: # print_total_time = "%-.2fs" % (total_time) #elif total_time > MINUTES * MINUTES: # print_total_time = strftime("%-Hh %-Mm", gmtime(total_time)) #else: # print_total_time = strftime("%-Mm %-Ss", gmtime(total_time)) plt.text(i + 1, bottom_offset * 1.2, print_total_time, ha='center', va='bottom', fontsize=LARGE_SIZE, linespacing=0, bbox=dict(pad=-.6, facecolor='white', linewidth=0), rotation='vertical') # add a time label for the application # if len(components) > 1 and component_label == DEFAULT_COMPONENET_KEY: # component_seconds = component[MEAN_KEY] # if component_seconds < 1: # print_total_time = "%-.0fms" % (component_seconds * 1000) # elif component_seconds < MINUTES: # print_total_time = "%-.2fs" % (component_seconds) # else: # print_total_time = strftime("%-Mm%-Ss", gmtime(component_seconds)) # print(component_seconds, print_total_time) # # Account for very tiny applciation builds and position above axis bar # yplot = bottom_offset + component[MEAN_KEY] # plt.text(i + 1, yplot, r'\textbf{%s}' % print_total_time, # ha='center', # va='top' if round(yplot) >= 1 else 'bottom', # fontsize=LARGE_SIZE, # fontweight='bold', # color='white', # zorder=6, # bbox=dict(pad=2, facecolor='none', linewidth=0), # rotation='vertical' # ) j += 1 i += 1 xticks = range(1, total_vmms + 1) ax.set_xticks(xticks) # ax.set_xticklabels(xlabels, fontsize=LARGE_SIZE) ax.set_xticklabels(xlabels, fontsize=LARGE_SIZE, rotation=40, ha='right', rotation_mode='anchor') ax.set_xlim(.5, total_vmms + .5) ax.yaxis.grid(True, zorder=0, linestyle=':') plt.setp(ax.lines, linewidth=.5) # Resize plot for bottom legend chartBox = ax.get_position() # ax.set_position([chartBox.x0, chartBox.y0 + chartBox.height*0.18, chartBox.width, chartBox.height*0.82]) # Create a unique legend handles, labels = plt.gca().get_legend_handles_labels() by_label = dict(zip(labels[::-1], handles[::-1])) leg = plt.legend(by_label.values(), by_label.keys(), loc='upper right', ncol=1, fontsize=LARGE_SIZE) leg.get_frame().set_linewidth(0.0) # Save to file fig.tight_layout() fig.savefig(output)
def plot(data=None, output=None): RESULTSDIR = data RESULTEXT = '.csv' MINUTES = 60 DEFAULT_COMPONENET_KEY = '_' MEAN_KEY = 'mean' MEDIAN_KEY = 'median' AMAX_KEY = 'amax' AMIN_KEY = 'amin' BAR_WIDTH = 0.6 files = [] labels = [] apps = [] boottimes = {} boottime_max = 0 # maximum observed build time stack_max = 1 # number of bars to be stacked total_allocators = 0 component_colors = {} text_xlabels = { 'buddy': 'Binary buddy', 'mimalloc': 'Mimalloc', 'region': 'Bootalloc', 'tinyalloc': 'tinyalloc', 'tlsf': 'TLSF' } colors = [ # 'black', # 'dimgray', # 'lightcoral', # 'orangered', # 'sandybrown', # 'darkorange', # 'gold', # 'darkkhaki', # 'yellowgreen', # 'seagreen', # 'turquoise', # 'teal', # 'deepskyblue', # 'royalblue', # 'mediumpurple', # 'orchid', # 'lightskyblue', '#91c6e7', # blue '#d18282', # red '#ddcae3', # lavender '#a2d9d1', # thyme '#ededed', # gray '#fff3cd', # yellow '#91c6e7', # light blue '#618c84', # dark green '#49687c', # dark blue '#7c4f4f', # dark yellow ] for f in os.listdir(RESULTSDIR): if f.endswith(RESULTEXT): allocator = f.replace(RESULTEXT, '') files.append(f) # component = None # if '+' in unikernel: # unikernel = unikernel.split('+') # component = unikernel[1] # unikernel = unikernel[0] if allocator not in boottimes: total_allocators += 1 boottimes[allocator] = {} with open(os.path.join(RESULTSDIR, f), 'r') as csvfile: csvdata = csv.reader(csvfile, delimiter="\t") next(csvdata) # skip header execution_times = {} for row in csvdata: if row[0] not in execution_times: execution_times[row[0]] = [] execution_times[row[0]].append(float(row[1]) / 1000.0) for component in execution_times.keys(): # Create a unique pair for the component for an associated colour if component not in component_colors.keys(): component_colors[component] = colors[1] colors.pop(1) componenet_exec_times = np.array( execution_times[component]) mean = float(np.average(componenet_exec_times)) median = float(np.median(componenet_exec_times)) amax = float(np.amax(componenet_exec_times)) amin = float(np.amin(componenet_exec_times)) if amax > boottime_max: boottime_max = amax boottimes[allocator][component] = { MEAN_KEY: mean, MEDIAN_KEY: median, AMAX_KEY: amax, AMIN_KEY: amin, } if len(boottimes[allocator]) > stack_max: stack_max = len(boottimes[allocator]) # General style common_style(plt) boottime_max += 1.5 # margin above biggest bar # Setup matplotlib fig = plt.figure(figsize=(8, 5)) ax = fig.add_subplot(1, 1, 1) ax.grid(which='major', axis='y', linestyle=':', alpha=0.5) # This plot: # ax.set_title('Unikernel Build Times', pad=35) ax.set_ylabel("Total Boot Time (ms)") # ax.set_xlabel('Applications', labelpad=10) # Add padding above tallest bar plt.ylim(0, boottime_max) renderer = fig.canvas.get_renderer() ax_yticks = np.arange(0, boottime_max, step=0.5) ax.set_yticklabels([str(ytick) for ytick in ax_yticks]) ax.set_yticks(ax_yticks, minor=False) # ax.set_yscale('symlog') # ax.set_yticks(np.arange(0, (boottime_max / MINUTES) + 10, step=2), minor=False) # Adjust margining # fig.subplots_adjust(bottom=.1) #, top=1) # Plot coordinates yticks = 0 scale = 1. / len(text_xlabels) xlabels = [] # Create a blank matrix where we'll align bar sizes for matplotlib means = np.zeros((stack_max, total_allocators), dict) labels = np.zeros((stack_max, total_allocators), dict) i = 0 for allocator in text_xlabels.keys(): # Write unikernel project on top as "header" lxpos = (i + .5 * len(boottimes[allocator].keys())) * scale xlabels.append(text_xlabels[allocator]) # ax.text(lxpos, 1.04, r'\textbf{%s}' % unikernel, ha='center', transform=ax.transAxes, fontweight='bold') # # Plot a line beteween unikernel applications # if i > 0: # line = plt.Line2D([i * scale, i * scale], [0, 1.02], # transform=ax.transAxes, color='black', # linewidth=1) # line.set_clip_on(False) # ax.add_line(line) components = list(boottimes[allocator].items()) total_time = 0. # Plot each allocator's as a multi-bar j = 0 for component_label in sorted(boottimes[allocator]): component = boottimes[allocator][component_label] means[j][i] = (component[MEAN_KEY]) total_time += component[MEAN_KEY] bottom_offset = 0 # Increase y-axis distance for the component's bar for k in range(j, 0, -1): bottom_offset += means[k - 1][i] # Save the component label if component_label == DEFAULT_COMPONENET_KEY: component_label = DEFAULT_COMPONENET_KEY if component_label == DEFAULT_COMPONENET_KEY: labels[j][i] = (DEFAULT_COMPONENET_KEY) else: labels[j][i] = (component_label) # Plot the bar at the correct matrix location bar = ax.bar([i + 1], component[MEAN_KEY], bottom=bottom_offset, label=component_label, align='center', zorder=12, width=BAR_WIDTH, color=component_colors[component_label], linewidth=.5) # Write total time label if last bar if j == len(components) - 1: bottom_offset += component[MEAN_KEY] # + .28 # + spacing print_total_time = round(total_time, 2) # if total_time < 1: # elif total_time < MINUTES: # print_total_time = "%-.2fs" % (total_time) # elif total_time > MINUTES * MINUTES: # print_total_time = strftime("%-Hh %-Mm", gmtime(total_time)) # else: # print_total_time = strftime("%-Mm %-Ss", gmtime(total_time)) plt.text(i + 1, bottom_offset + 0.1, print_total_time, ha='center', va='bottom', fontsize=LARGE_SIZE, linespacing=0, bbox=dict(pad=-.6, facecolor='white', linewidth=0), rotation='vertical') # add a time label for the application # if len(components) > 1 and component_label == DEFAULT_COMPONENET_KEY: # component_seconds = component[MEAN_KEY] # if component_seconds < 1: # print_total_time = "%-.0fms" % (component_seconds * 1000) # elif component_seconds < MINUTES: # print_total_time = "%-.2fs" % (component_seconds) # else: # print_total_time = strftime("%-Mm%-Ss", gmtime(component_seconds)) # print(component_seconds, print_total_time) # # Account for very tiny applciation builds and position above axis bar # yplot = bottom_offset + component[MEAN_KEY] # plt.text(i + 1, yplot, r'\textbf{%s}' % print_total_time, # ha='center', # va='top' if round(yplot) >= 1 else 'bottom', # fontsize=LARGE_SIZE, # fontweight='bold', # color='white', # zorder=6, # bbox=dict(pad=2, facecolor='none', linewidth=0), # rotation='vertical' # ) j += 1 i += 1 xticks = range(1, total_allocators + 1) ax.set_xticks(xticks) ax.set_xticklabels(xlabels, fontsize=LARGE_SIZE) # ax.set_xticklabels(xlabels, fontsize=LARGE_SIZE, rotation=45, ha='right', rotation_mode='anchor') ax.set_xlim(.5, total_allocators + .5) ax.yaxis.grid(True, zorder=0, linestyle=':') plt.setp(ax.lines, linewidth=.5) # Resize plot for bottom legend chartBox = ax.get_position() # ax.set_position([chartBox.x0, chartBox.y0 + chartBox.height*0.18, chartBox.width, chartBox.height*0.82]) # Create a unique legend handles, labels = plt.gca().get_legend_handles_labels() # make sure it is in the same order as the layers by_label = dict(zip(labels[::-1], handles[::-1])) leg = plt.legend(by_label.values(), by_label.keys(), loc='upper right', ncol=3, fontsize=LARGE_SIZE, columnspacing=.8) leg.get_frame().set_linewidth(0.0) # Save to file fig.tight_layout() fig.savefig(output)
def plot(data=None, output=None): WORKDIR = os.getcwd() RESULTSDIR = data RESULTEXT = '.csv' GROUP_BAR_WIDTH = .8 DEFAULT = '_' THROUGHPUT = 'throughput' MEAN_KEY = 'mean' MEDIAN_KEY = 'median' AMAX_KEY = 'amax' AMIN_KEY = 'amin' files = [] labels = [] apps = [] stats = {} throughput_max = 0 # maximum observed throughput total_apps = 0 bar_colors = { 'GET': '#FFF6F9', 'SET': '#5697C4', } labels = { 'unikraft-qemu': 'Unikraft KVM', 'docker': 'Docker Native', 'hermitux-uhyve': 'Hermitux uHyve', 'osv-qemu': 'OSv KVM', 'rump-qemu': 'Rump KVM', 'microvm-qemu': 'Linux KVM', 'microvm-fc': 'Linux FC', 'native-redis': 'Linux Native', 'lupine-fc': 'Lupine FC', 'lupine-qemu': 'Lupine KVM' } for f in os.listdir(RESULTSDIR): if f.endswith(RESULTEXT): unikernel = f.replace(RESULTEXT,'') if unikernel not in stats: stats[unikernel] = {} with open(os.path.join(RESULTSDIR, f), 'r') as csvfile: csvdata = csv.reader(csvfile, delimiter="\t") next(csvdata) # skip header operations = {} for row in csvdata: if row[0] not in operations: operations[row[0]] = [] operations[row[0]].append(float(row[1])/1000.0) for operation in operations: all_ops = np.array(operations[operation]) operations[operation] = { MEAN_KEY: np.average(all_ops), MEDIAN_KEY: np.median(all_ops), AMAX_KEY: np.amax(all_ops), AMIN_KEY: np.amin(all_ops) } if int(round((np.amax(all_ops)))) > throughput_max: throughput_max = int(round((np.amax(all_ops)))) stats[unikernel] = operations # General style common_style(plt) throughput_max += 0.5 # margin above biggest bar # Setup matplotlib axis fig = plt.figure(figsize=(8, 5)) renderer = fig.canvas.get_renderer() # image size axis ax1 = fig.add_subplot(1,1,1) ax1.set_ylabel("Aver. Throughput (Million req/s)") ax1.grid(which='major', axis='y', linestyle=':', alpha=0.5, zorder=0) ax1_yticks = np.arange(0, throughput_max, step=0.5) ax1.set_yticks(ax1_yticks, minor=False) ax1.set_yticklabels(ax1_yticks) ax1.set_ylim(0, throughput_max) # Plot coordinates scale = 1 / (len(stats.keys())) xlabels = [] # Adjust margining # fig.subplots_adjust(bottom=.15) #, top=1) i = 0 line_offset = 0 for unikernel in [ 'hermitux-uhyve', 'microvm-fc', 'lupine-fc', 'rump-qemu', 'microvm-qemu', 'lupine-qemu', 'docker', 'osv-qemu', 'native-redis', 'unikraft-qemu']: xlabels.append(labels[unikernel]) operations = stats[unikernel] # Plot a line beteween unikernel applications if i > 0: line = plt.Line2D([i * scale, i * scale], [-.02, 1], transform=ax1.transAxes, color='black', linewidth=1) line.set_clip_on(False) ax1.add_line(line) j = 0 bar_width = GROUP_BAR_WIDTH / len(operations.keys()) bar_offset = (bar_width / 2) - (GROUP_BAR_WIDTH / 2) # Plot each application for operation_label in sorted(operations): bar = ax1.bar([i + 1 + bar_offset], operations[operation_label][MEAN_KEY], label=operation_label, align='center', zorder=4, yerr=(operations[operation_label][AMAX_KEY] - operations[operation_label][AMIN_KEY]), error_kw=dict(lw=1, capsize=10, capthick=1), width=bar_width, color=bar_colors[operation_label], linewidth=.5 ) ax1.text(i + 1 + bar_offset, operations[operation_label][AMAX_KEY] + 0.2, round(operations[operation_label][MEAN_KEY], 2), ha='center', va='bottom', zorder=6, fontsize=LARGE_SIZE, linespacing=0, bbox=dict(pad=-.6, facecolor='white', linewidth=0), rotation='vertical' ) bar_offset += bar_width j += 1 i += 1 # sys.exit(1) # set up x-axis labels xticks = range(1, len(xlabels) + 1) ax1.set_xticks(xticks) ax1.set_xticklabels(xlabels, fontsize=LARGE_SIZE, rotation=40, ha='right', rotation_mode='anchor')#, # horizontalalignment='center') # ax1.set_xticklabels(xlabels, fontsize=LARGE_SIZE) ax1.set_xlim(.5, len(xlabels) + .5) ax1.yaxis.grid(True, zorder=0, linestyle=':') ax1.tick_params(axis='both', which='both', length=0) # Create a unique legend handles, labels = plt.gca().get_legend_handles_labels() by_label = dict(zip(labels, handles)) leg = plt.legend(by_label.values(), by_label.keys(), loc='upper left', ncol=2, fontsize=LARGE_SIZE, ) leg.get_frame().set_linewidth(0.0) plt.setp(ax1.lines, linewidth=.5) # Save to file fig.tight_layout() #plt.show() fig.savefig(output) #, bbox_extra_artists=(ax1,), bbox_inches='tight')
def plot(data=None, output=None): WORKDIR = os.getcwd() RESULTSDIR = data RESULTEXT = '.csv' IMAGESTAT = 'imagestats' IMAGE_SIZE_KEY = 'image_size' NUMSYMS_KEY = 'number_symbols' GROUP_BAR_WIDTH = .8 DEFAULT = '_' files = [] labels = [] apps = [] imagestats = {} imagesize_max = 0 # maximum observed image size number_symbols_max = 0 # maximum observed symbol count total_apps = 0 bar_colors = { 'nginx': '#0C8828', 'redis': '#CE1216', 'hello': 'dimgray', 'sqlite': '#4BA3E1' } labels = { 'hermitux': 'Hermitux', 'linuxuser': '******', 'lupine': 'Lupine', 'osv': 'OSv', 'rump': 'Rumprun', 'unikraft': 'Unikraft', 'mirage': 'Mirage' } # Prepare maxplotlib data by parsing the individual .csv files. This process # goes through all image sizes and number of symbols and populates a dictionary # of unikernels and the application "image stats" based on the framework. for f in os.listdir(RESULTSDIR): if f.endswith(RESULTEXT): index = f.replace(RESULTEXT,'') files.append(f) result = index.split('-') unikernel = result[0] app = result[1] if unikernel not in imagestats: imagestats[unikernel] = {} if app not in imagestats[unikernel]: total_apps += 1 imagestats[unikernel][app] = 0 if app not in apps: apps.append(app) with open(os.path.join(RESULTSDIR, f), 'r') as csvfile: size= int(csvfile.readline()) imagestats[unikernel][app] = size # General style common_style(plt) imagesize_max += KBYTES * KBYTES * 12 # add MB "margin" number_symbols_max += 2000 # Setup matplotlib axis fig = plt.figure(figsize=(8, 5)) renderer = fig.canvas.get_renderer() # image size axis ax1 = fig.add_subplot(1,1,1) ax1.set_ylabel("Image size") ax1.grid(which='major', axis='y', linestyle=':', alpha=0.5, zorder=0) ax1_yticks = np.arange(0, imagesize_max, step=KBYTES*KBYTES*2) ax1.set_yticks(ax1_yticks, minor=False) ax1.set_yticklabels([sizeof_fmt(ytick) for ytick in ax1_yticks]) ax1.set_ylim(0, imagesize_max) # Plot coordinates scale = 1. / len(labels.keys()) xlabels = [] # Adjust margining fig.subplots_adjust(bottom=.15) #, top=1) i = 0 line_offset = 0 for unikernel in [ 'unikraft', 'hermitux', 'linuxuser', 'lupine', 'mirage', 'osv', 'rump' ]: xlabels.append(labels[unikernel]) apps = imagestats[unikernel] # Plot a line beteween unikernel applications if i > 0: line = plt.Line2D([i * scale, i * scale], [-.02, 1], transform=ax1.transAxes, color='black', linewidth=1) line.set_clip_on(False) ax1.add_line(line) j = 0 bar_width = GROUP_BAR_WIDTH / len(apps.keys()) bar_offset = (bar_width / 2) - (GROUP_BAR_WIDTH / 2) # Plot each application for app_label in sorted(apps): app = imagestats[unikernel][app_label] print(unikernel, app_label, app) bar = ax1.bar([i + 1 + bar_offset], app, label=app_label, align='center', zorder=3, width=bar_width, color=bar_colors[app_label], linewidth=.5 ) ax1.text(i + 1 + bar_offset, app + 500000, sizeof_fmt(app), ha='center', va='bottom', fontsize=LARGE_SIZE, linespacing=0, zorder=2, bbox=dict(pad=0, facecolor='white', linewidth=0), rotation='vertical' ) bar_offset += bar_width j += 1 i += 1 # sys.exit(1) # set up x-axis labels xticks = range(1, len(xlabels) + 1) ax1.set_xticks(xticks) ax1.set_xticklabels(xlabels, fontsize=LARGE_SIZE, rotation=40, ha='right', rotation_mode='anchor') # ax1.set_xticklabels(xlabels, fontsize=LARGE_SIZE, fontweight='bold') ax1.set_xlim(.5, len(xlabels) + .5) ax1.yaxis.grid(True, zorder=0, linestyle=':') ax1.tick_params(axis='both', which='both', length=0) # Create a unique legend handles, labels = plt.gca().get_legend_handles_labels() by_label = dict(zip(labels, handles)) leg = plt.legend(by_label.values(), by_label.keys(), loc='upper left', ncol=2, fontsize=LARGE_SIZE, ) leg.get_frame().set_linewidth(0.0) plt.setp(ax1.lines, linewidth=.5) # Save to file fig.tight_layout() fig.savefig(output) #, bbox_extra_artists=(ax1,), bbox_inches='tight')
def plot(data=None, output=None): RESULTSDIR = data RESULTEXT = '.csv' GROUP_BAR_WIDTH = .4 DEFAULT = '_' THROUGHPUT = 'throughput' MEAN_KEY = 'mean' MEDIAN_KEY = 'median' AMAX_KEY = 'amax' AMIN_KEY = 'amin' files = [] labels = [] apps = [] stats = {} throughput_max = 0 # maximum observed throughput total_apps = 0 bar_color = '#5697C4' labels = { 'unikraft-qemu': 'Unikraft KVM', 'docker': 'Docker Native', 'hermitux-uhyve': 'Hermitux uHyve', 'osv-qemu': 'OSv KVM', 'rump-qemu': 'Rump KVM', 'microvm-qemu': 'Linux KVM', 'lupine-qemu': 'Lupine KVM', 'lupine-fc': 'Lupine FC', 'native': 'Linux Native', 'microvm-fc': 'Linux FC', 'mirage-solo5': 'Mirage Solo5' } for f in os.listdir(RESULTSDIR): if f.endswith(RESULTEXT): unikernel = f.replace(RESULTEXT, '') if unikernel not in stats: stats[unikernel] = { MEAN_KEY: 0, MEDIAN_KEY: 0, AMAX_KEY: 0, AMIN_KEY: 0 } with open(os.path.join(RESULTSDIR, f), 'r') as csvfile: csvdata = csv.reader(csvfile, delimiter="\t") next(csvdata) # skip header throughput = [] for row in csvdata: throughput.append(float(row[0]) / 1000) throughput = np.array(throughput) throughput = { MEAN_KEY: np.average(throughput), MEDIAN_KEY: np.median(throughput), AMAX_KEY: np.amax(throughput), AMIN_KEY: np.amin(throughput) } if throughput[AMAX_KEY] > throughput_max: throughput_max = throughput[AMAX_KEY] stats[unikernel] = throughput # General style common_style(plt) throughput_max += 100 # margin above biggest bar # Setup matplotlib axis fig = plt.figure(figsize=(8, 5)) renderer = fig.canvas.get_renderer() # image size axis ax1 = fig.add_subplot(1, 1, 1) ax1.set_ylabel("Average Throughput (x1000 req/s)") ax1.grid(which='major', axis='y', linestyle=':', alpha=0.5, zorder=0) ax1_yticks = np.arange(0, throughput_max, step=50) ax1.set_yticks(ax1_yticks, minor=False) ax1.set_yticklabels(["%3.0f" % ytick for ytick in ax1_yticks]) ax1.set_ylim(0, throughput_max) # Plot coordinates scale = 1. / len(stats.keys()) xlabels = [] # Adjust margining # fig.subplots_adjust(bottom=.15) #, top=1) i = 0 line_offset = 0 for unikernel in [ 'mirage-solo5', 'microvm-fc', 'lupine-fc', 'microvm-qemu', 'rump-qemu', 'docker', 'native', 'lupine-qemu', 'osv-qemu', 'unikraft-qemu' ]: xlabels.append(labels[unikernel]) throughput = stats[unikernel] yerr = throughput[AMAX_KEY] - throughput[AMIN_KEY] print(unikernel, throughput[MEAN_KEY], '+/-', yerr) # Plot each application bar = ax1.bar([i + 1], throughput[MEAN_KEY], label=unikernel, align='center', zorder=4, yerr=yerr, width=GROUP_BAR_WIDTH, color=bar_color, linewidth=.5) ax1.text(i + 1, throughput[MEAN_KEY] + yerr + 15, "%3.1f" % throughput[MEAN_KEY], ha='center', va='bottom', zorder=6, fontsize=LARGE_SIZE, linespacing=0, bbox=dict(pad=-.6, facecolor='white', linewidth=0), rotation='vertical') i += 1 # sys.exit(1) # set up x-axis labels xticks = range(1, len(xlabels) + 1) ax1.set_xticks(xticks) ax1.set_xticklabels(xlabels, fontsize=LARGE_SIZE, rotation=45, ha='right', rotation_mode='anchor') # ax1.set_xticklabels(xlabels, fontsize=LARGE_SIZE, fontweight='bold') ax1.set_xlim(.5, len(xlabels) + .5) ax1.yaxis.grid(True, zorder=0, linestyle=':') ax1.tick_params(axis='both', which='both', length=0) # Create a unique legend # handles, labels = plt.gca().get_legend_handles_labels() # by_label = dict(zip(labels, handles)) # leg = plt.legend(by_label.values(), by_label.keys(), loc='upper left', ncol=4) # leg.get_frame().set_linewidth(0.0) plt.setp(ax1.lines, linewidth=.5) # Save to file fig.tight_layout() plt.show() fig.savefig(output) #, bbox_extra_artists=(ax1,), bbox_inches='tight')
def plot(data=None, output=None): OUTFILE = output RESULTEXT = '.csv' KBYTES = 1024.0 IMAGESTAT = 'imagestats' IMAGE_SIZE_KEY = 'image_size' NUMSYMS_KEY = 'number_symbols' GROUP_BAR_WIDTH = .8 DEFAULT = 'default' files = [] labels = [] apps = [] imagestats = {} imagesize_max = 0 # maximum observed image size number_symbols_max = 0 # maximum observed symbol count total_apps = 0 bar_colors = {} text_labels = { DEFAULT: 'Default configuration', 'dce': '+ Dead Code Elim. (DCE)', 'lto': '+ Link-Time Optim. (LTO)', 'dce+lto': '+ DCE + LTO', 'perf': '+ Performance Optimizations' } colors = sorted([ 'sandybrown', 'teal', 'deepskyblue', 'lightskyblue', 'orchid', ]) def sizeof_fmt(num, suffix='B'): for unit in ['', 'K', 'M', 'G']: if abs(num) < KBYTES: return "%3.1f%s%s" % (num, unit, suffix) num /= KBYTES return "%.1f%s%s" % (num, 'Yi', suffix) with open(data, 'r') as csvfile: csvdata = csv.reader(csvfile, delimiter="\t") for row in csvdata: name = row[0].split('_', 1) app = name[0] app_type = name[1].replace('_', '+') if app not in imagestats: imagestats[app] = {} total_apps += 1 if app_type not in bar_colors: bar_colors[app_type] = colors[1] colors.pop(1) imagestats[app][app_type] = int(row[1]) # General style common_style(plt) imagesize_max = KBYTES * KBYTES * 3.1 # add MB "margin" number_symbols_max += 2000 # Setup matplotlib axis fig = plt.figure(figsize=(8, 5)) renderer = fig.canvas.get_renderer() # image size axis ax1 = fig.add_subplot(1, 1, 1) ax1.set_ylabel("Image size") ax1.grid(which='major', axis='y', linestyle=':', alpha=0.5) ax1_yticks = np.arange(0, imagesize_max, step=KBYTES * KBYTES) ax1.set_yticks(ax1_yticks, minor=False) ax1.set_yticklabels([sizeof_fmt(ytick) for ytick in ax1_yticks]) ax1.set_ylim(0, imagesize_max) # Plot coordinates scale = 1. / total_apps xlabels = [] # Adjust margining fig.subplots_adjust(bottom=.15) #, top=1) i = 0 # Plot each application for app_label in sorted(imagestats): app = imagestats[app_label] xlabels.append(app_label) # Plot a line beteween unikernel applications if i > 0: line = plt.Line2D([i * scale, i * scale], [-.02, 1], transform=ax1.transAxes, color='black', linewidth=1) line.set_clip_on(False) ax1.add_line(line) j = 0 bar_width = GROUP_BAR_WIDTH / len(app.keys()) bar_offset = (bar_width / 2) - (GROUP_BAR_WIDTH / 2) for app_type in [DEFAULT, 'lto', 'dce', 'dce+lto']: bar = ax1.bar([i + 1 + bar_offset], app[app_type], label=text_labels[app_type], align='center', zorder=3, width=bar_width, color=bar_colors[app_type], linewidth=.5) ax1.text(i + 1 + bar_offset, app[app_type] + 50000, sizeof_fmt(app[app_type]), ha='center', va='bottom', fontsize=LARGE_SIZE, linespacing=0, bbox=dict(pad=-.6, facecolor='white', linewidth=0), rotation='vertical') bar_offset += bar_width j += 1 i += 1 # set up x-axis labels xticks = range(1, len(xlabels) + 1) ax1.set_xticks(xticks) ax1.set_xticklabels(xlabels, fontsize=LARGE_SIZE, rotation=40, ha='right', rotation_mode='anchor') # ax1.set_xticklabels(xlabels, fontsize=LARGE_SIZE, fontweight='bold') ax1.set_xlim(.5, len(xlabels) + .5) ax1.yaxis.grid(True, zorder=0, linestyle=':') ax1.tick_params(axis='both', which='both', length=0) plt.setp(ax1.lines, linewidth=.5) # Create a unique legend handles, labels = plt.gca().get_legend_handles_labels() by_label = dict(zip(labels, handles)) leg = plt.legend(by_label.values(), by_label.keys(), loc='upper left', ncol=2, fontsize=LARGE_SIZE * 0.8) leg.get_frame().set_linewidth(0.0) # Save to file fig.tight_layout() fig.savefig(OUTFILE) #, bbox_extra_artists=(ax1,), bbox_inches='tight')
stats = {} max_time = 0 for fn in glob.glob("*.dat"): data = np.loadtxt(fn) avg = np.average(data) std = np.std(data) stats[fn] = { 'min': avg - std, 'avg': avg, 'max': avg + std, } if stats[fn]['max'] > max_time: max_time = stats[fn]['max'] # General style common_style(plt) max_time *= 1.2 # Margin above biggest bar fig = plt.figure(figsize=(8, 4)) ax = fig.add_subplot(1, 1, 1) ax.set_ylabel("Time (seconds)", fontsize=LARGE_SIZE) ax.grid(which='major', axis='y', linestyle=':', alpha=0.5, zorder=0) yticks = np.arange(0, max_time, step=1) ax.set_yticks(yticks, minor=False) ax.set_yticklabels(["%3.0f" % ytick for ytick in yticks]) ax.set_ylim(0, max_time) xlabels = [] i = 0 for experiment in labels.keys():
def plot(data=None, output=None): WORKDIR = os.getcwd() RESULTSDIR = data RESULTEXT = '.csv' GROUP_BAR_WIDTH = .8 DEFAULT = '_' files = [] labels = [] apps = [] memstats = {} memusage_max = 0 # maximum observed memory size total_apps = 0 bar_colors = { 'nginx': '#0C8828', 'redis': '#CE1216', 'hello': 'dimgray', 'sqlite': '#4BA3E1' } labels = { 'unikraft': 'Unikraft', 'docker': 'Docker', 'hermitux': 'Hermitux', 'lupine': 'Lupine', 'osv': 'OSv', 'rump': 'Rumprun', 'microvm': 'Linux\nMicroVM' } for f in os.listdir(RESULTSDIR): if f.endswith(RESULTEXT): index = f.replace(RESULTEXT, '') unikernel = index if unikernel not in memstats: memstats[unikernel] = {} with open(os.path.join(RESULTSDIR, f), 'r') as csvfile: csvdata = csv.reader(csvfile, delimiter="\t") next(csvdata) # skip header for row in csvdata: app = row[0] memusagemb = int(row[1]) * KBYTES * KBYTES memstats[unikernel][app] = memusagemb if memusagemb > memusage_max: memusage_max = memusagemb # General style common_style(plt) memusage_max += KBYTES * KBYTES * 14 # add MB "margin" # Setup matplotlib axis fig = plt.figure(figsize=(8, 5)) renderer = fig.canvas.get_renderer() # image size axis ax1 = fig.add_subplot(1, 1, 1) ax1.set_ylabel("Minimum Memory Requirement") ax1.grid(which='major', axis='y', linestyle=':', alpha=0.5, zorder=0) ax1_yticks = np.arange(0, memusage_max, step=KBYTES * KBYTES * 8) ax1.set_yticks(ax1_yticks, minor=False) ax1.set_yticklabels([sizeof_fmt(ytick) for ytick in ax1_yticks]) ax1.set_ylim(0, memusage_max) # Plot coordinates scale = 1. / len(memstats.keys()) xlabels = [] # Adjust margining # fig.subplots_adjust(bottom=.) #, top=1) i = 0 line_offset = 0 for unikernel in [ 'unikraft', 'docker', 'rump', 'hermitux', 'lupine', 'osv', 'microvm' ]: xlabels.append(labels[unikernel]) apps = memstats[unikernel] # Plot a line beteween unikernel applications if i > 0: line = plt.Line2D([i * scale, i * scale], [-.02, 1], transform=ax1.transAxes, color='black', linewidth=1) line.set_clip_on(False) ax1.add_line(line) j = 0 bar_width = GROUP_BAR_WIDTH / len(apps.keys()) bar_offset = (bar_width / 2) - (GROUP_BAR_WIDTH / 2) # Plot each application for app_label in sorted(apps): app = memstats[unikernel][app_label] print(unikernel, app_label, app) bar = ax1.bar([i + 1 + bar_offset], app, label=app_label, align='center', zorder=3, width=bar_width, color=bar_colors[app_label], linewidth=.5) ax1.text( i + 1.02 + bar_offset, app + 1000000, sizeof_fmt(app), ha='center', va='bottom', fontsize=LARGE_SIZE, linespacing=0, #bbox=dict(pad=-.6, facecolor='white', linewidth=0), rotation='vertical') bar_offset += bar_width j += 1 i += 1 # sys.exit(1) # set up x-axis labels xticks = range(1, len(xlabels) + 1) ax1.set_xticks(xticks) ax1.set_xticklabels(xlabels, fontsize=LARGE_SIZE, rotation=40, ha='right', rotation_mode='anchor') # ax1.set_xticklabels(xlabels, fontsize=LARGE_SIZE, fontweight='bold') ax1.set_xlim(.5, len(xlabels) + .5) ax1.yaxis.grid(True, zorder=0, linestyle=':') ax1.tick_params(axis='both', which='both', length=0) # Create a unique legend handles, labels = plt.gca().get_legend_handles_labels() by_label = dict(zip(labels, handles)) leg = plt.legend( by_label.values(), by_label.keys(), loc='upper left', ncol=2, fontsize=LARGE_SIZE, ) leg.get_frame().set_linewidth(0.0) plt.setp(ax1.lines, linewidth=.5) # Save to file fig.tight_layout() fig.savefig(output) #, bbox_extra_artists=(ax1,), bbox_inches='tight')