#!/usr/bin/env python3 from __future__ import print_function import time, subprocess, sys, os, shutil, glob, random import argparse def median(lst): lst = sorted(lst) mid, odd = divmod(len(lst), 2) if odd: return lst[mid] else: return (lst[mid - 1] + lst[mid]) / 2.0 def mean(lst): return float(sum(lst)) / max(len(lst), 1) compiler_path = '' flags = [] main_file = r''' #define CATCH_CONFIG_MAIN #include "catch.hpp" ''' main_name = 'catch-main.cpp' dir_name = 'benchmark-dir' files = 20 test_cases_in_file = 20 sections_in_file = 4 assertions_per_section = 5 checks = [ 'a != b', 'a != c', 'a != d', 'a != e', 'b != c', 'b != d', 'b != e', 'c != d', 'c != e', 'd != e', 'a + a == a', 'a + b == b', 'a + c == c', 'a + d == d', 'a + e == e', 'b + a == b', 'b + b == c', 'b + c == d', 'b + d == e', 'c + a == c', 'c + b == d', 'c + c == e', 'd + a == d', 'd + b == e', 'e + a == e', 'a + a + a == a', 'b + c == a + d', 'c + a + a == a + b + b + a', 'a < b', 'b < c', 'c < d', 'd < e', 'a >= a', 'd >= b', ] def create_temp_dir(): if os.path.exists(dir_name): shutil.rmtree(dir_name) os.mkdir(dir_name) def copy_catch(path_to_catch): shutil.copy(path_to_catch, dir_name) def create_catch_main(): with open(main_name, 'w') as f: f.write(main_file) def compile_main(): start_t = time.time() subprocess.check_call([compiler_path, main_name, '-c'] + flags) end_t = time.time() return end_t - start_t def compile_files(): cpp_files = glob.glob('tests*.cpp') start_t = time.time() subprocess.check_call([compiler_path, '-c'] + flags + cpp_files) end_t = time.time() return end_t - start_t def link_files(): obj_files = glob.glob('*.o') start_t = time.time() subprocess.check_call([compiler_path] + flags + obj_files) end_t = time.time() return end_t - start_t def benchmark(func): results = [func() for i in range(10)] return mean(results), median(results) def char_range(start, end): for c in range(ord(start), ord(end)): yield chr(c) def generate_sections(fd): for i in range(sections_in_file): fd.write(' SECTION("Section {}") {{\n'.format(i)) fd.write('\n'.join(' CHECK({});'.format(check) for check in random.sample(checks, assertions_per_section))) fd.write(' }\n') def generate_file(file_no): with open('tests{}.cpp'.format(file_no), 'w') as f: f.write('#include "catch.hpp"\n\n') for i in range(test_cases_in_file): f.write('TEST_CASE("File {} test {}", "[.compile]"){{\n'.format(file_no, i)) for i, c in enumerate(char_range('a', 'f')): f.write(' int {} = {};\n'.format(c, i)) generate_sections(f) f.write('}\n\n') def generate_files(): create_catch_main() for i in range(files): generate_file(i) options = ['all', 'main', 'files', 'link'] parser = argparse.ArgumentParser(description='Benchmarks Catch\'s compile times against some synthetic tests') # Add first arg -- benchmark type parser.add_argument('benchmark_kind', nargs='?', default='all', choices=options, help='What kind of benchmark to run, default: all') # Args to allow changing header/compiler parser.add_argument('-I', '--catch-header', default='catch.hpp', help = 'Path to catch.hpp, default: catch.hpp') parser.add_argument('-c', '--compiler', default='g++', help = 'Compiler to use, default: g++') parser.add_argument('-f', '--flags', help = 'Flags to be passed to the compiler. Pass as "," separated list') # Allow creating files only, without running the whole thing parser.add_argument('-g', '--generate-files', action='store_true', help='Generate test files and quit') args = parser.parse_args() compiler_path = args.compiler catch_path = args.catch_header if args.generate_files: create_temp_dir() copy_catch(catch_path) os.chdir(dir_name) # now create the fake test files generate_files() # Early exit print('Finished generating files') exit(1) os.chdir(dir_name) if args.flags: flags = args.flags.split(',') print('Time needed for ...') if args.benchmark_kind in ('all', 'main'): print(' ... compiling main, mean: {:.2f}, median: {:.2f} s'.format(*benchmark(compile_main))) if args.benchmark_kind in ('all', 'files'): print(' ... compiling test files, mean: {:.2f}, median: {:.2f} s'.format(*benchmark(compile_files))) if args.benchmark_kind in ('all', 'link'): print(' ... linking everything, mean: {:.2f}, median: {:.2f} s'.format(*benchmark(link_files)))