1cbd624adSopenharmony_ci#!/usr/bin/env python 2cbd624adSopenharmony_ci''' 3cbd624adSopenharmony_ci timings 4cbd624adSopenharmony_ci ======= 5cbd624adSopenharmony_ci 6cbd624adSopenharmony_ci Plot the timings from building minimal-lexical. 7cbd624adSopenharmony_ci''' 8cbd624adSopenharmony_ci 9cbd624adSopenharmony_ciimport argparse 10cbd624adSopenharmony_ciimport json 11cbd624adSopenharmony_ciimport subprocess 12cbd624adSopenharmony_ciimport os 13cbd624adSopenharmony_ci 14cbd624adSopenharmony_ciimport matplotlib.pyplot as plt 15cbd624adSopenharmony_cifrom matplotlib import patches 16cbd624adSopenharmony_cifrom matplotlib import textpath 17cbd624adSopenharmony_ci 18cbd624adSopenharmony_ciplt.style.use('ggplot') 19cbd624adSopenharmony_ci 20cbd624adSopenharmony_ciscripts = os.path.dirname(os.path.realpath(__file__)) 21cbd624adSopenharmony_cihome = os.path.dirname(scripts) 22cbd624adSopenharmony_ci 23cbd624adSopenharmony_cidef parse_args(argv=None): 24cbd624adSopenharmony_ci '''Create and parse our command line arguments.''' 25cbd624adSopenharmony_ci 26cbd624adSopenharmony_ci parser = argparse.ArgumentParser(description='Time building minimal-lexical.') 27cbd624adSopenharmony_ci parser.add_argument( 28cbd624adSopenharmony_ci '--features', 29cbd624adSopenharmony_ci help='''optional features to add''', 30cbd624adSopenharmony_ci default='', 31cbd624adSopenharmony_ci ) 32cbd624adSopenharmony_ci parser.add_argument( 33cbd624adSopenharmony_ci '--no-default-features', 34cbd624adSopenharmony_ci help='''disable default features''', 35cbd624adSopenharmony_ci action='store_true', 36cbd624adSopenharmony_ci ) 37cbd624adSopenharmony_ci return parser.parse_args(argv) 38cbd624adSopenharmony_ci 39cbd624adSopenharmony_cidef clean(directory=home): 40cbd624adSopenharmony_ci '''Clean the project''' 41cbd624adSopenharmony_ci 42cbd624adSopenharmony_ci os.chdir(directory) 43cbd624adSopenharmony_ci subprocess.check_call( 44cbd624adSopenharmony_ci ['cargo', '+nightly', 'clean'], 45cbd624adSopenharmony_ci shell=False, 46cbd624adSopenharmony_ci stdout=subprocess.DEVNULL, 47cbd624adSopenharmony_ci stderr=subprocess.DEVNULL, 48cbd624adSopenharmony_ci ) 49cbd624adSopenharmony_ci 50cbd624adSopenharmony_cidef build(args): 51cbd624adSopenharmony_ci '''Build the project and get the timings output.''' 52cbd624adSopenharmony_ci 53cbd624adSopenharmony_ci command = 'cargo +nightly build -Z timings=json' 54cbd624adSopenharmony_ci if args.no_default_features: 55cbd624adSopenharmony_ci command = f'{command} --no-default-features' 56cbd624adSopenharmony_ci if args.features: 57cbd624adSopenharmony_ci command = f'{command} --features={args.features}' 58cbd624adSopenharmony_ci process = subprocess.Popen( 59cbd624adSopenharmony_ci # Use shell for faster performance. 60cbd624adSopenharmony_ci # Spawning a new process is a **lot** slower, gives misleading info. 61cbd624adSopenharmony_ci command, 62cbd624adSopenharmony_ci shell=True, 63cbd624adSopenharmony_ci stderr=subprocess.DEVNULL, 64cbd624adSopenharmony_ci stdout=subprocess.PIPE, 65cbd624adSopenharmony_ci ) 66cbd624adSopenharmony_ci process.wait() 67cbd624adSopenharmony_ci data = {} 68cbd624adSopenharmony_ci for line in iter(process.stdout.readline, b''): 69cbd624adSopenharmony_ci line = line.decode('utf-8') 70cbd624adSopenharmony_ci crate = json.loads(line) 71cbd624adSopenharmony_ci name = crate['target']['name'] 72cbd624adSopenharmony_ci data[name] = (crate['duration'], crate['rmeta_time']) 73cbd624adSopenharmony_ci 74cbd624adSopenharmony_ci process.stdout.close() 75cbd624adSopenharmony_ci 76cbd624adSopenharmony_ci return data 77cbd624adSopenharmony_ci 78cbd624adSopenharmony_cidef filename(basename, args): 79cbd624adSopenharmony_ci '''Get a resilient name for the benchmark data.''' 80cbd624adSopenharmony_ci 81cbd624adSopenharmony_ci name = basename 82cbd624adSopenharmony_ci if args.no_default_features: 83cbd624adSopenharmony_ci name = f'{name}_nodefault' 84cbd624adSopenharmony_ci if args.features: 85cbd624adSopenharmony_ci name = f'{name}_features={args.features}' 86cbd624adSopenharmony_ci return name 87cbd624adSopenharmony_ci 88cbd624adSopenharmony_cidef plot_timings(timings, output): 89cbd624adSopenharmony_ci '''Plot our timings data.''' 90cbd624adSopenharmony_ci 91cbd624adSopenharmony_ci offset = 0 92cbd624adSopenharmony_ci text_length = 0 93cbd624adSopenharmony_ci count = len(timings) + 1 94cbd624adSopenharmony_ci fig, ax = plt.subplots() 95cbd624adSopenharmony_ci bar_height = count * 0.05 96cbd624adSopenharmony_ci 97cbd624adSopenharmony_ci def plot_timing(name): 98cbd624adSopenharmony_ci '''Plot the timing of a specific value.''' 99cbd624adSopenharmony_ci 100cbd624adSopenharmony_ci nonlocal count 101cbd624adSopenharmony_ci nonlocal text_length 102cbd624adSopenharmony_ci 103cbd624adSopenharmony_ci if name not in timings: 104cbd624adSopenharmony_ci return 105cbd624adSopenharmony_ci duration, rmeta = timings[name] 106cbd624adSopenharmony_ci local_offset = offset 107cbd624adSopenharmony_ci ax.add_patch(patches.Rectangle( 108cbd624adSopenharmony_ci (offset, count - bar_height / 2), duration, bar_height, 109cbd624adSopenharmony_ci alpha=0.6, 110cbd624adSopenharmony_ci facecolor='lightskyblue', 111cbd624adSopenharmony_ci label=name, 112cbd624adSopenharmony_ci )) 113cbd624adSopenharmony_ci local_offset += rmeta 114cbd624adSopenharmony_ci ax.add_patch(patches.Rectangle( 115cbd624adSopenharmony_ci (local_offset, count - bar_height / 2), duration - rmeta, bar_height, 116cbd624adSopenharmony_ci alpha=0.6, 117cbd624adSopenharmony_ci facecolor='darkorchid', 118cbd624adSopenharmony_ci label=f'{name}_rmeta', 119cbd624adSopenharmony_ci )) 120cbd624adSopenharmony_ci local_offset += duration - rmeta 121cbd624adSopenharmony_ci text = f'minimal-lexical {round(duration, 2)}s' 122cbd624adSopenharmony_ci text_length = max(len(text), text_length) 123cbd624adSopenharmony_ci ax.annotate( 124cbd624adSopenharmony_ci text, 125cbd624adSopenharmony_ci xy=(local_offset + 0.02, count), 126cbd624adSopenharmony_ci xycoords='data', 127cbd624adSopenharmony_ci horizontalalignment='left', 128cbd624adSopenharmony_ci verticalalignment='center', 129cbd624adSopenharmony_ci ) 130cbd624adSopenharmony_ci count -= 1 131cbd624adSopenharmony_ci 132cbd624adSopenharmony_ci def max_duration(*keys): 133cbd624adSopenharmony_ci '''Get the max duration from a list of keys.''' 134cbd624adSopenharmony_ci 135cbd624adSopenharmony_ci max_time = 0 136cbd624adSopenharmony_ci for key in keys: 137cbd624adSopenharmony_ci if key not in timings: 138cbd624adSopenharmony_ci continue 139cbd624adSopenharmony_ci max_time = max(timings[key][0], max_time) 140cbd624adSopenharmony_ci return max_time 141cbd624adSopenharmony_ci 142cbd624adSopenharmony_ci # Plot in order of our dependencies. 143cbd624adSopenharmony_ci plot_timing('minimal-lexical') 144cbd624adSopenharmony_ci offset += max_duration('minimal-lexical') 145cbd624adSopenharmony_ci 146cbd624adSopenharmony_ci title = 'Build Timings' 147cbd624adSopenharmony_ci ax.set_title(title) 148cbd624adSopenharmony_ci ax.set_xlabel('Time (s)') 149cbd624adSopenharmony_ci 150cbd624adSopenharmony_ci # Hide the y-axis labels. 151cbd624adSopenharmony_ci ax.set_yticks(list(range(1, len(timings) + 2))) 152cbd624adSopenharmony_ci ax.yaxis.set_tick_params(which='both', length=0) 153cbd624adSopenharmony_ci plt.setp(ax.get_yticklabels(), visible=False) 154cbd624adSopenharmony_ci 155cbd624adSopenharmony_ci # Ensure the canvas includes all the annotations. 156cbd624adSopenharmony_ci # 0.5 is long enough for the largest label. 157cbd624adSopenharmony_ci plt.xlim(0, offset + 0.02 * text_length) 158cbd624adSopenharmony_ci plt.ylim(count + 0.5, len(timings) + 1.5) 159cbd624adSopenharmony_ci 160cbd624adSopenharmony_ci # Save the figure. 161cbd624adSopenharmony_ci fig.savefig(output, format='svg') 162cbd624adSopenharmony_ci fig.clf() 163cbd624adSopenharmony_ci 164cbd624adSopenharmony_cidef plot(args): 165cbd624adSopenharmony_ci '''Build and plot the timings for the root module.''' 166cbd624adSopenharmony_ci 167cbd624adSopenharmony_ci clean() 168cbd624adSopenharmony_ci timings = build(args) 169cbd624adSopenharmony_ci path = f'{home}/assets/timings_{filename("timings", args)}_{os.name}.svg' 170cbd624adSopenharmony_ci plot_timings(timings, path) 171cbd624adSopenharmony_ci 172cbd624adSopenharmony_cidef main(argv=None): 173cbd624adSopenharmony_ci '''Entry point.''' 174cbd624adSopenharmony_ci 175cbd624adSopenharmony_ci args = parse_args(argv) 176cbd624adSopenharmony_ci plot(args) 177cbd624adSopenharmony_ci 178cbd624adSopenharmony_ciif __name__ == '__main__': 179cbd624adSopenharmony_ci main() 180