From a7c7409ced9e6c88eaf3b64f530f37730f6a9cc5 Mon Sep 17 00:00:00 2001 From: Marco Poletti Date: Sun, 9 Sep 2018 09:47:07 +0100 Subject: Don't generate runtime bench code in compile benchmarks and executable size benchmarks. --- extras/benchmark/README.md | 6 - extras/benchmark/boost_di_source_generator.py | 21 +++- extras/benchmark/fruit_source_generator.py | 121 ++++-------------- extras/benchmark/generate_benchmark.py | 23 ++-- extras/benchmark/no_di_library_source_generator.py | 21 +++- extras/benchmark/run_benchmarks.py | 20 ++- extras/benchmark/suites/debug.yml | 137 ++++++++++----------- 7 files changed, 147 insertions(+), 202 deletions(-) diff --git a/extras/benchmark/README.md b/extras/benchmark/README.md index 775ba21..f54fbc2 100644 --- a/extras/benchmark/README.md +++ b/extras/benchmark/README.md @@ -33,9 +33,6 @@ The following benchmark suites are defined: `fruit_full.yml`. This is a quick benchmark that can used during development of performance optimizations. * `fruit_debug.yml`: a suite used to debug Fruit's benchmarking code. This is very quick, but the actual results are not meaningful. Run this after changing any benchmarking code, to check that it still works. -* `fruit_full_old_style.yml`: a variant of `fruit_full.yml` that uses the Fruit 2.x API. -* `fruit_quick_old_style.yml`: a variant of `fruit_quick.yml` that uses the Fruit 2.x API. -* `fruit_single_old_style.yml`: a variant of `fruit_single.yml` that uses the Fruit 2.x API. * `boost_di`: unlike the others, this benchmark suite exercises the Boost.DI library (still in boost-experimental at the time of writing) instead of Fruit. @@ -71,11 +68,8 @@ $ ~/projects/fruit/extras/benchmark/format_bench_results.py \ The following tables are defined: * `fruit_wiki.yml`: the "main" table definition, with the tables that are in Fruit's wiki. -* `fruit_wiki_old_style.yml`: a variant of `fruit_wiki.yml` that uses the Fruit 2.x APIs, whereas `fruit_wiki.yml` uses - the new one. This is useful to visualize benchmarks of old Fruit versions that don't support the 3.x new-style API. * `fruit_internal.yml`: a more detailed version of `fruit_wiki.yml`, also displaying metrics that are only meaningful to Fruit developers (e.g. splitting the setup time into component creation time and normalization time). -* `fruit_internal_old_vs_new_style.yml`: used to compare the performance of the Fruit 3.x and 2.x APIs. ### Manual benchmarks diff --git a/extras/benchmark/boost_di_source_generator.py b/extras/benchmark/boost_di_source_generator.py index 421ed34..b6f7ed1 100644 --- a/extras/benchmark/boost_di_source_generator.py +++ b/extras/benchmark/boost_di_source_generator.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -def generate_files(injection_graph): +def generate_files(injection_graph, generate_runtime_bench_code): file_content_by_name = dict() for node_id in injection_graph.nodes_iter(): @@ -23,7 +23,7 @@ def generate_files(injection_graph): [toplevel_node] = [node_id for node_id in injection_graph.nodes_iter() if not injection_graph.predecessors(node_id)] - file_content_by_name['main.cpp'] = _generate_main(injection_graph, toplevel_node) + file_content_by_name['main.cpp'] = _generate_main(injection_graph, toplevel_node, generate_runtime_bench_code) return file_content_by_name @@ -86,14 +86,15 @@ X{component_index}::X{component_index}({component_deps}) """ return template.format(**locals()) -def _generate_main(injection_graph, toplevel_component): +def _generate_main(injection_graph, toplevel_component, generate_runtime_bench_code): include_directives = ''.join('#include "component%s.h"\n' % index for index in injection_graph.nodes_iter()) injector_params = ', '.join('x%sComponent()' % index for index in injection_graph.nodes_iter()) - template = """ + if generate_runtime_bench_code: + template = """ {include_directives} #include "component{toplevel_component}.h" @@ -129,5 +130,17 @@ int main(int argc, char* argv[]) {{ std::cout << "Total per request = " << perRequestTime / num_loops << std::endl; return 0; }} +""" + else: + template = """ +{include_directives} + +#include "component{toplevel_component}.h" + +int main() {{ + auto injector = di::make_injector({injector_params}); + injector.create>(); + return 0; +}} """ return template.format(**locals()) diff --git a/extras/benchmark/fruit_source_generator.py b/extras/benchmark/fruit_source_generator.py index 50d703e..5346aea 100644 --- a/extras/benchmark/fruit_source_generator.py +++ b/extras/benchmark/fruit_source_generator.py @@ -13,28 +13,25 @@ # limitations under the License. -def generate_files(injection_graph, use_fruit_2_x_syntax=False): +def generate_files(injection_graph, generate_runtime_bench_code): file_content_by_name = dict() for node_id in injection_graph.nodes_iter(): - file_content_by_name['component%s.h' % node_id] = _generate_component_header(node_id, use_fruit_2_x_syntax) - file_content_by_name['component%s.cpp' % node_id] = _generate_component_source(node_id, injection_graph.successors(node_id), use_fruit_2_x_syntax) + file_content_by_name['component%s.h' % node_id] = _generate_component_header(node_id) + file_content_by_name['component%s.cpp' % node_id] = _generate_component_source(node_id, injection_graph.successors(node_id)) [toplevel_node] = [node_id for node_id in injection_graph.nodes_iter() if not injection_graph.predecessors(node_id)] - file_content_by_name['main.cpp'] = _generate_main(toplevel_node, use_fruit_2_x_syntax) + file_content_by_name['main.cpp'] = _generate_main(toplevel_node, generate_runtime_bench_code) return file_content_by_name -def _get_component_type(component_index, use_fruit_2_x_syntax): - if use_fruit_2_x_syntax: - return 'const fruit::Component&'.format(**locals()) - else: - return 'fruit::Component'.format(**locals()) +def _get_component_type(component_index): + return 'fruit::Component'.format(**locals()) -def _generate_component_header(component_index, use_fruit_2_x_syntax): - component_type = _get_component_type(component_index, use_fruit_2_x_syntax) +def _generate_component_header(component_index): + component_type = _get_component_type(component_index) template = """ #ifndef COMPONENT{component_index}_H #define COMPONENT{component_index}_H @@ -54,7 +51,7 @@ struct Interface{component_index} {{ """ return template.format(**locals()) -def _generate_component_source(component_index, deps, use_fruit_2_x_syntax): +def _generate_component_source(component_index, deps): include_directives = ''.join(['#include "component%s.h"\n' % index for index in deps + [component_index]]) fields = ''.join(['Interface%s& x%s;\n' % (dep, dep) @@ -67,12 +64,9 @@ def _generate_component_source(component_index, deps, use_fruit_2_x_syntax): if param_initializers: param_initializers = ': ' + param_initializers - if use_fruit_2_x_syntax: - install_expressions = ''.join([' .install(getComponent%s())\n' % dep for dep in deps]) - else: - install_expressions = ''.join([' .install(getComponent%s)\n' % dep for dep in deps]) + install_expressions = ''.join([' .install(getComponent%s)\n' % dep for dep in deps]) - component_type = _get_component_type(component_index, use_fruit_2_x_syntax) + component_type = _get_component_type(component_index) template = """ {include_directives} @@ -89,16 +83,7 @@ struct X{component_index} : public Interface{component_index} {{ """ - if use_fruit_2_x_syntax: - template += """ -{component_type} getComponent{component_index}() {{ - static {component_type} comp = fruit::createComponent(){install_expressions} - .bind(); - return comp; -}} -""" - else: - template += """ + template += """ {component_type} getComponent{component_index}() {{ return fruit::createComponent(){install_expressions} .bind(); @@ -107,74 +92,9 @@ struct X{component_index} : public Interface{component_index} {{ return template.format(**locals()) -def _generate_main(toplevel_component, use_fruit_2_x_syntax): - if use_fruit_2_x_syntax: - return _generate_main_with_fruit_2_x_syntax(toplevel_component) - else: - return _generate_main_with_fruit_3_x_syntax(toplevel_component) - -def _generate_main_with_fruit_2_x_syntax(toplevel_component): - template = """ -#include "component{toplevel_component}.h" - -#include -#include -#include -#include -#include - -using namespace std; - -int main(int argc, char* argv[]) {{ - if (argc != 2) {{ - std::cout << "Need to specify num_loops as argument." << std::endl; - exit(1); - }} - size_t num_loops = std::atoi(argv[1]); - double componentCreationTime = 0; - double componentNormalizationTime = 0; - std::chrono::high_resolution_clock::time_point start_time; - - for (size_t i = 0; i < 1 + num_loops/100; i++) {{ - start_time = std::chrono::high_resolution_clock::now(); - fruit::Component component(getComponent{toplevel_component}()); - componentCreationTime += std::chrono::duration_cast>(std::chrono::high_resolution_clock::now() - start_time).count(); - start_time = std::chrono::high_resolution_clock::now(); - fruit::NormalizedComponent normalizedComponent(std::move(component)); - componentNormalizationTime += std::chrono::duration_cast>(std::chrono::high_resolution_clock::now() - start_time).count(); - }} - - start_time = std::chrono::high_resolution_clock::now(); - for (size_t i = 0; i < 1 + num_loops/100; i++) {{ - fruit::Injector injector(getComponent{toplevel_component}()); - injector.get>(); - }} - double fullInjectionTime = std::chrono::duration_cast>(std::chrono::high_resolution_clock::now() - start_time).count(); - - // The cast to Component is needed for Fruit<2.1.0, where the constructor of - // NormalizedComponent only accepted a Component&&. - fruit::NormalizedComponent normalizedComponent{{fruit::Component{{getComponent{toplevel_component}()}}}}; - - start_time = std::chrono::high_resolution_clock::now(); - for (size_t i = 0; i < num_loops; i++) {{ - fruit::Injector injector(normalizedComponent, fruit::Component<>(fruit::createComponent())); - injector.get>(); - }} - double perRequestTime = std::chrono::duration_cast>(std::chrono::high_resolution_clock::now() - start_time).count(); - - std::cout << std::fixed; - std::cout << std::setprecision(15); - std::cout << "componentNormalizationTime = " << componentNormalizationTime * 100 / num_loops << std::endl; - std::cout << "Total for setup = " << (componentCreationTime + componentNormalizationTime) * 100 / num_loops << std::endl; - std::cout << "Full injection time = " << fullInjectionTime * 100 / num_loops << std::endl; - std::cout << "Total per request = " << perRequestTime / num_loops << std::endl; - return 0; -}} - """ - return template.format(**locals()) - -def _generate_main_with_fruit_3_x_syntax(toplevel_component): - template = """ +def _generate_main(toplevel_component, generate_runtime_bench_code): + if generate_runtime_bench_code: + template = """ #include "component{toplevel_component}.h" #include @@ -228,4 +148,15 @@ int main(int argc, char* argv[]) {{ return 0; }} """ + else: + template = """ +#include "component{toplevel_component}.h" + +int main(void) {{ + fruit::Injector injector(getComponent{toplevel_component}); + injector.get>(); + return 0; +}} + """ + return template.format(**locals()) diff --git a/extras/benchmark/generate_benchmark.py b/extras/benchmark/generate_benchmark.py index c1efe1b..6d72016 100755 --- a/extras/benchmark/generate_benchmark.py +++ b/extras/benchmark/generate_benchmark.py @@ -78,10 +78,10 @@ def generate_benchmark( num_components_with_no_deps, num_components_with_deps, num_deps, + generate_runtime_bench_code, fruit_build_dir=None, fruit_sources_dir=None, boost_di_sources_dir=None, - use_fruit_2_x_syntax=False, generate_debuginfo=False, use_new_delete=False, use_interfaces=False): @@ -104,18 +104,17 @@ def generate_benchmark( num_deps=num_deps) if di_library == 'fruit': - file_content_by_name = fruit_source_generator.generate_files(injection_graph, - use_fruit_2_x_syntax = use_fruit_2_x_syntax) + file_content_by_name = fruit_source_generator.generate_files(injection_graph, generate_runtime_bench_code) include_dirs = [fruit_build_dir + '/include', fruit_sources_dir + '/include'] library_dirs = [fruit_build_dir + '/src'] link_libraries = ['fruit'] elif di_library == 'boost_di': - file_content_by_name = boost_di_source_generator.generate_files(injection_graph) + file_content_by_name = boost_di_source_generator.generate_files(injection_graph, generate_runtime_bench_code) include_dirs = [boost_di_sources_dir + '/include', boost_di_sources_dir + '/extension/include'] library_dirs = [] link_libraries = [] elif di_library == 'none': - file_content_by_name = no_di_library_source_generator.generate_files(injection_graph, use_new_delete, use_interfaces) + file_content_by_name = no_di_library_source_generator.generate_files(injection_graph, use_new_delete, use_interfaces, generate_runtime_bench_code) include_dirs = [] library_dirs = [] link_libraries = [] @@ -127,8 +126,6 @@ def generate_benchmark( rpath_flags = ' '.join(['-Wl,-rpath,%s' % library_dir for library_dir in library_dirs]) link_libraries_flags = ' '.join(['-l%s' % library for library in link_libraries]) other_compile_flags = [] - if use_fruit_2_x_syntax: - other_compile_flags.append('-Wno-deprecated-declarations') if generate_debuginfo: other_compile_flags.append('-g') compile_command = '%s -std=%s -MMD -MP -O2 -W -Wall -Werror -DNDEBUG -ftemplate-depth=10000 %s %s' % (compiler, cxx_std, include_flags, ' '.join(other_compile_flags)) @@ -162,10 +159,10 @@ def main(): parser.add_argument('--output-dir', help='Output directory for generated files') parser.add_argument('--cxx-std', default='c++11', help='Version of the C++ standard to use. Typically one of \'c++11\' and \'c++14\'. (default: \'c++11\')') - parser.add_argument('--use-fruit-2-x-syntax', default=False, help='Set this to \'true\' to generate source files compatible with Fruit 2.x (instead of 3.x).') - parser.add_argument('--use-new-delete', default=False, help='Set this to \'true\' to use new/delete. Only relevant when --di_library=none.') - parser.add_argument('--use-interfaces', default=False, help='Set this to \'true\' to use interfaces. Only relevant when --di_library=none.') - parser.add_argument('--generate-debuginfo', default=False, help='Set this to \'true\' to generate debugging information (-g).') + parser.add_argument('--use-new-delete', default='false', help='Set this to \'true\' to use new/delete. Only relevant when --di_library=none.') + parser.add_argument('--use-interfaces', default='false', help='Set this to \'true\' to use interfaces. Only relevant when --di_library=none.') + parser.add_argument('--generate-runtime-bench-code', default='true', help='Set this to \'false\' for compile benchmarks.') + parser.add_argument('--generate-debuginfo', default='false', help='Set this to \'true\' to generate debugging information (-g).') args = parser.parse_args() @@ -203,10 +200,10 @@ def main(): num_components_with_no_deps=num_components_with_no_deps, fruit_build_dir=args.fruit_build_dir, num_deps=num_deps, - use_fruit_2_x_syntax=(args.use_fruit_2_x_syntax == 'true'), generate_debuginfo=(args.generate_debuginfo == 'true'), use_new_delete=(args.use_new_delete == 'true'), - use_interfaces=(args.use_interfaces == 'true')) + use_interfaces=(args.use_interfaces == 'true'), + generate_runtime_bench_code=(args.generate_runtime_bench_code == 'true')) if __name__ == "__main__": diff --git a/extras/benchmark/no_di_library_source_generator.py b/extras/benchmark/no_di_library_source_generator.py index a3df515..910a0c1 100644 --- a/extras/benchmark/no_di_library_source_generator.py +++ b/extras/benchmark/no_di_library_source_generator.py @@ -14,7 +14,7 @@ import itertools import networkx as nx -def generate_files(injection_graph, use_new_delete, use_interfaces): +def generate_files(injection_graph, use_new_delete, use_interfaces, generate_runtime_bench_code): file_content_by_name = dict() for node_id in injection_graph.nodes_iter(): @@ -27,7 +27,7 @@ def generate_files(injection_graph, use_new_delete, use_interfaces): file_content_by_name['class%s.h' % node_id] = _generate_class_header_without_interfaces(node_id, deps) file_content_by_name['class%s.cpp' % node_id] = _generate_class_cpp_file_without_interfaces(node_id, deps) - file_content_by_name['main.cpp'] = _generate_main(injection_graph, use_interfaces, use_new_delete) + file_content_by_name['main.cpp'] = _generate_main(injection_graph, use_interfaces, use_new_delete, generate_runtime_bench_code) return file_content_by_name @@ -145,7 +145,7 @@ Class{class_index}::Class{class_index}({constructor_params}) return template.format(**locals()) -def _generate_main(injection_graph, use_interfaces, use_new_delete): +def _generate_main(injection_graph, use_interfaces, use_new_delete, generate_runtime_bench_code): [toplevel_class_index] = [node_id for node_id in injection_graph.nodes_iter() if not injection_graph.predecessors(node_id)] @@ -173,7 +173,8 @@ def _generate_main(injection_graph, use_interfaces, use_new_delete): void_casts = ''.join('(void) x%s;\n' % index for index in injection_graph.nodes_iter()) - template = """ + if generate_runtime_bench_code: + template = """ {include_directives} #include @@ -211,5 +212,17 @@ int main(int argc, char* argv[]) {{ std::cout << "Total per request = " << fullInjectionTime * 100 / num_loops << std::endl; return 0; }} +""" + else: + template = """ +{include_directives} + +#include + +int main() {{ + {instance_creations} + {void_casts} + return 0; +}} """ return template.format(**locals()) diff --git a/extras/benchmark/run_benchmarks.py b/extras/benchmark/run_benchmarks.py index 41990df..0f4af67 100755 --- a/extras/benchmark/run_benchmarks.py +++ b/extras/benchmark/run_benchmarks.py @@ -187,15 +187,9 @@ class FruitSingleFileCompileTimeBenchmark: cxx_std = self.benchmark_definition['cxx_std'] num_bindings = self.benchmark_definition['num_bindings'] compiler_executable_name = self.benchmark_definition['compiler'] - benchmark_generation_flags = self.benchmark_definition['benchmark_generation_flags'] - - other_compile_flags = [] - if 'use_fruit_2_x_syntax' in benchmark_generation_flags: - other_compile_flags.append('-DUSE_FRUIT_2_X_SYNTAX=1') - other_compile_flags.append('-Wno-deprecated-declarations') run_command(compiler_executable_name, - args = compile_flags + other_compile_flags + [ + args = compile_flags + [ '-std=%s' % cxx_std, '-DMULTIPLIER=%s' % (num_bindings // 5), '-I', self.fruit_sources_dir + '/include', @@ -312,7 +306,8 @@ class GenericGeneratedSourcesBenchmark: class CompileTimeBenchmark(GenericGeneratedSourcesBenchmark): def __init__(self, **kwargs): - super().__init__(**kwargs) + super().__init__(generate_runtime_bench_code=False, + **kwargs) def prepare(self): self.prepare_compile_benchmark() @@ -322,7 +317,8 @@ class CompileTimeBenchmark(GenericGeneratedSourcesBenchmark): class IncrementalCompileTimeBenchmark(GenericGeneratedSourcesBenchmark): def __init__(self, **kwargs): - super().__init__(**kwargs) + super().__init__(generate_runtime_bench_code=False, + **kwargs) def prepare(self): self.prepare_incremental_compile_benchmark() @@ -332,7 +328,8 @@ class IncrementalCompileTimeBenchmark(GenericGeneratedSourcesBenchmark): class RunTimeBenchmark(GenericGeneratedSourcesBenchmark): def __init__(self, **kwargs): - super().__init__(**kwargs) + super().__init__(generate_runtime_bench_code=True, + **kwargs) def prepare(self): self.prepare_runtime_benchmark() @@ -343,7 +340,8 @@ class RunTimeBenchmark(GenericGeneratedSourcesBenchmark): # This is not really a 'benchmark', but we consider it as such to reuse the benchmark infrastructure. class ExecutableSizeBenchmark(GenericGeneratedSourcesBenchmark): def __init__(self, **kwargs): - super().__init__(**kwargs) + super().__init__(generate_runtime_bench_code=False, + **kwargs) def prepare(self): self.prepare_executable_size_benchmark() diff --git a/extras/benchmark/suites/debug.yml b/extras/benchmark/suites/debug.yml index 2b9d037..4746ef7 100644 --- a/extras/benchmark/suites/debug.yml +++ b/extras/benchmark/suites/debug.yml @@ -30,78 +30,77 @@ constants: - "clang++-4.0" benchmarks: -# - name: "fruit_single_file_compile_time" -# num_bindings: -# - 20 -# compiler: *compilers -# cxx_std: "c++11" -# additional_cmake_args: -# - [] -# benchmark_generation_flags: -# - [] -# #- ["use_fruit_2_x_syntax"] -# -# - name: -# - "new_delete_run_time" -# - "simple_di_compile_time" -# - "simple_di_incremental_compile_time" -# - "simple_di_run_time" -# - "simple_di_executable_size" -# - "simple_di_with_interfaces_compile_time" -# - "simple_di_with_interfaces_incremental_compile_time" -# - "simple_di_with_interfaces_run_time" -# - "simple_di_with_interfaces_executable_size" -# - "simple_di_with_interfaces_and_new_delete_compile_time" -# - "simple_di_with_interfaces_and_new_delete_incremental_compile_time" -# - "simple_di_with_interfaces_and_new_delete_run_time" -# - "simple_di_with_interfaces_and_new_delete_executable_size" -# loop_factor: 0.01 -# num_classes: -# - 100 -# compiler: *compilers -# cxx_std: "c++11" -# additional_cmake_args: -# - [] -# benchmark_generation_flags: -# - [] -# -# - name: -# - "fruit_compile_time" -# - "fruit_incremental_compile_time" -# - "fruit_run_time" -# - "fruit_executable_size" -# loop_factor: 0.01 -# num_classes: -# - 100 -# compiler: *gcc -# cxx_std: "c++11" -# additional_cmake_args: -# - [] -# - ['-DFRUIT_USES_BOOST=False'] -# - ["-DBUILD_SHARED_LIBS=False"] -# benchmark_generation_flags: -# - [] -# -# - name: -# - "fruit_compile_time" -# - "fruit_incremental_compile_time" -# - "fruit_run_time" -# - "fruit_executable_size" -# loop_factor: 0.01 -# num_classes: -# - 100 -# compiler: *clang -# cxx_std: "c++11" -# additional_cmake_args: -# - [] -# benchmark_generation_flags: -# - [] + - name: "fruit_single_file_compile_time" + num_bindings: + - 20 + compiler: *compilers + cxx_std: "c++11" + additional_cmake_args: + - [] + benchmark_generation_flags: + - [] + + - name: + - "new_delete_run_time" + - "simple_di_compile_time" + - "simple_di_incremental_compile_time" + - "simple_di_run_time" + - "simple_di_executable_size" + - "simple_di_with_interfaces_compile_time" + - "simple_di_with_interfaces_incremental_compile_time" + - "simple_di_with_interfaces_run_time" + - "simple_di_with_interfaces_executable_size" + - "simple_di_with_interfaces_and_new_delete_compile_time" + - "simple_di_with_interfaces_and_new_delete_incremental_compile_time" + - "simple_di_with_interfaces_and_new_delete_run_time" + - "simple_di_with_interfaces_and_new_delete_executable_size" + loop_factor: 0.01 + num_classes: + - 100 + compiler: *compilers + cxx_std: "c++11" + additional_cmake_args: + - [] + benchmark_generation_flags: + - [] + + - name: + - "fruit_compile_time" + - "fruit_incremental_compile_time" + - "fruit_run_time" + - "fruit_executable_size" + loop_factor: 0.01 + num_classes: + - 100 + compiler: *gcc + cxx_std: "c++11" + additional_cmake_args: + - [] + - ['-DFRUIT_USES_BOOST=False'] + - ["-DBUILD_SHARED_LIBS=False"] + benchmark_generation_flags: + - [] + + - name: + - "fruit_compile_time" + - "fruit_incremental_compile_time" + - "fruit_run_time" + - "fruit_executable_size" + loop_factor: 0.01 + num_classes: + - 100 + compiler: *clang + cxx_std: "c++11" + additional_cmake_args: + - [] + benchmark_generation_flags: + - [] - name: -# - "boost_di_compile_time" -# - "boost_di_incremental_compile_time" + - "boost_di_compile_time" + - "boost_di_incremental_compile_time" - "boost_di_run_time" -# - "boost_di_executable_size" + - "boost_di_executable_size" loop_factor: 0.01 num_classes: - 100 -- cgit v1.2.3