xref: /aosp_15_r20/external/google-fruit/extras/benchmark/generate_benchmark.py (revision a65addddcf69f38db5b288d787b6b7571a57bb8f)
1*a65addddSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*a65addddSAndroid Build Coastguard Worker#  Copyright 2016 Google Inc. All Rights Reserved.
3*a65addddSAndroid Build Coastguard Worker#
4*a65addddSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
5*a65addddSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
6*a65addddSAndroid Build Coastguard Worker# You may obtain a copy of the License at
7*a65addddSAndroid Build Coastguard Worker#
8*a65addddSAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
9*a65addddSAndroid Build Coastguard Worker#
10*a65addddSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
11*a65addddSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS-IS" BASIS,
12*a65addddSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*a65addddSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
14*a65addddSAndroid Build Coastguard Worker# limitations under the License.
15*a65addddSAndroid Build Coastguard Worker
16*a65addddSAndroid Build Coastguard Workerimport random
17*a65addddSAndroid Build Coastguard Workerimport os
18*a65addddSAndroid Build Coastguard Worker
19*a65addddSAndroid Build Coastguard Workerimport fruit_source_generator
20*a65addddSAndroid Build Coastguard Workerimport boost_di_source_generator
21*a65addddSAndroid Build Coastguard Workerimport no_di_library_source_generator
22*a65addddSAndroid Build Coastguard Workerfrom makefile_generator import generate_makefile
23*a65addddSAndroid Build Coastguard Workerimport argparse
24*a65addddSAndroid Build Coastguard Workerimport networkx as nx
25*a65addddSAndroid Build Coastguard Worker
26*a65addddSAndroid Build Coastguard Worker
27*a65addddSAndroid Build Coastguard Workerdef generate_injection_graph(num_components_with_no_deps: int,
28*a65addddSAndroid Build Coastguard Worker                             num_components_with_deps: int,
29*a65addddSAndroid Build Coastguard Worker                             num_deps: int):
30*a65addddSAndroid Build Coastguard Worker    injection_graph = nx.DiGraph()
31*a65addddSAndroid Build Coastguard Worker
32*a65addddSAndroid Build Coastguard Worker    num_used_ids = 0
33*a65addddSAndroid Build Coastguard Worker    is_toplevel = [True for i in range(0, num_components_with_no_deps + num_components_with_deps)]
34*a65addddSAndroid Build Coastguard Worker    toplevel_components = set()
35*a65addddSAndroid Build Coastguard Worker    for i in range(0, num_components_with_no_deps):
36*a65addddSAndroid Build Coastguard Worker        id = num_used_ids
37*a65addddSAndroid Build Coastguard Worker        num_used_ids += 1
38*a65addddSAndroid Build Coastguard Worker        toplevel_components.add(id)
39*a65addddSAndroid Build Coastguard Worker
40*a65addddSAndroid Build Coastguard Worker    # Then the rest have num_deps deps, chosen (pseudo-)randomly from the previous components.
41*a65addddSAndroid Build Coastguard Worker    # The last few components depend more components with >1 deps, so that the last component transitively depends on
42*a65addddSAndroid Build Coastguard Worker    # everything.
43*a65addddSAndroid Build Coastguard Worker    for i in range(0, num_components_with_deps):
44*a65addddSAndroid Build Coastguard Worker        deps = set()
45*a65addddSAndroid Build Coastguard Worker
46*a65addddSAndroid Build Coastguard Worker        if len(toplevel_components) > (num_components_with_deps - 1 - i) * (num_deps - 1):
47*a65addddSAndroid Build Coastguard Worker            # We need at least 1 dep with deps, otherwise the last few components will not be enough
48*a65addddSAndroid Build Coastguard Worker            # to tie together all components.
49*a65addddSAndroid Build Coastguard Worker            num_deps_with_deps = len(toplevel_components) - (num_components_with_deps - 1 - i) * (num_deps - 1)
50*a65addddSAndroid Build Coastguard Worker            deps |= set(random.sample(toplevel_components, num_deps_with_deps))
51*a65addddSAndroid Build Coastguard Worker
52*a65addddSAndroid Build Coastguard Worker        # Add other deps to get to the desired num_deps.
53*a65addddSAndroid Build Coastguard Worker        deps |= set(random.sample(range(0, num_components_with_no_deps + i), num_deps - len(deps)))
54*a65addddSAndroid Build Coastguard Worker
55*a65addddSAndroid Build Coastguard Worker        toplevel_components -= deps
56*a65addddSAndroid Build Coastguard Worker        for dep in deps:
57*a65addddSAndroid Build Coastguard Worker            is_toplevel[dep] = False
58*a65addddSAndroid Build Coastguard Worker
59*a65addddSAndroid Build Coastguard Worker        component_id = num_used_ids
60*a65addddSAndroid Build Coastguard Worker        toplevel_components |= {component_id}
61*a65addddSAndroid Build Coastguard Worker        num_used_ids += 1
62*a65addddSAndroid Build Coastguard Worker        deps_list = list(deps)
63*a65addddSAndroid Build Coastguard Worker        random.shuffle(deps_list)
64*a65addddSAndroid Build Coastguard Worker        for dep in deps_list:
65*a65addddSAndroid Build Coastguard Worker            injection_graph.add_edge(component_id, dep)
66*a65addddSAndroid Build Coastguard Worker
67*a65addddSAndroid Build Coastguard Worker    assert len(toplevel_components) == 1, toplevel_components
68*a65addddSAndroid Build Coastguard Worker    toplevel_component = num_used_ids - 1
69*a65addddSAndroid Build Coastguard Worker    assert is_toplevel[toplevel_component]
70*a65addddSAndroid Build Coastguard Worker
71*a65addddSAndroid Build Coastguard Worker    return injection_graph
72*a65addddSAndroid Build Coastguard Worker
73*a65addddSAndroid Build Coastguard Workerdef generate_benchmark(
74*a65addddSAndroid Build Coastguard Worker        di_library: str,
75*a65addddSAndroid Build Coastguard Worker        compiler: str,
76*a65addddSAndroid Build Coastguard Worker        cxx_std: str,
77*a65addddSAndroid Build Coastguard Worker        output_dir: str,
78*a65addddSAndroid Build Coastguard Worker        num_components_with_no_deps: int,
79*a65addddSAndroid Build Coastguard Worker        num_components_with_deps: int,
80*a65addddSAndroid Build Coastguard Worker        num_deps: int,
81*a65addddSAndroid Build Coastguard Worker        generate_runtime_bench_code: bool,
82*a65addddSAndroid Build Coastguard Worker        use_exceptions: bool=True,
83*a65addddSAndroid Build Coastguard Worker        use_rtti: bool=True,
84*a65addddSAndroid Build Coastguard Worker        fruit_build_dir: str=None,
85*a65addddSAndroid Build Coastguard Worker        fruit_sources_dir: str=None,
86*a65addddSAndroid Build Coastguard Worker        boost_di_sources_dir: str=None,
87*a65addddSAndroid Build Coastguard Worker        generate_debuginfo: bool=False,
88*a65addddSAndroid Build Coastguard Worker        use_new_delete: bool=False,
89*a65addddSAndroid Build Coastguard Worker        use_interfaces: bool=False,
90*a65addddSAndroid Build Coastguard Worker        use_normalized_component: bool=False):
91*a65addddSAndroid Build Coastguard Worker    """Generates a sample codebase using the specified DI library, meant for benchmarking.
92*a65addddSAndroid Build Coastguard Worker
93*a65addddSAndroid Build Coastguard Worker    :param boost_di_sources_dir: this is only used if di_library=='boost_di', it can be None otherwise.
94*a65addddSAndroid Build Coastguard Worker    """
95*a65addddSAndroid Build Coastguard Worker
96*a65addddSAndroid Build Coastguard Worker    if num_components_with_no_deps < num_deps:
97*a65addddSAndroid Build Coastguard Worker        raise Exception(
98*a65addddSAndroid Build Coastguard Worker            "Too few components with no deps. num_components_with_no_deps=%s but num_deps=%s." % (num_components_with_no_deps, num_deps))
99*a65addddSAndroid Build Coastguard Worker    if num_deps < 2:
100*a65addddSAndroid Build Coastguard Worker        raise Exception("num_deps should be at least 2.")
101*a65addddSAndroid Build Coastguard Worker
102*a65addddSAndroid Build Coastguard Worker    # This is a constant so that we always generate the same file (=> benchmark more repeatable).
103*a65addddSAndroid Build Coastguard Worker    random.seed(42)
104*a65addddSAndroid Build Coastguard Worker
105*a65addddSAndroid Build Coastguard Worker    injection_graph = generate_injection_graph(num_components_with_no_deps=num_components_with_no_deps,
106*a65addddSAndroid Build Coastguard Worker                                               num_components_with_deps=num_components_with_deps,
107*a65addddSAndroid Build Coastguard Worker                                               num_deps=num_deps)
108*a65addddSAndroid Build Coastguard Worker
109*a65addddSAndroid Build Coastguard Worker    if di_library == 'fruit':
110*a65addddSAndroid Build Coastguard Worker        file_content_by_name = fruit_source_generator.generate_files(injection_graph, generate_runtime_bench_code)
111*a65addddSAndroid Build Coastguard Worker        include_dirs = [fruit_build_dir + '/include', fruit_sources_dir + '/include']
112*a65addddSAndroid Build Coastguard Worker        library_dirs = [fruit_build_dir + '/src']
113*a65addddSAndroid Build Coastguard Worker        link_libraries = ['fruit']
114*a65addddSAndroid Build Coastguard Worker    elif di_library == 'boost_di':
115*a65addddSAndroid Build Coastguard Worker        file_content_by_name = boost_di_source_generator.generate_files(injection_graph, generate_runtime_bench_code)
116*a65addddSAndroid Build Coastguard Worker        include_dirs = [boost_di_sources_dir + '/include', boost_di_sources_dir + '/extension/include']
117*a65addddSAndroid Build Coastguard Worker        library_dirs = []
118*a65addddSAndroid Build Coastguard Worker        link_libraries = []
119*a65addddSAndroid Build Coastguard Worker    elif di_library == 'none':
120*a65addddSAndroid Build Coastguard Worker        file_content_by_name = no_di_library_source_generator.generate_files(injection_graph, use_new_delete, use_interfaces, generate_runtime_bench_code)
121*a65addddSAndroid Build Coastguard Worker        include_dirs = []
122*a65addddSAndroid Build Coastguard Worker        library_dirs = []
123*a65addddSAndroid Build Coastguard Worker        link_libraries = []
124*a65addddSAndroid Build Coastguard Worker    else:
125*a65addddSAndroid Build Coastguard Worker        raise Exception('Unrecognized di_library: %s' % di_library)
126*a65addddSAndroid Build Coastguard Worker
127*a65addddSAndroid Build Coastguard Worker    include_flags = ' '.join(['-I%s' % include_dir for include_dir in include_dirs])
128*a65addddSAndroid Build Coastguard Worker    library_dirs_flags = ' '.join(['-L%s' % library_dir for library_dir in library_dirs])
129*a65addddSAndroid Build Coastguard Worker    rpath_flags = ' '.join(['-Wl,-rpath,%s' % library_dir for library_dir in library_dirs])
130*a65addddSAndroid Build Coastguard Worker    link_libraries_flags = ' '.join(['-l%s' % library for library in link_libraries])
131*a65addddSAndroid Build Coastguard Worker    other_compile_flags = []
132*a65addddSAndroid Build Coastguard Worker    if generate_debuginfo:
133*a65addddSAndroid Build Coastguard Worker        other_compile_flags.append('-g')
134*a65addddSAndroid Build Coastguard Worker    if not use_exceptions:
135*a65addddSAndroid Build Coastguard Worker        other_compile_flags.append('-fno-exceptions')
136*a65addddSAndroid Build Coastguard Worker    if not use_rtti:
137*a65addddSAndroid Build Coastguard Worker        other_compile_flags.append('-fno-rtti')
138*a65addddSAndroid Build Coastguard Worker    compile_command = '%s -std=%s -MMD -MP -O2 -W -Wall -DNDEBUG -ftemplate-depth=10000 %s %s' % (compiler, cxx_std, include_flags, ' '.join(other_compile_flags))
139*a65addddSAndroid Build Coastguard Worker    link_command = '%s -std=%s -O2 -W -Wall %s %s' % (compiler, cxx_std, rpath_flags, library_dirs_flags)
140*a65addddSAndroid Build Coastguard Worker    # GCC requires passing the -lfruit flag *after* all object files to be linked for some reason.
141*a65addddSAndroid Build Coastguard Worker    link_command_suffix = link_libraries_flags
142*a65addddSAndroid Build Coastguard Worker
143*a65addddSAndroid Build Coastguard Worker    cpp_files = [file_name
144*a65addddSAndroid Build Coastguard Worker                 for file_name in file_content_by_name.keys()
145*a65addddSAndroid Build Coastguard Worker                 if file_name.endswith('.cpp')]
146*a65addddSAndroid Build Coastguard Worker
147*a65addddSAndroid Build Coastguard Worker    file_content_by_name['Makefile'] = generate_makefile(cpp_files, 'main', compile_command, link_command, link_command_suffix)
148*a65addddSAndroid Build Coastguard Worker
149*a65addddSAndroid Build Coastguard Worker    os.makedirs(output_dir, exist_ok=True)
150*a65addddSAndroid Build Coastguard Worker    for file_name, file_content in file_content_by_name.items():
151*a65addddSAndroid Build Coastguard Worker        with open('%s/%s' % (output_dir, file_name), 'w') as file:
152*a65addddSAndroid Build Coastguard Worker            file.write(file_content)
153*a65addddSAndroid Build Coastguard Worker
154*a65addddSAndroid Build Coastguard Worker    return file_content_by_name.keys()
155*a65addddSAndroid Build Coastguard Worker
156*a65addddSAndroid Build Coastguard Workerdef main():
157*a65addddSAndroid Build Coastguard Worker    parser = argparse.ArgumentParser(description='Generates source files and a build script for benchmarks.')
158*a65addddSAndroid Build Coastguard Worker    parser.add_argument('--di-library', default='fruit', help='DI library to use. One of {fruit, boost_di, none}. (default: fruit)')
159*a65addddSAndroid Build Coastguard Worker    parser.add_argument('--compiler', help='Compiler to use')
160*a65addddSAndroid Build Coastguard Worker    parser.add_argument('--fruit-sources-dir', help='Path to the fruit sources (only used when di_library==\'fruit\')')
161*a65addddSAndroid Build Coastguard Worker    parser.add_argument('--fruit-build-dir', help='Path to the fruit build dir (only used with --di_library=\'fruit\')')
162*a65addddSAndroid Build Coastguard Worker    parser.add_argument('--boost-di-sources-dir', help='Path to the Boost.DI sources (only used with --di-library==\'boost_di\')')
163*a65addddSAndroid Build Coastguard Worker    parser.add_argument('--num-components-with-no-deps', default=10, help='Number of components with no deps that will be generated')
164*a65addddSAndroid Build Coastguard Worker    parser.add_argument('--num-components-with-deps', default=90, help='Number of components with deps that will be generated')
165*a65addddSAndroid Build Coastguard Worker    parser.add_argument('--num-deps', default=10, help='Number of deps in each component with deps that will be generated')
166*a65addddSAndroid Build Coastguard Worker    parser.add_argument('--output-dir', help='Output directory for generated files')
167*a65addddSAndroid Build Coastguard Worker    parser.add_argument('--cxx-std', default='c++11',
168*a65addddSAndroid Build Coastguard Worker                        help='Version of the C++ standard to use. Typically one of \'c++11\' and \'c++14\'. (default: \'c++11\')')
169*a65addddSAndroid Build Coastguard Worker    parser.add_argument('--use-new-delete', default='false', help='Set this to \'true\' to use new/delete. Only relevant when --di_library=none.')
170*a65addddSAndroid Build Coastguard Worker    parser.add_argument('--use-interfaces', default='false', help='Set this to \'true\' to use interfaces. Only relevant when --di_library=none.')
171*a65addddSAndroid Build Coastguard Worker    parser.add_argument('--use-normalized-component', default='false', help='Set this to \'true\' to create a NormalizedComponent and create the injector from that. Only relevant when --di_library=fruit and --generate-runtime-bench-code=false.')
172*a65addddSAndroid Build Coastguard Worker    parser.add_argument('--generate-runtime-bench-code', default='true', help='Set this to \'false\' for compile benchmarks.')
173*a65addddSAndroid Build Coastguard Worker    parser.add_argument('--generate-debuginfo', default='false', help='Set this to \'true\' to generate debugging information (-g).')
174*a65addddSAndroid Build Coastguard Worker    parser.add_argument('--use-exceptions', default='true', help='Set this to \'false\' to disable exceptions.')
175*a65addddSAndroid Build Coastguard Worker    parser.add_argument('--use-rtti', default='true', help='Set this to \'false\' to disable RTTI.')
176*a65addddSAndroid Build Coastguard Worker
177*a65addddSAndroid Build Coastguard Worker    args = parser.parse_args()
178*a65addddSAndroid Build Coastguard Worker
179*a65addddSAndroid Build Coastguard Worker    if args.compiler is None:
180*a65addddSAndroid Build Coastguard Worker        raise Exception('--compiler is required.')
181*a65addddSAndroid Build Coastguard Worker
182*a65addddSAndroid Build Coastguard Worker    if args.di_library == 'fruit':
183*a65addddSAndroid Build Coastguard Worker        if args.fruit_sources_dir is None:
184*a65addddSAndroid Build Coastguard Worker            raise Exception('--fruit-sources-dir is required with --di-library=\'fruit\'.')
185*a65addddSAndroid Build Coastguard Worker        if args.fruit_build_dir is None:
186*a65addddSAndroid Build Coastguard Worker            raise Exception('--fruit-build-dir is required with --di-library=\'fruit\'.')
187*a65addddSAndroid Build Coastguard Worker    elif args.di_library == 'boost_di':
188*a65addddSAndroid Build Coastguard Worker        if args.boost_di_sources_dir is None:
189*a65addddSAndroid Build Coastguard Worker            raise Exception('--boost-di-sources-dir is required with --di-library=\'boost_di\'.')
190*a65addddSAndroid Build Coastguard Worker    elif args.di_library == 'none':
191*a65addddSAndroid Build Coastguard Worker        pass
192*a65addddSAndroid Build Coastguard Worker    else:
193*a65addddSAndroid Build Coastguard Worker        raise Exception('Unrecognized --di-library: \'%s\'. Allowed values are %s' % (args.di_library, {'fruit', 'boost_di', 'none'}))
194*a65addddSAndroid Build Coastguard Worker
195*a65addddSAndroid Build Coastguard Worker    num_components_with_deps = int(args.num_components_with_deps)
196*a65addddSAndroid Build Coastguard Worker    num_components_with_no_deps = int(args.num_components_with_no_deps)
197*a65addddSAndroid Build Coastguard Worker    num_deps = int(args.num_deps)
198*a65addddSAndroid Build Coastguard Worker
199*a65addddSAndroid Build Coastguard Worker    if args.output_dir is None:
200*a65addddSAndroid Build Coastguard Worker        raise Exception("output_dir must be specified.")
201*a65addddSAndroid Build Coastguard Worker
202*a65addddSAndroid Build Coastguard Worker    generate_benchmark(
203*a65addddSAndroid Build Coastguard Worker        di_library=args.di_library,
204*a65addddSAndroid Build Coastguard Worker        fruit_sources_dir=args.fruit_sources_dir,
205*a65addddSAndroid Build Coastguard Worker        boost_di_sources_dir=args.boost_di_sources_dir,
206*a65addddSAndroid Build Coastguard Worker        output_dir=args.output_dir,
207*a65addddSAndroid Build Coastguard Worker        compiler=args.compiler,
208*a65addddSAndroid Build Coastguard Worker        cxx_std=args.cxx_std,
209*a65addddSAndroid Build Coastguard Worker        num_components_with_deps=num_components_with_deps,
210*a65addddSAndroid Build Coastguard Worker        num_components_with_no_deps=num_components_with_no_deps,
211*a65addddSAndroid Build Coastguard Worker        fruit_build_dir=args.fruit_build_dir,
212*a65addddSAndroid Build Coastguard Worker        num_deps=num_deps,
213*a65addddSAndroid Build Coastguard Worker        generate_debuginfo=(args.generate_debuginfo == 'true'),
214*a65addddSAndroid Build Coastguard Worker        use_new_delete=(args.use_new_delete == 'true'),
215*a65addddSAndroid Build Coastguard Worker        use_interfaces=(args.use_interfaces == 'true'),
216*a65addddSAndroid Build Coastguard Worker        use_normalized_component=(args.use_normalized_component == 'true'),
217*a65addddSAndroid Build Coastguard Worker        generate_runtime_bench_code=(args.generate_runtime_bench_code == 'true'),
218*a65addddSAndroid Build Coastguard Worker        use_exceptions=(args.use_exceptions == 'true'),
219*a65addddSAndroid Build Coastguard Worker        use_rtti=(args.use_rtti == 'true'))
220*a65addddSAndroid Build Coastguard Worker
221*a65addddSAndroid Build Coastguard Worker
222*a65addddSAndroid Build Coastguard Workerif __name__ == "__main__":
223*a65addddSAndroid Build Coastguard Worker    main()
224