Skip to content

Commit 119948f

Browse files
guludotridge
authored andcommitted
waf: add gbenchmark Waf tool
1 parent 1dfd8bc commit 119948f

File tree

2 files changed

+182
-0
lines changed

2 files changed

+182
-0
lines changed

Tools/ardupilotwaf/gbenchmark.py

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
#!/usr/bin/env python
2+
# encoding: utf-8
3+
4+
"""
5+
gbenchmark is a Waf tool for benchmark builds in Ardupilot
6+
"""
7+
8+
from waflib import Build, Context, Task
9+
from waflib.TaskGen import feature, before_method, after_method
10+
11+
def configure(cfg):
12+
env = cfg.env
13+
env.HAS_GBENCHMARK = False
14+
15+
cfg.start_msg('Checking for gbenchmark submodule')
16+
cmake_lists = cfg.srcnode.find_resource('modules/gbenchmark/CMakeLists.txt')
17+
if not cmake_lists:
18+
cfg.end_msg('not initialized', color='YELLOW')
19+
return
20+
cfg.end_msg('yes')
21+
22+
cfg.find_program('cmake', mandatory=False)
23+
24+
if not env.CMAKE:
25+
return
26+
27+
env.GBENCHMARK_CMAKE_GENERATOR = None
28+
29+
cfg.find_program('ninja', mandatory=False)
30+
if not env.NINJA:
31+
cfg.find_program('ninja-build', var='NINJA', mandatory=False)
32+
33+
if env.NINJA:
34+
env.GBENCHMARK_CMAKE_GENERATOR = 'Ninja'
35+
36+
env.GBENCHMARK_GENERATOR_OPTION = ''
37+
if env.GBENCHMARK_CMAKE_GENERATOR:
38+
env.GBENCHMARK_GENERATOR_OPTION = '-G%s' % env.GBENCHMARK_CMAKE_GENERATOR
39+
40+
prefix_node = cfg.bldnode.make_node('gbenchmark')
41+
my_build_node = cfg.bldnode.make_node('gbenchmark_build')
42+
my_src_node = cfg.srcnode.find_dir('modules/gbenchmark')
43+
44+
env.GBENCHMARK_PREFIX_REL = prefix_node.path_from(cfg.bldnode)
45+
env.GBENCHMARK_BUILD = my_build_node.abspath()
46+
env.GBENCHMARK_BUILD_REL = my_build_node.path_from(cfg.bldnode)
47+
env.GBENCHMARK_SRC = my_src_node.abspath()
48+
49+
env.INCLUDES_GBENCHMARK = [prefix_node.make_node('include').abspath()]
50+
env.LIBPATH_GBENCHMARK = [prefix_node.make_node('lib').abspath()]
51+
env.LIB_GBENCHMARK = ['benchmark']
52+
53+
env.HAS_GBENCHMARK = True
54+
55+
class gbenchmark_build(Task.Task):
56+
def __init__(self, *k, **kw):
57+
super(gbenchmark_build, self).__init__(*k, **kw)
58+
59+
bldnode = self.generator.bld.bldnode
60+
output_list = [
61+
'%s/%s' % (self.env.GBENCHMARK_PREFIX_REL, path)
62+
for path in (
63+
'include/benchmark/benchmark.h',
64+
'include/benchmark/macros.h',
65+
'include/benchmark/benchmark_api.h',
66+
'include/benchmark/reporter.h',
67+
'lib/libbenchmark.a',
68+
)
69+
]
70+
self.outputs.extend([bldnode.make_node(f) for f in output_list])
71+
72+
def run(self):
73+
bld = self.generator.bld
74+
cmds = []
75+
76+
cmake_lists = bld.srcnode.find_resource('modules/gbenchmark/CMakeLists.txt')
77+
if not cmake_lists:
78+
bld.fatal('Submodule gbenchmark not initialized, please run configure again')
79+
80+
# Generate build system first, if necessary
81+
my_build_node = bld.bldnode.find_dir(self.env.GBENCHMARK_BUILD_REL)
82+
if not (my_build_node and my_build_node.find_resource('CMakeCache.txt')):
83+
if not my_build_node:
84+
bld.bldnode.make_node(self.env.GBENCHMARK_BUILD_REL).mkdir()
85+
86+
cmds.append('%s %s -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX:PATH=%s %s' % (
87+
self.env.CMAKE[0],
88+
self.env.GBENCHMARK_SRC,
89+
bld.bldnode.make_node(self.env.GBENCHMARK_PREFIX_REL).abspath(),
90+
self.env.GBENCHMARK_GENERATOR_OPTION
91+
))
92+
93+
cmds.append('%s --build %s --target install' % (
94+
self.env.CMAKE[0],
95+
self.env.GBENCHMARK_BUILD
96+
))
97+
try:
98+
for cmd in cmds:
99+
bld.cmd_and_log(
100+
cmd,
101+
cwd=self.env.GBENCHMARK_BUILD,
102+
quiet=Context.BOTH,
103+
)
104+
return 0
105+
except Exception as e:
106+
print(e.stdout, e.stderr)
107+
return 1
108+
109+
def __str__(self):
110+
return 'Google Benchmark'
111+
112+
gbenchmark_build = Task.always_run(Task.update_outputs(gbenchmark_build))
113+
114+
build_task = None
115+
116+
@feature('gbenchmark')
117+
@before_method('process_use')
118+
def append_gbenchmark_use(self):
119+
self.use = self.to_list(getattr(self, 'use', []))
120+
if 'GBENCHMARK' not in self.use:
121+
self.use.append('GBENCHMARK')
122+
123+
@feature('gbenchmark')
124+
@after_method('process_source')
125+
def wait_for_gbenchmark_build(self):
126+
global build_task
127+
128+
if not build_task:
129+
build_task = self.create_task('gbenchmark_build')
130+
131+
for task in self.compiled_tasks:
132+
task.set_run_after(build_task)
133+
task.dep_nodes.extend(build_task.outputs)

benchmarks/AP_gbenchmark.h

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Utility header for benchmarks with Google Benchmark.
3+
*/
4+
#include <benchmark/benchmark.h>
5+
6+
/* The two functions below are an approach proposed by Chandler Carruth in
7+
* CPPCON 2015: CppCon 2015: "Tuning C++: Benchmarks, and CPUs, and Compilers!
8+
* Oh My!" in order keep the compiler from optimizing the use of a variable
9+
* (gbenchmark_escape) or whole memory (gbenchmark_clobber).
10+
*
11+
* The compiler optimizer may sometimes remove code when it sees it isn't
12+
* necessary. For example, when a variable isn't used, the optimizer removes
13+
* the code that computes the value for that variable - that's not good for
14+
* benchmarks. The function gbenchmark_escape(void *p) makes the compiler think
15+
* that that p is being used in a code that might have "unknowable side
16+
* effects", which keeps it from removing the variable. The "side effects" in
17+
* the case here would be the benchmark numbers.
18+
*
19+
* Here is an example that would give wrong benchmark values:
20+
*
21+
* static void BM_Test(benchmark::State& state)
22+
* {
23+
* while (state.KeepRunning()) {
24+
* float a = expensive_operation();
25+
* }
26+
* }
27+
*
28+
* Since variable a isn't used, the call to expensive_operation() is removed
29+
* from the compiled program. The benchmark would show that
30+
* expensive_operation() is extremely fast. The following code would fix that:
31+
*
32+
* static void BM_Test(benchmark::State& state)
33+
* {
34+
* while (state.KeepRunning()) {
35+
* float a = expensive_operation();
36+
* gbenchmark_escape(&a);
37+
* }
38+
* }
39+
*/
40+
41+
inline void gbenchmark_escape(void* p)
42+
{
43+
asm volatile("" : : "g"(p) : "memory");
44+
}
45+
46+
inline void gbenchmark_clobber()
47+
{
48+
asm volatile("" : : : "memory");
49+
}

0 commit comments

Comments
 (0)