Random interleaving of benchmark repetitions - the sequel (fixes #1051) (#1163)
Inspired by the original implementation by Hai Huang @haih-g
from https://github.com/google/benchmark/pull/1105.
The original implementation had design deficiencies that
weren't really addressable without redesign, so it was reverted.
In essence, the original implementation consisted of two separateable parts:
* reducing the amount time each repetition is run for, and symmetrically increasing repetition count
* running the repetitions in random order
While it worked fine for the usual case, it broke down when user would specify repetitions
(it would completely ignore that request), or specified per-repetition min time (while it would
still adjust the repetition count, it would not adjust the per-repetition time,
leading to much greater run times)
Here, like i was originally suggesting in the original review, i'm separating the features,
and only dealing with a single one - running repetitions in random order.
Now that the runs/repetitions are no longer in-order, the tooling may wish to sort the output,
and indeed `compare.py` has been updated to do that: #1168.
2021-06-03 18:16:54 +00:00
|
|
|
#include <queue>
|
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include "../src/commandlineflags.h"
|
|
|
|
#include "../src/string_util.h"
|
|
|
|
#include "benchmark/benchmark.h"
|
|
|
|
#include "gmock/gmock.h"
|
|
|
|
#include "gtest/gtest.h"
|
|
|
|
|
2021-06-24 15:50:19 +00:00
|
|
|
namespace benchmark {
|
|
|
|
|
2021-06-24 17:21:59 +00:00
|
|
|
BM_DECLARE_bool(benchmark_enable_random_interleaving);
|
|
|
|
BM_DECLARE_string(benchmark_filter);
|
|
|
|
BM_DECLARE_int32(benchmark_repetitions);
|
Random interleaving of benchmark repetitions - the sequel (fixes #1051) (#1163)
Inspired by the original implementation by Hai Huang @haih-g
from https://github.com/google/benchmark/pull/1105.
The original implementation had design deficiencies that
weren't really addressable without redesign, so it was reverted.
In essence, the original implementation consisted of two separateable parts:
* reducing the amount time each repetition is run for, and symmetrically increasing repetition count
* running the repetitions in random order
While it worked fine for the usual case, it broke down when user would specify repetitions
(it would completely ignore that request), or specified per-repetition min time (while it would
still adjust the repetition count, it would not adjust the per-repetition time,
leading to much greater run times)
Here, like i was originally suggesting in the original review, i'm separating the features,
and only dealing with a single one - running repetitions in random order.
Now that the runs/repetitions are no longer in-order, the tooling may wish to sort the output,
and indeed `compare.py` has been updated to do that: #1168.
2021-06-03 18:16:54 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-06-29 10:06:53 +00:00
|
|
|
EventQueue* queue = new EventQueue();
|
Random interleaving of benchmark repetitions - the sequel (fixes #1051) (#1163)
Inspired by the original implementation by Hai Huang @haih-g
from https://github.com/google/benchmark/pull/1105.
The original implementation had design deficiencies that
weren't really addressable without redesign, so it was reverted.
In essence, the original implementation consisted of two separateable parts:
* reducing the amount time each repetition is run for, and symmetrically increasing repetition count
* running the repetitions in random order
While it worked fine for the usual case, it broke down when user would specify repetitions
(it would completely ignore that request), or specified per-repetition min time (while it would
still adjust the repetition count, it would not adjust the per-repetition time,
leading to much greater run times)
Here, like i was originally suggesting in the original review, i'm separating the features,
and only dealing with a single one - running repetitions in random order.
Now that the runs/repetitions are no longer in-order, the tooling may wish to sort the output,
and indeed `compare.py` has been updated to do that: #1168.
2021-06-03 18:16:54 +00:00
|
|
|
|
|
|
|
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();
|
|
|
|
|
2022-02-11 10:23:05 +00:00
|
|
|
std::unique_ptr<BenchmarkReporter> reporter(new NullReporter());
|
Random interleaving of benchmark repetitions - the sequel (fixes #1051) (#1163)
Inspired by the original implementation by Hai Huang @haih-g
from https://github.com/google/benchmark/pull/1105.
The original implementation had design deficiencies that
weren't really addressable without redesign, so it was reverted.
In essence, the original implementation consisted of two separateable parts:
* reducing the amount time each repetition is run for, and symmetrically increasing repetition count
* running the repetitions in random order
While it worked fine for the usual case, it broke down when user would specify repetitions
(it would completely ignore that request), or specified per-repetition min time (while it would
still adjust the repetition count, it would not adjust the per-repetition time,
leading to much greater run times)
Here, like i was originally suggesting in the original review, i'm separating the features,
and only dealing with a single one - running repetitions in random order.
Now that the runs/repetitions are no longer in-order, the tooling may wish to sort the output,
and indeed `compare.py` has been updated to do that: #1168.
2021-06-03 18:16:54 +00:00
|
|
|
FLAGS_benchmark_filter = pattern;
|
2022-02-11 10:23:05 +00:00
|
|
|
RunSpecifiedBenchmarks(reporter.get());
|
Random interleaving of benchmark repetitions - the sequel (fixes #1051) (#1163)
Inspired by the original implementation by Hai Huang @haih-g
from https://github.com/google/benchmark/pull/1105.
The original implementation had design deficiencies that
weren't really addressable without redesign, so it was reverted.
In essence, the original implementation consisted of two separateable parts:
* reducing the amount time each repetition is run for, and symmetrically increasing repetition count
* running the repetitions in random order
While it worked fine for the usual case, it broke down when user would specify repetitions
(it would completely ignore that request), or specified per-repetition min time (while it would
still adjust the repetition count, it would not adjust the per-repetition time,
leading to much greater run times)
Here, like i was originally suggesting in the original review, i'm separating the features,
and only dealing with a single one - running repetitions in random order.
Now that the runs/repetitions are no longer in-order, the tooling may wish to sort the output,
and indeed `compare.py` has been updated to do that: #1168.
2021-06-03 18:16:54 +00:00
|
|
|
|
|
|
|
queue->Put("DONE"); // End marker
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-06-29 10:06:53 +00:00
|
|
|
void BM_Match1(benchmark::State& state) {
|
Random interleaving of benchmark repetitions - the sequel (fixes #1051) (#1163)
Inspired by the original implementation by Hai Huang @haih-g
from https://github.com/google/benchmark/pull/1105.
The original implementation had design deficiencies that
weren't really addressable without redesign, so it was reverted.
In essence, the original implementation consisted of two separateable parts:
* reducing the amount time each repetition is run for, and symmetrically increasing repetition count
* running the repetitions in random order
While it worked fine for the usual case, it broke down when user would specify repetitions
(it would completely ignore that request), or specified per-repetition min time (while it would
still adjust the repetition count, it would not adjust the per-repetition time,
leading to much greater run times)
Here, like i was originally suggesting in the original review, i'm separating the features,
and only dealing with a single one - running repetitions in random order.
Now that the runs/repetitions are no longer in-order, the tooling may wish to sort the output,
and indeed `compare.py` has been updated to do that: #1168.
2021-06-03 18:16:54 +00:00
|
|
|
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});
|
|
|
|
|
|
|
|
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) {
|
|
|
|
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;
|
|
|
|
FLAGS_benchmark_repetitions = 100;
|
|
|
|
|
|
|
|
std::map<std::string, int> element_count;
|
|
|
|
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());
|
2021-12-06 11:18:04 +00:00
|
|
|
element_count[interleaving[0]]++;
|
|
|
|
element_count[interleaving[1]]++;
|
Random interleaving of benchmark repetitions - the sequel (fixes #1051) (#1163)
Inspired by the original implementation by Hai Huang @haih-g
from https://github.com/google/benchmark/pull/1105.
The original implementation had design deficiencies that
weren't really addressable without redesign, so it was reverted.
In essence, the original implementation consisted of two separateable parts:
* reducing the amount time each repetition is run for, and symmetrically increasing repetition count
* running the repetitions in random order
While it worked fine for the usual case, it broke down when user would specify repetitions
(it would completely ignore that request), or specified per-repetition min time (while it would
still adjust the repetition count, it would not adjust the per-repetition time,
leading to much greater run times)
Here, like i was originally suggesting in the original review, i'm separating the features,
and only dealing with a single one - running repetitions in random order.
Now that the runs/repetitions are no longer in-order, the tooling may wish to sort the output,
and indeed `compare.py` has been updated to do that: #1168.
2021-06-03 18:16:54 +00:00
|
|
|
interleaving_count[StrFormat("%s,%s", interleaving[0].c_str(),
|
|
|
|
interleaving[1].c_str())]++;
|
|
|
|
}
|
|
|
|
EXPECT_EQ(element_count["BM_Match1/64"], 100) << "Unexpected repetitions.";
|
|
|
|
EXPECT_EQ(element_count["BM_Match1/80"], 100) << "Unexpected repetitions.";
|
|
|
|
EXPECT_GE(interleaving_count.size(), 2) << "Interleaving was not randomized.";
|
|
|
|
ASSERT_EQ("DONE", queue->Get());
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
} // namespace internal
|
|
|
|
} // namespace benchmark
|