Skip to content

Commit c6f3f0e

Browse files
authoredSep 6, 2016
Cleanup RunBenchmark code. (google#289)
* Cleanup the code for generating and running benchmarks * Rework calculation of real/manual time * Add back TSAN builder
1 parent d038472 commit c6f3f0e

File tree

4 files changed

+104
-93
lines changed

4 files changed

+104
-93
lines changed
 

‎.travis.yml

+10
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,16 @@ matrix:
6565
- COMPILER=clang++-3.8 C_COMPILER=clang-3.8 BUILD_TYPE=Debug
6666
- LIBCXX_BUILD=1 LIBCXX_SANITIZER=MemoryWithOrigins
6767
- EXTRA_FLAGS="-stdlib=libc++ -g -O2 -fno-omit-frame-pointer -fsanitize=memory -fsanitize-memory-track-origins"
68+
# Clang w/ libc++ and MSAN
69+
- compiler: clang
70+
addons:
71+
apt:
72+
packages:
73+
clang-3.8
74+
env:
75+
- COMPILER=clang++-3.8 C_COMPILER=clang-3.8 BUILD_TYPE=RelWithDebInfo
76+
- LIBCXX_BUILD=1 LIBCXX_SANITIZER=Thread
77+
- EXTRA_FLAGS="-stdlib=libc++ -g -O2 -fno-omit-frame-pointer -fsanitize=thread -fno-sanitize-recover=all"
6878

6979
before_script:
7080
- if [ -n "${LIBCXX_BUILD}" ]; then

‎src/benchmark.cc

+93-90
Original file line numberDiff line numberDiff line change
@@ -131,15 +131,18 @@ class ThreadManager {
131131
}
132132

133133
public:
134-
GUARDED_BY(GetBenchmarkMutex()) double real_time_used = 0;
135-
GUARDED_BY(GetBenchmarkMutex()) double cpu_time_used = 0;
136-
GUARDED_BY(GetBenchmarkMutex()) double manual_time_used = 0;
137-
GUARDED_BY(GetBenchmarkMutex()) int64_t bytes_processed = 0;
138-
GUARDED_BY(GetBenchmarkMutex()) int64_t items_processed = 0;
139-
GUARDED_BY(GetBenchmarkMutex()) int complexity_n = 0;
140-
GUARDED_BY(GetBenchmarkMutex()) std::string report_label_;
141-
GUARDED_BY(GetBenchmarkMutex()) std::string error_message_;
142-
GUARDED_BY(GetBenchmarkMutex()) bool has_error_ = false;
134+
struct Result {
135+
double real_time_used = 0;
136+
double cpu_time_used = 0;
137+
double manual_time_used = 0;
138+
int64_t bytes_processed = 0;
139+
int64_t items_processed = 0;
140+
int complexity_n = 0;
141+
std::string report_label_;
142+
std::string error_message_;
143+
bool has_error_ = false;
144+
};
145+
GUARDED_BY(GetBenchmarkMutex()) Result results;
143146

144147
private:
145148
mutable Mutex benchmark_mutex_;
@@ -211,6 +214,47 @@ class ThreadTimer {
211214

212215
namespace {
213216

217+
BenchmarkReporter::Run
218+
CreateRunReport(const benchmark::internal::Benchmark::Instance& b,
219+
const internal::ThreadManager::Result& results,
220+
size_t iters, double seconds)
221+
{
222+
// Create report about this benchmark run.
223+
BenchmarkReporter::Run report;
224+
225+
report.benchmark_name = b.name;
226+
report.error_occurred = results.has_error_;
227+
report.error_message = results.error_message_;
228+
report.report_label = results.report_label_;
229+
// Report the total iterations across all threads.
230+
report.iterations = static_cast<int64_t>(iters) * b.threads;
231+
report.time_unit = b.time_unit;
232+
233+
if (!report.error_occurred) {
234+
double bytes_per_second = 0;
235+
if (results.bytes_processed > 0 && seconds > 0.0) {
236+
bytes_per_second = (results.bytes_processed / seconds);
237+
}
238+
double items_per_second = 0;
239+
if (results.items_processed > 0 && seconds > 0.0) {
240+
items_per_second = (results.items_processed / seconds);
241+
}
242+
243+
if (b.use_manual_time) {
244+
report.real_accumulated_time = results.manual_time_used;
245+
} else {
246+
report.real_accumulated_time = results.real_time_used;
247+
}
248+
report.cpu_accumulated_time = results.cpu_time_used;
249+
report.bytes_per_second = bytes_per_second;
250+
report.items_per_second = items_per_second;
251+
report.complexity_n = results.complexity_n;
252+
report.complexity = b.complexity;
253+
report.complexity_lambda = b.complexity_lambda;
254+
}
255+
return report;
256+
}
257+
214258
// Execute one thread of benchmark b for the specified number of iterations.
215259
// Adds the stats collected for the thread into *total.
216260
void RunInThread(const benchmark::internal::Benchmark::Instance* b,
@@ -223,12 +267,13 @@ void RunInThread(const benchmark::internal::Benchmark::Instance* b,
223267
"Benchmark returned before State::KeepRunning() returned false!";
224268
{
225269
MutexLock l(manager->GetBenchmarkMutex());
226-
manager->cpu_time_used += timer.cpu_time_used();
227-
manager->real_time_used += timer.real_time_used();
228-
manager->manual_time_used += timer.manual_time_used();
229-
manager->bytes_processed += st.bytes_processed();
230-
manager->items_processed += st.items_processed();
231-
manager->complexity_n += st.complexity_length_n();
270+
internal::ThreadManager::Result& results = manager->results;
271+
results.cpu_time_used += timer.cpu_time_used();
272+
results.real_time_used += timer.real_time_used();
273+
results.manual_time_used += timer.manual_time_used();
274+
results.bytes_processed += st.bytes_processed();
275+
results.items_processed += st.items_processed();
276+
results.complexity_n += st.complexity_length_n();
232277
}
233278
manager->NotifyThreadComplete();
234279
}
@@ -239,96 +284,58 @@ std::vector<BenchmarkReporter::Run> RunBenchmark(
239284
std::vector<BenchmarkReporter::Run> reports; // return value
240285

241286
size_t iters = 1;
242-
const int num_threads = b.multithreaded ? b.threads : 1;
243-
std::vector<std::thread> pool;
244-
if (num_threads > 1) pool.resize(num_threads -1);
245-
287+
std::unique_ptr<internal::ThreadManager> manager;
288+
std::vector<std::thread> pool(b.threads - 1);
246289
const int repeats = b.repetitions != 0 ? b.repetitions
247290
: FLAGS_benchmark_repetitions;
248291
const bool report_aggregates_only = repeats != 1 &&
249292
(b.report_mode == internal::RM_Unspecified
250293
? FLAGS_benchmark_report_aggregates_only
251294
: b.report_mode == internal::RM_ReportAggregatesOnly);
252295
for (int i = 0; i < repeats; i++) {
253-
std::string mem;
254296
for (;;) {
255297
// Try benchmark
256298
VLOG(2) << "Running " << b.name << " for " << iters << "\n";
257299

258-
internal::ThreadManager manager(num_threads);
259-
if (b.multithreaded) {
260-
// If this is out first iteration of the while(true) loop then the
261-
// threads haven't been started and can't be joined. Otherwise we need
262-
// to join the thread before replacing them.
263-
for (std::thread& thread : pool) {
264-
if (thread.joinable())
265-
thread.join();
266-
}
267-
for (std::size_t ti = 0; ti < pool.size(); ++ti) {
268-
pool[ti] = std::thread(&RunInThread, &b, iters,
269-
static_cast<int>(ti + 1), &manager);
270-
}
300+
manager.reset(new internal::ThreadManager(b.threads));
301+
for (std::size_t ti = 0; ti < pool.size(); ++ti) {
302+
pool[ti] = std::thread(&RunInThread, &b, iters,
303+
static_cast<int>(ti + 1), manager.get());
271304
}
272-
RunInThread(&b, iters, 0, &manager);
273-
manager.WaitForAllThreads();
274-
MutexLock l(manager.GetBenchmarkMutex());
275-
276-
const double cpu_accumulated_time = manager.cpu_time_used;
277-
const double real_accumulated_time = manager.real_time_used / num_threads;
278-
const double manual_accumulated_time = manager.manual_time_used / num_threads;
305+
RunInThread(&b, iters, 0, manager.get());
306+
manager->WaitForAllThreads();
307+
for (std::thread& thread : pool)
308+
thread.join();
309+
internal::ThreadManager::Result results;
310+
{
311+
MutexLock l(manager->GetBenchmarkMutex());
312+
results = manager->results;
313+
}
314+
manager.reset();
315+
// Adjust real/manual time stats since they were reported per thread.
316+
results.real_time_used /= b.threads;
317+
results.manual_time_used /= b.threads;
279318

280-
VLOG(2) << "Ran in " << cpu_accumulated_time << "/"
281-
<< real_accumulated_time << "\n";
319+
VLOG(2) << "Ran in " << results.cpu_time_used << "/"
320+
<< results.real_time_used << "\n";
282321

283322
// Base decisions off of real time if requested by this benchmark.
284-
double seconds = cpu_accumulated_time;
323+
double seconds = results.cpu_time_used;
285324
if (b.use_manual_time) {
286-
seconds = manual_accumulated_time;
325+
seconds = results.manual_time_used;
287326
} else if (b.use_real_time) {
288-
seconds = real_accumulated_time;
327+
seconds = results.real_time_used;
289328
}
290329

291330
const double min_time = !IsZero(b.min_time) ? b.min_time
292331
: FLAGS_benchmark_min_time;
293332
// If this was the first run, was elapsed time or cpu time large enough?
294333
// If this is not the first run, go with the current value of iter.
295-
if ((i > 0) || manager.has_error_ || (iters >= kMaxIterations) ||
296-
(seconds >= min_time) || (real_accumulated_time >= 5 * min_time)) {
297-
// Create report about this benchmark run.
298-
BenchmarkReporter::Run report;
299-
report.benchmark_name = b.name;
300-
report.error_occurred = manager.has_error_;
301-
report.error_message = manager.error_message_;
302-
report.report_label = manager.report_label_;
303-
// Report the total iterations across all threads.
304-
report.iterations = static_cast<int64_t>(iters) * b.threads;
305-
report.time_unit = b.time_unit;
306-
307-
if (!report.error_occurred) {
308-
double bytes_per_second = 0;
309-
if (manager.bytes_processed > 0 && seconds > 0.0) {
310-
bytes_per_second = (manager.bytes_processed / seconds);
311-
}
312-
double items_per_second = 0;
313-
if (manager.items_processed > 0 && seconds > 0.0) {
314-
items_per_second = (manager.items_processed / seconds);
315-
}
316-
317-
if (b.use_manual_time) {
318-
report.real_accumulated_time = manual_accumulated_time;
319-
} else {
320-
report.real_accumulated_time = real_accumulated_time;
321-
}
322-
report.cpu_accumulated_time = cpu_accumulated_time;
323-
report.bytes_per_second = bytes_per_second;
324-
report.items_per_second = items_per_second;
325-
report.complexity_n = manager.complexity_n;
326-
report.complexity = b.complexity;
327-
report.complexity_lambda = b.complexity_lambda;
328-
if(report.complexity != oNone)
329-
complexity_reports->push_back(report);
330-
}
331-
334+
if ((i > 0) || results.has_error_ || (iters >= kMaxIterations) ||
335+
(seconds >= min_time) || (results.real_time_used >= 5 * min_time)) {
336+
BenchmarkReporter::Run report = CreateRunReport(b, results, iters, seconds);
337+
if (!report.error_occurred && b.complexity != oNone)
338+
complexity_reports->push_back(report);
332339
reports.push_back(report);
333340
break;
334341
}
@@ -352,10 +359,6 @@ std::vector<BenchmarkReporter::Run> RunBenchmark(
352359
iters = static_cast<int>(next_iters + 0.5);
353360
}
354361
}
355-
if (b.multithreaded) {
356-
for (std::thread& thread : pool)
357-
thread.join();
358-
}
359362
// Calculate additional statistics
360363
auto stat_reports = ComputeStats(reports);
361364
if((b.complexity != oNone) && b.last_benchmark_instance) {
@@ -409,9 +412,9 @@ void State::SkipWithError(const char* msg) {
409412
error_occurred_ = true;
410413
{
411414
MutexLock l(manager_->GetBenchmarkMutex());
412-
if (manager_->has_error_ == false) {
413-
manager_->error_message_ = msg;
414-
manager_->has_error_ = true;
415+
if (manager_->results.has_error_ == false) {
416+
manager_->results.error_message_ = msg;
417+
manager_->results.has_error_ = true;
415418
}
416419
}
417420
total_iterations_ = max_iterations;
@@ -425,7 +428,7 @@ void State::SetIterationTime(double seconds)
425428

426429
void State::SetLabel(const char* label) {
427430
MutexLock l(manager_->GetBenchmarkMutex());
428-
manager_->report_label_ = label;
431+
manager_->results.report_label_ = label;
429432
}
430433

431434
void State::StartKeepRunning() {

‎src/benchmark_api_internal.h

+1-2
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@ struct Benchmark::Instance {
2727
bool last_benchmark_instance;
2828
int repetitions;
2929
double min_time;
30-
int threads; // Number of concurrent threads to use
31-
bool multithreaded; // Is benchmark multi-threaded?
30+
int threads; // Number of concurrent threads to us
3231
};
3332

3433
bool FindBenchmarksInternal(const std::string& re,

‎src/benchmark_register.cc

-1
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,6 @@ bool BenchmarkFamilies::FindBenchmarks(
151151
instance.complexity = family->complexity_;
152152
instance.complexity_lambda = family->complexity_lambda_;
153153
instance.threads = num_threads;
154-
instance.multithreaded = !(family->thread_counts_.empty());
155154

156155
// Add arguments to instance name
157156
for (auto const& arg : args) {

0 commit comments

Comments
 (0)
Please sign in to comment.