blob: 5e8329a4e6c6bef11e7c2eb29f1af1317bde7411 [file] [log] [blame]
#include <queue>
#include <string>
#include <vector>
#include "../src/benchmark_adjust_repetitions.h"
#include "../src/string_util.h"
#include "benchmark/benchmark.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
DECLARE_bool(benchmark_enable_random_interleaving);
DECLARE_string(benchmark_filter);
DECLARE_double(benchmark_random_interleaving_max_overhead);
namespace do_not_read_flag_directly {
DECLARE_int32(benchmark_repetitions);
} // namespace do_not_read_flag_directly
namespace benchmark {
namespace internal {
namespace {
class EventQueue : public std::queue<std::string> {
public:
void Put(const std::string& event) {
push(event);
}
void Clear() {
while (!empty()) {
pop();
}
}
std::string Get() {
std::string event = front();
pop();
return event;
}
};
static EventQueue* queue = new EventQueue;
class NullReporter : public BenchmarkReporter {
public:
bool ReportContext(const Context& /*context*/) override {
return true;
}
void ReportRuns(const std::vector<Run>& /* report */) override {}
};
class BenchmarkTest : public testing::Test {
public:
static void SetupHook(int /* num_threads */) { queue->push("Setup"); }
static void TeardownHook(int /* num_threads */) { queue->push("Teardown"); }
void Execute(const std::string& pattern) {
queue->Clear();
BenchmarkReporter* reporter = new NullReporter;
FLAGS_benchmark_filter = pattern;
RunSpecifiedBenchmarks(reporter);
delete reporter;
queue->Put("DONE"); // End marker
}
};
static void BM_Match1(benchmark::State& state) {
const int64_t arg = state.range(0);
for (auto _ : state) {}
queue->Put(StrFormat("BM_Match1/%d", static_cast<int>(arg)));
}
BENCHMARK(BM_Match1)
->Iterations(100)
->Arg(1)
->Arg(2)
->Arg(3)
->Range(10, 80)
->Args({90})
->Args({100});
static void BM_MatchOverhead(benchmark::State& state) {
const int64_t arg = state.range(0);
for (auto _ : state) {}
queue->Put(StrFormat("BM_MatchOverhead/%d", static_cast<int>(arg)));
}
BENCHMARK(BM_MatchOverhead)
->Iterations(100)
->Arg(64)
->Arg(80);
TEST_F(BenchmarkTest, Match1) {
Execute("BM_Match1");
ASSERT_EQ("BM_Match1/1", queue->Get());
ASSERT_EQ("BM_Match1/2", queue->Get());
ASSERT_EQ("BM_Match1/3", queue->Get());
ASSERT_EQ("BM_Match1/10", queue->Get());
ASSERT_EQ("BM_Match1/64", queue->Get());
ASSERT_EQ("BM_Match1/80", queue->Get());
ASSERT_EQ("BM_Match1/90", queue->Get());
ASSERT_EQ("BM_Match1/100", queue->Get());
ASSERT_EQ("DONE", queue->Get());
}
TEST_F(BenchmarkTest, Match1WithRepetition) {
do_not_read_flag_directly::FLAGS_benchmark_repetitions = 2;
Execute("BM_Match1/(64|80)");
ASSERT_EQ("BM_Match1/64", queue->Get());
ASSERT_EQ("BM_Match1/64", queue->Get());
ASSERT_EQ("BM_Match1/80", queue->Get());
ASSERT_EQ("BM_Match1/80", queue->Get());
ASSERT_EQ("DONE", queue->Get());
}
TEST_F(BenchmarkTest, Match1WithRandomInterleaving) {
FLAGS_benchmark_enable_random_interleaving = true;
do_not_read_flag_directly::FLAGS_benchmark_repetitions = 100;
FLAGS_benchmark_random_interleaving_max_overhead =
std::numeric_limits<double>::infinity();
std::vector<std::string> expected({"BM_Match1/64", "BM_Match1/80"});
std::map<std::string, int> interleaving_count;
Execute("BM_Match1/(64|80)");
for (int i = 0; i < 100; ++i) {
std::vector<std::string> interleaving;
interleaving.push_back(queue->Get());
interleaving.push_back(queue->Get());
EXPECT_THAT(interleaving, testing::UnorderedElementsAreArray(expected));
interleaving_count[StrFormat("%s,%s", interleaving[0].c_str(),
interleaving[1].c_str())]++;
}
EXPECT_GE(interleaving_count.size(), 2) << "Interleaving was not randomized.";
ASSERT_EQ("DONE", queue->Get());
}
TEST_F(BenchmarkTest, Match1WithRandomInterleavingAndZeroOverhead) {
FLAGS_benchmark_enable_random_interleaving = true;
do_not_read_flag_directly::FLAGS_benchmark_repetitions = 100;
FLAGS_benchmark_random_interleaving_max_overhead = 0;
// ComputeRandomInterleavingRepetitions() will kick in and rerun each
// benchmark once with increased iterations. Then number of repetitions will
// be reduced to < 100. The first 4 executions should be
// 2 x BM_MatchOverhead/64 and 2 x BM_MatchOverhead/80.
std::vector<std::string> expected(
{"BM_MatchOverhead/64", "BM_MatchOverhead/80", "BM_MatchOverhead/64",
"BM_MatchOverhead/80"});
std::map<std::string, int> interleaving_count;
Execute("BM_MatchOverhead/(64|80)");
std::vector<std::string> interleaving;
interleaving.push_back(queue->Get());
interleaving.push_back(queue->Get());
interleaving.push_back(queue->Get());
interleaving.push_back(queue->Get());
EXPECT_THAT(interleaving, testing::UnorderedElementsAreArray(expected));
ASSERT_LT(queue->size(), 100) << "# Repetitions was not reduced to < 100.";
}
InternalRandomInterleavingRepetitionsInput CreateInput(
double total, double time, double real_time, double min_time,
double overhead, int repetitions) {
InternalRandomInterleavingRepetitionsInput input;
input.total_execution_time_per_repetition = total;
input.time_used_per_repetition = time;
input.real_time_used_per_repetition = real_time;
input.min_time_per_repetition = min_time;
input.max_overhead = overhead;
input.max_repetitions = repetitions;
return input;
}
TEST(Benchmark, ComputeRandomInterleavingRepetitions) {
// On wall clock time.
EXPECT_EQ(ComputeRandomInterleavingRepetitions(
CreateInput(0.05, 0.05, 0.05, 0.05, 0.0, 10)),
10);
EXPECT_EQ(ComputeRandomInterleavingRepetitions(
CreateInput(0.05, 0.05, 0.05, 0.05, 0.4, 10)),
10);
EXPECT_EQ(ComputeRandomInterleavingRepetitions(
CreateInput(0.06, 0.05, 0.05, 0.05, 0.0, 10)),
8);
EXPECT_EQ(ComputeRandomInterleavingRepetitions(
CreateInput(0.06, 0.05, 0.05, 0.05, 0.4, 10)),
10);
EXPECT_EQ(ComputeRandomInterleavingRepetitions(
CreateInput(0.08, 0.05, 0.05, 0.05, 0.0, 10)),
6);
EXPECT_EQ(ComputeRandomInterleavingRepetitions(
CreateInput(0.08, 0.05, 0.05, 0.05, 0.4, 10)),
9);
EXPECT_EQ(ComputeRandomInterleavingRepetitions(
CreateInput(0.26, 0.25, 0.25, 0.05, 0.0, 10)),
2);
EXPECT_EQ(ComputeRandomInterleavingRepetitions(
CreateInput(0.25, 0.25, 0.25, 0.05, 0.4, 10)),
3);
EXPECT_EQ(ComputeRandomInterleavingRepetitions(
CreateInput(0.26, 0.25, 0.25, 0.05, 0.0, 10)),
2);
EXPECT_EQ(ComputeRandomInterleavingRepetitions(
CreateInput(0.26, 0.25, 0.25, 0.05, 0.4, 10)),
3);
EXPECT_EQ(ComputeRandomInterleavingRepetitions(
CreateInput(0.38, 0.25, 0.25, 0.05, 0.0, 10)),
2);
EXPECT_EQ(ComputeRandomInterleavingRepetitions(
CreateInput(0.38, 0.25, 0.25, 0.05, 0.4, 10)),
3);
// On CPU time.
EXPECT_EQ(ComputeRandomInterleavingRepetitions(
CreateInput(0.1, 0.05, 0.1, 0.05, 0.0, 10)),
10);
EXPECT_EQ(ComputeRandomInterleavingRepetitions(
CreateInput(0.1, 0.05, 0.1, 0.05, 0.4, 10)),
10);
EXPECT_EQ(ComputeRandomInterleavingRepetitions(
CreateInput(0.11, 0.05, 0.1, 0.05, 0.0, 10)),
9);
EXPECT_EQ(ComputeRandomInterleavingRepetitions(
CreateInput(0.11, 0.05, 0.1, 0.05, 0.4, 10)),
10);
EXPECT_EQ(ComputeRandomInterleavingRepetitions(
CreateInput(0.15, 0.05, 0.1, 0.05, 0.0, 10)),
7);
EXPECT_EQ(ComputeRandomInterleavingRepetitions(
CreateInput(0.15, 0.05, 0.1, 0.05, 0.4, 10)),
9);
EXPECT_EQ(ComputeRandomInterleavingRepetitions(
CreateInput(0.5, 0.25, 0.5, 0.05, 0.0, 10)),
2);
EXPECT_EQ(ComputeRandomInterleavingRepetitions(
CreateInput(0.5, 0.25, 0.5, 0.05, 0.4, 10)),
3);
EXPECT_EQ(ComputeRandomInterleavingRepetitions(
CreateInput(0.51, 0.25, 0.5, 0.05, 0.0, 10)),
2);
EXPECT_EQ(ComputeRandomInterleavingRepetitions(
CreateInput(0.51, 0.25, 0.5, 0.05, 0.4, 10)),
3);
EXPECT_EQ(ComputeRandomInterleavingRepetitions(
CreateInput(0.8, 0.25, 0.5, 0.05, 0.0, 10)),
2);
EXPECT_EQ(ComputeRandomInterleavingRepetitions(
CreateInput(0.8, 0.25, 0.5, 0.05, 0.4, 10)),
2);
// Corner cases.
EXPECT_EQ(ComputeRandomInterleavingRepetitions(
CreateInput(0.0, 0.25, 0.5, 0.05, 0.4, 10)),
3);
EXPECT_EQ(ComputeRandomInterleavingRepetitions(
CreateInput(0.8, 0.0, 0.5, 0.05, 0.4, 10)),
9);
EXPECT_EQ(ComputeRandomInterleavingRepetitions(
CreateInput(0.8, 0.25, 0.0, 0.05, 0.4, 10)),
1);
EXPECT_EQ(ComputeRandomInterleavingRepetitions(
CreateInput(0.8, 0.25, 0.5, 0.0, 0.4, 10)),
1);
}
} // namespace
} // namespace internal
} // namespace benchmark