#! /usr/bin/env python3 from matplotlib import pyplot import functools import os import subprocess import sys import tempfile import time TARGET_INTERFACES = 50 def gen(num_interfaces, outline_void_methods, total_void_methods, outline_bool_methods, total_bool_methods, inline_destructor, virtual_dtor): if outline_void_methods > total_void_methods: outline_void_methods = total_void_methods if outline_bool_methods > total_bool_methods: outline_bool_methods = total_bool_methods header = '\n\n'.join('''class Interface{n} {{ public: {virtual} ~Interface{n}() {dtor_body} {outline_void} {inline_void} {outline_bool} {inline_bool} }}; '''.format(n=inum, dtor_body='{}' if inline_destructor else ';', virtual='virtual' if virtual_dtor else '', outline_void='\n'.join(' virtual void VoidMethod{}();'.format( n) for n in range(0, outline_void_methods)), inline_void='\n'.join(' virtual void VoidMethod{}() {{}}'.format( n) for n in range(outline_void_methods, total_void_methods)), outline_bool='\n'.join(' virtual bool BoolMethod{}();'.format( n) for n in range(0, outline_bool_methods)), inline_bool='\n'.join(' virtual bool BoolMethod{}() {{ return false; }}'.format(n) for n in range(outline_bool_methods, total_bool_methods))) for inum in range(num_interfaces)) with open('interface.h', 'w') as header_file: header_file.write(header) use = ('''#include "interface.h" {uses} '''.format(uses='\n\n'.join('''void use{n}(Interface{n}* iface) {{ delete iface; }}'''.format(n=n) for n in range(num_interfaces)))) with open('use.cc', 'w') as use_file: use_file.write(use) class Measurement: def __init__(self, time, size): self.time = time self.size = size CLANG_O2 = ('clang++', '-fno-exceptions', '-O2', '-c', 'use.cc', '-o', 'use.o') CL_O2 = ('cl', '/O2', '/c', 'use.cc', '/Fouse.o') CL_Od = ('cl', '/Od', '/Zi', '/c', 'use.cc', '/Fouse.o') CLANG_CL_O2 = ('clang-cl', '/O2', '/c', 'use.cc', '/Fouse.o') @functools.lru_cache(maxsize=None) def measure(repetitions=1, compile_line=CL_O2, **kwargs): # return Measurement(time=.5, size=6000) gen(**kwargs) times = [] for i in range(repetitions): start = time.perf_counter() subprocess.check_call(compile_line) elapsed = time.perf_counter() - start times.append(elapsed) size = os.stat('use.o').st_size return Measurement(time=min(times), size=size) class AxisMax: def __init__(self): self.max = 0 def update(self, new_max): self.max = max(self.max, new_max) class Subplots: def __init__(self, x, y): self.fig = pyplot.figure(figsize=(15, 7)) pyplot.subplots_adjust(left=0.05, right=0.95, wspace=0.6, hspace=0.5) self.x = x self.y = y self.current = 0 self.subplots = [] def next(self, subplot): self.current += 1 self.subplots.append(subplot) return self.fig.add_subplot(self.y, self.x, self.current) def bound_axes(self): for subplot in self.subplots: subplot.bound_axes() class SizeTimePlot: def __init__(self, subplots, size_max=None, time_max=None): self.size = subplots.next(self) self.time = self.size.twinx() self.size.set_ylabel('Size/kB', color='b') self.time.set_ylabel('Time/s', color='r') self.size_max = size_max self.time_max = time_max def title(self, title): self.size.set_title(title) def xlabel(self, xlabel): self.size.set_xlabel(xlabel) def plot(self, xs, compute_y): xs = tuple(xs) measurements = [compute_y(x) for x in xs] self.size.plot(xs, [m.size / 1024 for m in measurements], 'b-') self.time.plot(xs, [m.time for m in measurements], 'r-') if self.size_max: xmin, xmax, ymin, ymax = self.size.axis() self.size_max.update(ymax) if self.time_max: xmin, xmax, ymin, ymax = self.time.axis() self.time_max.update(ymax) def bound_axes(self): self.size.axis(ymin=0, ymax=(self.size_max and self.size_max.max)) self.time.axis(ymin=0, ymax=(self.time_max and self.time_max.max)) for tl in self.size.get_yticklabels(): tl.set_color('b') for tl in self.time.get_yticklabels(): tl.set_color('r') class MultiSeriesPlot: def __init__(self, subplots, ymax=None): self._plot = subplots.next(self) self.ymax = ymax def title(self, title): self._plot.set_title(title) def ylabel(self, ylabel): self._plot.set_ylabel(ylabel) def xaxis(self, xlabel, xpoints): self._plot.set_xlabel(xlabel) self.xpoints = tuple(xpoints) def plot(self, compute_y, *args, **kwargs): self._plot.plot(self.xpoints, [compute_y(x) for x in self.xpoints], *args, **kwargs) if self.ymax: xmin, xmax, ymin, ymax = self._plot.axis() self.ymax.update(ymax) def legend(self, *args, **kwargs): self._plot.legend(*args, **kwargs) def bound_axes(self): self._plot.axis(ymin=0, ymax=(self.ymax and self.ymax.max)) def main(): subplots = Subplots(3, 2) size_max = AxisMax() for virtual_dtor in [True, False]: for compile_line, title in [(CL_O2, 'cl /O2'), (CL_Od, 'cl /Od'), (CLANG_CL_O2, 'clang-cl /O2')]: fig = MultiSeriesPlot(subplots, ymax=size_max) if virtual_dtor: fig.title(title + '\nvirtual dtor') else: fig.title(title + '\nnon-virtual dtor') fig.ylabel('Size/kB') fig.xaxis('# methods', range(0, 5)) fig.plot(lambda count: measure(compile_line=compile_line, num_interfaces=TARGET_INTERFACES, outline_void_methods=0, total_void_methods=count, outline_bool_methods=1, total_bool_methods=1, repetitions=3, inline_destructor=True, virtual_dtor=virtual_dtor).size / 1024, 'b-', label='inline dtor') fig.plot(lambda count: measure(compile_line=compile_line, num_interfaces=TARGET_INTERFACES, outline_void_methods=0, total_void_methods=count, outline_bool_methods=1, total_bool_methods=1, repetitions=3, inline_destructor=False, virtual_dtor=virtual_dtor).size / 1024, 'k-.', label='outline dtor') fig.legend(loc='best') subplots.bound_axes() pyplot.show() return time_max = AxisMax() fig = SizeTimePlot(subplots) fig.title('With 10 inline\nvoid and bool methods') fig.xlabel('# interfaces') fig.plot(range(1, TARGET_INTERFACES * 2, int(TARGET_INTERFACES / 10)), lambda num_interfaces: measure(num_interfaces=num_interfaces, outline_void_methods=0, total_void_methods=10, outline_bool_methods=0, total_bool_methods=10)) fig = SizeTimePlot(subplots, size_max=size_max, time_max=time_max) fig.title('With {} interfaces,\n10 void methods, 0 bool methods'.format( TARGET_INTERFACES)) fig.xlabel('# inline void methods') fig.plot(range(0, 11), lambda count: measure(num_interfaces=TARGET_INTERFACES, outline_void_methods=10 - count, total_void_methods=10, outline_bool_methods=0, total_bool_methods=0, repetitions=3)) fig = SizeTimePlot(subplots, size_max=size_max, time_max=time_max) fig.title('With {} interfaces,\n0 void methods, 10 bool methods'.format( TARGET_INTERFACES)) fig.xlabel('# inline bool methods') fig.plot(range(0, 11), lambda count: measure(num_interfaces=TARGET_INTERFACES, outline_void_methods=0, total_void_methods=0, outline_bool_methods=10 - count, total_bool_methods=10, repetitions=3)) fig = SizeTimePlot(subplots, size_max=size_max, time_max=time_max) fig.title('{} interfaces with bool methods,\nall inline'.format( TARGET_INTERFACES)) fig.xlabel('# bool methods') fig.plot(range(0, 11), lambda count: measure(num_interfaces=TARGET_INTERFACES, outline_void_methods=0, total_void_methods=0, outline_bool_methods=0, total_bool_methods=count, repetitions=3)) fig = SizeTimePlot(subplots, size_max=size_max, time_max=time_max) fig.title('{} interfaces with void methods,\nall inline'.format( TARGET_INTERFACES)) fig.xlabel('# void methods') fig.plot(range(0, 11), lambda count: measure(num_interfaces=TARGET_INTERFACES, outline_void_methods=0, total_void_methods=count, outline_bool_methods=0, total_bool_methods=0, repetitions=3)) fig = SizeTimePlot(subplots, size_max=size_max, time_max=time_max) fig.title('{} interfaces with bool methods,\n1 out of line'.format( TARGET_INTERFACES)) fig.xlabel('# bool methods') fig.plot(range(0, 11), lambda count: measure(num_interfaces=TARGET_INTERFACES, outline_void_methods=0, total_void_methods=0, outline_bool_methods=1, total_bool_methods=count, repetitions=3)) fig = SizeTimePlot(subplots, size_max=size_max, time_max=time_max) fig.title('{} interfaces with void methods,\n1 out of line'.format( TARGET_INTERFACES)) fig.xlabel('# void methods') fig.plot(range(0, 11), lambda count: measure(num_interfaces=TARGET_INTERFACES, outline_void_methods=1, total_void_methods=count, outline_bool_methods=0, total_bool_methods=0, repetitions=3)) fig = MultiSeriesPlot(subplots, ymax=time_max) fig.title('All-inline method+vtable time') fig.ylabel('Time/s') fig.xaxis('# methods', range(0, 11)) fig.plot(lambda count: measure(num_interfaces=TARGET_INTERFACES, outline_void_methods=0, total_void_methods=count, outline_bool_methods=0, total_bool_methods=0, repetitions=3).time, 'b-', label='empty body') fig.plot(lambda count: measure(num_interfaces=TARGET_INTERFACES, outline_void_methods=0, total_void_methods=0, outline_bool_methods=0, total_bool_methods=count, repetitions=3).time, 'r--', label='return false') fig.legend(loc='best') subplots.bound_axes() pyplot.show() with tempfile.TemporaryDirectory() as dir: os.chdir(dir) try: main() finally: os.chdir('/')