def gen_unary_ops_tests(lf, rt, opts): for op_name, s0, s1 in bitwise_unary_ops: decls = check + limits + gen_random_val # {op}b content_src = bitwise_unary_test_template.format( op_name=op_name, lf=lf, rt=rt, includes=includes, decls=decls, rand_statement="__gen_random_val<{lf}, {rt}>();".format(lf=lf, rt=rt), test_statement=s0, l="", term="b") filename = get_filename(opts, op_name + "b", lf, rt) with common.open_utf8(opts, filename) as fp: fp.write(content_src) common.clang_format(opts, filename) # {op}l content_src = bitwise_unary_test_template.format( op_name=op_name, lf=lf, rt=rt, includes=includes, decls=decls, rand_statement="(raw_t)(rand() % 2);".format(lf=lf, rt=rt), test_statement=s1, l="l", term="l") filename = get_filename(opts, op_name + "l", lf, rt) with common.open_utf8(opts, filename) as fp: fp.write(content_src) common.clang_format(opts, filename)
def gen_modules_md(opts): common.myprint(opts, 'Generating modules.md') mods = common.get_modules(opts) ndms = [] for mod in mods: name = eval('mods[mod].{}.hatch.name()'.format(mod)) desc = eval('mods[mod].{}.hatch.desc()'.format(mod)) ndms.append([name, desc, mod]) filename = common.get_markdown_file(opts, 'modules') if not common.can_create_filename(opts, filename): return with common.open_utf8(opts, filename) as fout: fout.write('''# Modules NSIMD comes with several additional modules. A module provides a set of functionnalities that are usually not at the same level as SIMD intrinsics and/or that do not provide all C and C++ APIs. These functionnalities are given with the library because they make heavy use of NSIMD core which abstract SIMD intrinsics. Below is the exhaustive list of modules. ''') for ndm in ndms: fout.write('- [{}](module_{}_overview.md) \n'.format( ndm[0], ndm[2])) fout.write('\n'.join([' {}'.format(line.strip()) \ for line in ndm[1].split('\n')])) fout.write('\n\n')
def doit(opts): common.myprint(opts, 'Generating friendly but not optimized advanced ' 'C++ API') filename = os.path.join(opts.include_dir, 'friendly_but_not_optimized.hpp') if not common.can_create_filename(opts, filename): return with common.open_utf8(opts, filename) as out: out.write('''#ifndef NSIMD_FRIENDLY_BUT_NOT_OPTIMIZED_HPP #define NSIMD_FRIENDLY_BUT_NOT_OPTIMIZED_HPP #include <nsimd/nsimd.h> #include <nsimd/cxx_adv_api.hpp> namespace nsimd {{ '''.format(year=date.today().year)) for op_name, operator in operators.operators.items(): if operator.cxx_operator == None or len(operator.params) != 3 or \ operator.name in ['shl', 'shr']: continue out.write('''{hbar} {code} '''.format(hbar=common.hbar, code=get_impl(operator))) out.write('''{hbar} }} // namespace nsimd #endif'''.format(hbar=common.hbar)) common.clang_format(opts, filename)
def doit(opts): print ('-- Generating base APIs') common.mkdir_p(opts.include_dir) filename = os.path.join(opts.include_dir, 'functions.h') if not common.can_create_filename(opts, filename): return with common.open_utf8(filename) as out: out.write('''#ifndef NSIMD_FUNCTIONS_H #define NSIMD_FUNCTIONS_H '''.format(year=date.today().year)) for op_name, operator in operators.operators.items(): out.write('''{} #include NSIMD_AUTO_INCLUDE({}.h) {} {} '''.format(common.hbar, operator.name, get_c_base_generic(operator), get_cxx_base_generic(operator))) out.write('''{hbar} {put_decl} {hbar} #endif'''. \ format(hbar=common.hbar, put_decl=get_put_decl())) common.clang_format(opts, filename)
def doit(opts): print ('-- Generating advanced C++ API') filename = os.path.join(opts.include_dir, 'cxx_adv_api_functions.hpp') if not common.can_create_filename(opts, filename): return with common.open_utf8(opts, filename) as out: out.write('''#ifndef NSIMD_CXX_ADV_API_FUNCTIONS_HPP #define NSIMD_CXX_ADV_API_FUNCTIONS_HPP namespace nsimd {{ '''.format(year=date.today().year)) for op_name, operator in operators.operators.items(): if not operator.autogen_cxx_adv: continue out.write('''{hbar} {code} '''.format(hbar=common.hbar, code=get_cxx_advanced_generic(operator))) out.write('''{hbar} }} // namespace nsimd #endif'''.format(hbar=common.hbar)) common.clang_format(opts, filename)
def gen_archis_write_file(opts, op, platform, simd_ext, simd_dir): filename = os.path.join(simd_dir, '{}.h'.format(op.name)) if not common.can_create_filename(opts, filename): return mod = opts.platforms[platform] with common.open_utf8(opts, filename) as out: out.write('''#ifndef {guard} #define {guard} #include <nsimd/{platform}/{simd_ext}/types.h> {additional_include} {code} {hbar} #endif '''.format(additional_include=mod.get_additional_include( op.name, platform, simd_ext), year=date.today().year, guard=op.get_header_guard(platform, simd_ext), platform=platform, simd_ext=simd_ext, func=op.name, hbar=common.hbar, code=get_simd_implementation( opts, op, mod, simd_ext))) common.clang_format(opts, filename)
def doit(opts): common.myprint(opts, 'Generating ulps') common.mkdir_p(opts.ulps_dir) for op_name, operator in operators.operators.items(): if not operator.tests_mpfr: continue if op_name in ['gammaln', 'lgamma', 'pow']: continue mpfr_func = operator.tests_mpfr_name() mpfr_rnd = ", MPFR_RNDN" for typ in common.ftypes: if typ == 'f16': random_generator = random_f16_generator convert_to_type = "nsimd_f32_to_f16" convert_from_type = "nsimd_f16_to_f32" mantisse = 10 size = 0xffff mpfr_suffix = "flt" elif typ == 'f32': convert_to_type = "(f32)" convert_from_type = "" random_generator = random_f32_generator mantisse = 23 #size = 0xffffffff size = 0x00ffffff mpfr_suffix = "flt" elif typ == 'f64': convert_to_type = "(f64)" convert_from_type = "" random_generator = random_f64_generator mantisse = 52 size = 0x00ffffff mpfr_suffix = "d" else: raise Exception('Unsupported type "{}"'.format(typ)) filename = os.path.join(opts.ulps_dir, '{}_{}_{}.cpp'. \ format(op_name, "ulp", typ)) if not common.can_create_filename(opts, filename): continue with common.open_utf8(opts, filename) as out: out.write(includes) out.write(gen_tests.relative_distance_cpp) out.write( code.format(typ=typ, nsimd_func=op_name, mpfr_func=mpfr_func, mpfr_rnd=mpfr_rnd, random_generator=random_generator, convert_from_type=convert_from_type, convert_to_type=convert_to_type, mantisse=mantisse, SIZE=size, mpfr_suffix=mpfr_suffix)) common.clang_format(opts, filename)
def gen_bench(f, simd, typ): ## TODO path = gen_filename(f, simd, typ) ## Check if we need to create the file if not common.can_create_filename(_opts, path): return ## Generate specific code for the bench category = common.nsimd_category(simd) code = gen_code(f, simd, typ, category=category) if code is None: return ## Now aggregate every parts bench = '' #bench += gen_bench_asm_function(f, typ, category) bench += gen_bench_against(f, simd, typ, f.bench_against_cpu()) bench += code bench += gen_bench_unrolls(f, simd, typ, category) bench += gen_bench_against(f, simd, typ, f.bench_against_libs()) ## Finalize code code = gen_bench_from_code(f, typ, bench) ## Write file with common.open_utf8(path) as f: f.write(code) ## Clang-format it! common.clang_format(_opts, path)
def gen_doc(opts): for op in operators: filename = common.get_markdown_api_file(opts, op.name, 'fixed_point') with common.open_utf8(opts, filename) as fout: fout.write( api_template.format(full_name=op.full_name, desc=op.desc, decl=gen_decl(op)))
def gen_doc(opts): for op in operators: filename = os.path.join(opts.script_dir, '..', 'doc', 'markdown', 'modules', 'fixed_point', 'api_{}.md'.format(op.name)) with common.open_utf8(opts, filename) as fout: fout.write( api_template.format(full_name=op.full_name, desc=op.desc, decl=gen_decl(op)))
def gen_doc(opts, op_list): for _, op in operators.operators.items(): if op.name not in op_list: continue filename = common.get_markdown_api_file(opts, op.name, 'fixed_point') with common.open_utf8(opts, filename) as fout: fout.write( api_template.format(full_name=op.full_name, desc=op.desc, decl=gen_decl(op)))
def gen_if_else_tests(lf, rt, opts): decls = check + limits + comparison_fp + gen_random_val content_src = if_else_test_template.format(lf=lf, rt=rt, includes=includes, decls=decls) filename = get_filename(opts, "if_else", lf, rt) with common.open_utf8(opts, filename) as fp: fp.write(content_src) common.clang_format(opts, filename)
def gen_html(opts): # check if md2html exists md2html = 'md2html.exe' if platform.system() == 'Windows' else 'md2html' doc_dir = os.path.join(opts.script_dir, '..', 'doc') full_path_md2html = os.path.join(doc_dir, md2html) if not os.path.isfile(full_path_md2html): msg = '-- Cannot generate HTML: {} not found. '.format(md2html) if platform.system() == 'Windows': msg += 'Run "nmake /F Makefile.win" in {}'.format(doc_dir) else: msg += 'Run "make -f Makefile.nix" in {}'.format(doc_dir) print(msg) return # get all markdown files md_dir = os.path.join(doc_dir, 'markdown/modules/fixed_point') html_dir = os.path.join(doc_dir, 'html/modules/fixed_point') common.mkdir_p(html_dir) dirs = [md_dir] md_files = [] while len(dirs) > 0: curr_dir = dirs.pop() entries = os.listdir(curr_dir) for entry in entries: full_path_entry = os.path.join(curr_dir, entry) if full_path_entry == '..' or full_path_entry == '.': continue elif os.path.isdir(full_path_entry): continue elif entry.endswith('.md'): md_files.append(full_path_entry) # header and footer doc_title = '`nsimd` fixed point module documentation' root_dir = '../..' assets_dir = '../../assets' img_dir = '../../img' header = header_src.format(doc_title=doc_title, root_dir=root_dir, img_dir=img_dir, assets_dir=assets_dir) footer = footer_src tmp_file = os.path.join(doc_dir, 'tmp.html') for filename in md_files: i = filename.rfind('markdown') if i == -1: continue output = filename[0:i] + 'html' + filename[i + 8:-2] + 'html' common.mkdir_p(os.path.dirname(output)) os.system('{} "{}" "{}"'.format(full_path_md2html, filename, tmp_file)) with common.open_utf8(opts, output) as fout: fout.write(header) with io.open(tmp_file, mode='r', encoding='utf-8') as fin: fout.write(fin.read()) fout.write(footer)
def gen_comparison_tests(lf, rt, opts): for op_name, op_val in comparison_ops: decls = check + limits + comparison_log.format(op_val=op_val) + gen_random_val content_src = comparison_test_template.format( op_name=op_name, op_val=op_val, lf=lf, rt=rt, includes=includes, decls=decls) filename = get_filename(opts, op_name, lf, rt) if filename == None: continue with common.open_utf8(opts, filename) as fp: fp.write(content_src) common.clang_format(opts, filename)
def gen_minmax_ops_tests(lf, rt, opts): for op_name in minmax_ops: decls = check + limits + comparison_fp + gen_random_val content_src = minmax_test_template.format(op_name=op_name, lf=lf, rt=rt, includes=includes, decls=decls) filename = get_filename(opts, op_name, lf, rt) with common.open_utf8(opts, filename) as fp: fp.write(content_src) common.clang_format(opts, filename)
def gen_ternary_ops_tests(lf, rt, opts): for op_name, statement in ternary_ops: decls = check + limits + comparison_fp + gen_random_val content_src = ternary_ops_template.format( op_name=op_name, check_statement=statement.format(lf=lf, rt=rt), lf=lf, rt=rt,includes=includes, decls=decls) filename = get_filename(opts, op_name, lf, rt) if filename == None: continue with common.open_utf8(opts, filename) as fp: fp.write(content_src) common.clang_format(opts, filename)
def write_cpp(opts, simd_ext, emulate_fp16): filename = os.path.join(opts.src_dir, 'api_{}.cpp'.format(simd_ext)) if not common.can_create_filename(opts, filename): return with common.open_utf8(opts, filename) as out: out.write('''#define NSIMD_INSIDE #include <nsimd/nsimd.h> #include <nsimd/cxx_adv_api.hpp> '''.format(year=date.today().year)) out.write(get_put_impl(simd_ext)) common.clang_format(opts, filename)
def gen_math_functions_tests(lf, rt, opts): for op_name in math_ops: decls = check + limits + comparison_fp + gen_random_val if op_name == "rec": decls += rec_reference content_src = math_test_template.format(op_name=op_name, lf=lf, rt=rt, includes=includes, decls=decls) filename = get_filename(opts, op_name, lf, rt) with common.open_utf8(opts, filename) as fp: fp.write(content_src) common.clang_format(opts, filename)
def gen_archis_write_put(opts, platform, simd_ext, simd_dir): filename = os.path.join(simd_dir, 'put.h') if not common.can_create_filename(opts, filename): return op = None with common.open_utf8(filename) as out: out.write( \ '''#ifndef NSIMD_{PLATFORM}_{SIMD_EXT}_PUT_H #define NSIMD_{PLATFORM}_{SIMD_EXT}_PUT_H {include_cpu_put}#include <nsimd/{platform}/{simd_ext}/types.h> #include <stdio.h> {hbar} '''.format(year=date.today().year, hbar=common.hbar, simd_ext=simd_ext, platform=platform, PLATFORM=platform.upper(), SIMD_EXT=simd_ext.upper(), include_cpu_put='#include <nsimd/cpu/cpu/put.h>\n' \ if simd_ext != 'cpu' else '')) for typ in common.types: out.write( \ '''#if NSIMD_CXX > 0 extern "C" {{ #endif NSIMD_DLLSPEC int nsimd_put_{simd_ext}_{typ}(FILE *, const char *, nsimd_{simd_ext}_v{typ}); #if NSIMD_CXX > 0 }} // extern "C" #endif #if NSIMD_CXX > 0 namespace nsimd {{ NSIMD_INLINE int put(FILE *out, const char *fmt, nsimd_{simd_ext}_v{typ} a0, {typ}, {simd_ext}) {{ return nsimd_put_{simd_ext}_{typ}(out, fmt, a0); }} }} // namespace nsimd #endif {hbar} '''.format(simd_ext=simd_ext, hbar=common.hbar, typ=typ)) out.write('#endif') common.clang_format(opts, filename)
def gen_api(opts): filename = os.path.join(opts.script_dir, '..', 'doc', 'markdown', 'modules', 'fixed_point', 'api.md') with common.open_utf8(opts, filename) as fout: fout.write('''# NSIMD fixed point API\n''') for cat in fp_categories: ops = [op for op in fp_operators if cat in op.categories] if (len(ops) == 0): continue fout.write('\n## {}\n\n'.format(cat)) for op in ops: fout.write('- [{full_name} ({op_name})](api_{op_name}.md)\n'\ .format(full_name=op.full_name, op_name=op.name))
def gen_doc_api(opts): filename = common.get_markdown_file(opts, 'api', 'spmd') if not common.can_create_filename(opts, filename): return # Build tree for api.md api = dict() for _, operator in operators.operators.items(): if not operator.has_scalar_impl: continue for c in operator.categories: if c not in api: api[c] = [operator] else: api[c].append(operator) with common.open_utf8(opts, filename) as fout: fout.write( '''# NSIMD SPMD API reference This page contains the exhaustive API of the SPMD module. Note that most operators names follow the simple naming `k_[NSIMD name]` and have the same semantics. This page is light, you may use CTRL+F to find the operator you are looking for. For genericity on the base type you should use operator names instead of infix operators, e.g. `k_add` instead of `+`. Indeed for `f16`'s NVIDIA CUDA and NSIMD do not provide overloads and therefore code using `+` will fail to compile. Note that all operators accept literals and scalars. For example you may write `k_add(a, 1)` or `float s; k_add(a, s);`. This also applies when using infix operators. But note that literals or scalars must have the same type as the other operands. ''') for c, ops in api.items(): if len(ops) == 0: continue fout.write('\n## {}\n\n'.format(c.title)) for op in ops: fout.write('- `{}` \n'.format(get_signature(op))) if op.cxx_operator != None: fout.write(' Infix operator: `{}` ' \ '(*for certain types only*) \n'.\ format(op.cxx_operator)) fout.write(' {}\n\n'.format(op.desc))
def gen_tests(opts): for func in rand_functions: for word_size, nwords_nrounds in func.wordsize_nwords_nrounds.items(): for nwords, list_nrounds in nwords_nrounds.items(): for nrounds in list_nrounds: # Write headers dirname = os.path.join(opts.tests_dir, 'modules', 'random') common.mkdir_p(dirname) filename = os.path.join(dirname, '{}.cpp'. \ format(func.gen_function_name(nwords, word_size, nrounds))) with common.open_utf8(opts, filename) as out: out.write( func.gen_tests(opts, nrounds, word_size, nwords)) common.clang_format(opts, filename)
def gen_api(opts): filename = common.get_markdown_file(opts, 'api', 'fixed_point') with common.open_utf8(opts, filename) as fout: fout.write('''# NSIMD fixed point API\n''') for cat in fp_categories: ops = [op for op in fp_operators if cat in op.categories] if (len(ops) == 0): continue fout.write('\n## {}\n\n'.format(cat)) for op in ops: fout.write( '- [{} ({})](module_fixed_point_api_{}.md)\n'\ .format(op.full_name, op.name, common.to_filename(op.name)))
def gen_math_functions_tests(lf, rt, opts): for op_name in math_ops: decls = check + limits + comparison_fp + gen_random_val if op_name == "rec": decls += rec_reference ref_op_name = 'rec' else: ref_op_name = 'nsimd_scalar_abs_f64' content_src = math_test_template.format(op_name=op_name, lf=lf, rt=rt, ref_op_name=ref_op_name, includes=includes, decls=decls) filename = get_filename(opts, op_name, lf, rt) if filename == None: continue with common.open_utf8(opts, filename) as fp: fp.write(content_src) common.clang_format(opts, filename)
def gen_doc_html(opts, title): if not opts.list_files: build_exe_for_doc(opts) md2html = 'md2html.exe' if platform.system() == 'Windows' \ else 'md2html' doc_dir = os.path.join(opts.script_dir, '..', 'doc') full_path_md2html = os.path.join(doc_dir, md2html) if not os.path.isfile(full_path_md2html): common.myprint(opts, '{} not found'.format(md2html)) return # get all markdown files md_dir = common.get_markdown_dir(opts) html_dir = get_html_dir(opts) if not os.path.isdir(html_dir): mkdir_p(html_dir) doc_files = [] for filename in os.listdir(md_dir): name = os.path.basename(filename) if name.endswith('.md'): doc_files.append(os.path.splitext(name)[0]) if opts.list_files: ## list gen files for filename in doc_files: input_name = os.path.join(md_dir, filename + '.md') output_name = os.path.join(html_dir, filename + '.html') print(output_name) else: ## gen html files footer = get_html_footer() tmp_file = os.path.join(doc_dir, 'tmp.html') for filename in doc_files: header = get_html_header(opts, title, filename) input_name = os.path.join(md_dir, filename + '.md') output_name = os.path.join(html_dir, filename + '.html') os.system('{} "{}" "{}"'.format(full_path_md2html, input_name, tmp_file)) with common.open_utf8(opts, output_name) as fout: fout.write(header) with io.open(tmp_file, mode='r', encoding='utf-8') as fin: fout.write(fin.read()) fout.write(footer)
def write_cpp(opts, simd_ext, emulate_fp16): filename = os.path.join(opts.src_dir, 'api_{}.cpp'.format(simd_ext)) if not common.can_create_filename(opts, filename): return with common.open_utf8(opts, filename) as out: out.write('''#define NSIMD_INSIDE #include <nsimd/nsimd.h> #include <nsimd/cxx_adv_api.hpp> '''.format(year=date.today().year)) for op_name, operator in operators.operators.items(): if operator.src: out.write('''{hbar} #include <nsimd/src/{name}.hpp> '''.format(name=operator.name, hbar=common.hbar)) out.write(get_impl(operator, emulate_fp16, simd_ext)) out.write(get_put_impl(simd_ext)) common.clang_format(opts, filename)
def gen_api(opts, op_list): api = dict() for _, operator in operators.operators.items(): if operator.name not in op_list: continue for c in operator.categories: if c not in api: api[c] = [operator] else: api[c].append(operator) filename = common.get_markdown_file(opts, 'api', 'fixed_point') with common.open_utf8(opts, filename) as fout: fout.write('''# NSIMD fixed point API\n''') for c, ops in api.items(): if len(ops) == 0: continue fout.write('\n## {}\n\n'.format(c.title)) for op in ops: fout.write('- [{} ({})](module_fixed_point_api_{}.md)\n'. \ format(op.full_name, op.name, common.to_filename(op.name)))
def gen_doc(opts): api = '' for func in rand_functions: for word_size, nwords_nrounds in func.wordsize_nwords_nrounds.items(): for nwords, list_nrounds in nwords_nrounds.items(): for nrounds in list_nrounds: api += '- `' + func.gen_signature(nwords, word_size, nrounds) + '`; \n' api += ' Returns a random number using the ' \ '{func_name} generator\n\n'. \ format(func_name=func.name) res = ''' # NSIMD Random module overview {desc} Two different algorithms are proposed : threefry and philox. Both should give high quality random number. Threefry is quicker on CPU, while philox is best used on GPU. Both algorithms are counter based pseudorandom number generator, meaning that they need two parameters: - a key, each key will generate an unique sequence, - a counter, which will give the different numbers in the sequence. # NSIMD Random API reference {api} '''.format(desc=desc(), api=api) filename = common.get_markdown_file(opts, 'overview', 'random') if not common.can_create_filename(opts, filename): return with common.open_utf8(opts, filename) as fout: fout.write(res)
def doit(opts): common.myprint(opts, 'Generating advanced C++ API') filename = os.path.join(opts.include_dir, 'cxx_adv_api_functions.hpp') if not common.can_create_filename(opts, filename): return with common.open_utf8(opts, filename) as out: out.write('''#ifndef NSIMD_CXX_ADV_API_FUNCTIONS_HPP #define NSIMD_CXX_ADV_API_FUNCTIONS_HPP namespace nsimd { ''') for op_name, operator in operators.operators.items(): if not operator.autogen_cxx_adv: continue out.write('''{hbar} {code} '''.format(hbar=common.hbar, code=get_cxx_advanced_generic(operator))) if operator.cxx_operator and \ (operator.args in [['v', 'v'], ['v', 'p']]): out.write('{hbar}\n{code}'. \ format(hbar=common.hbar, code=gen_assignment_operators(operator))) out.write('''{hbar} }} // namespace nsimd #endif'''.format(hbar=common.hbar)) common.clang_format(opts, filename)
def gen_bench(f, simd, typ): ## TODO path = gen_filename(f, simd, typ) ## Check if we need to create the file if not common.can_create_filename(_opts, path): return ## Generate specific code for the bench category = common.nsimd_category(simd) code = gen_code(f, simd, typ, category=category) if code is None: return ## Now aggregate every parts bench = '' #bench += gen_bench_asm_function(f, typ, category) bench += gen_bench_against(f, 'cpu', typ, f.bench_against_cpu()) bench += code bench += gen_bench_unrolls(f, simd, typ, category) bench += gen_bench_against(f, simd, typ, f.bench_against_libs()) ## bench_with_timestamp bench_with_timestamp = '' bench_with_timestamp += 'std::map<std::string, std::pair<' + typ + ', double>> sums;' + '\n' bench_with_timestamp += 'size_t const nb_runs = 10 * 1000;' + '\n' bench_with_timestamp += gen_bench_against_with_timestamp( f, 'cpu', typ, f.bench_against_cpu()) bench_with_timestamp += gen_bench_with_timestamp(f, simd, typ, category) bench_with_timestamp += gen_bench_unrolls_with_timestamp( f, simd, typ, category) bench_with_timestamp += gen_bench_against_with_timestamp( f, simd, typ, f.bench_against_libs()) bench_with_timestamp += ''' std::string json = ""; json += "{{\\n"; json += " \\"benchmarks\\": [\\n"; for (auto const & bench_name_sum_time : sums) {{ std::string const & bench_name = bench_name_sum_time.first; {typ} const & sum = bench_name_sum_time.second.first; double const & elapsed_time_ns = bench_name_sum_time.second.second; json += " {{" "\\n"; json += " \\"name\\": \\"" + bench_name + "/{typ}\\"," + "\\n"; json += " \\"real_time\\": " + std::to_string(elapsed_time_ns) + "," + "\\n"; json += " \\"sum\\": " + std::string(std::isfinite(sum) ? "" : "\\"") + std::to_string(sum) + std::string(std::isfinite(sum) ? "" : "\\"") + "," + "\\n"; json += " \\"time_unit\\": \\"ns\\"\\n"; json += " }}"; if (&bench_name_sum_time != &*sums.rbegin()) {{ json += ","; }} json += "\\n"; }} json += " ]\\n"; json += "}}\\n"; std::cout << json << std::flush; '''.format(typ=typ) ## Finalize code code = gen_bench_from_code(f, typ, bench, '') # bench_with_timestamp ## Write file with common.open_utf8(path) as f: f.write(code) ## Clang-format it! common.clang_format(_opts, path)