Commit 1544a002 authored by Gabriel Charette's avatar Gabriel Charette Committed by Commit Bot

[WaitableEvent] Fix perf tests

WaitableEventPerfTest.Throughput was incorrect; it relied on a const
samples() but was looping through as many iterations as possible for
1 second... so the report was garbage. The test was disabled because
the vector went over the allocated capacity (faster machines now mean
this test can do in the order of 1'000'000 operations in 1 second).

Allocating a vector of a million entries just to accumulate later is
overkill. Switched to accumulating as we go.

An upcoming change of mine will improve perf further I think; cleaning
up and re-enabling these tests before helps show the delta.

Renamed the tests to have them prefixed by "WaitableEvent" so they're
easier to find on the perf dashboard (continuity is not an issue).

R=kylechar@chromium.org

Bug: 848499, 905412
Change-Id: I021886b472bfbb698af615038e393a92e654a122
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1718598
Commit-Queue: Gabriel Charette <gab@chromium.org>
Auto-Submit: Gabriel Charette <gab@chromium.org>
Reviewed-by: default avatarkylechar <kylechar@chromium.org>
Cr-Commit-Position: refs/heads/master@{#681152}
parent 7234aeec
...@@ -4,9 +4,11 @@ ...@@ -4,9 +4,11 @@
#include "base/synchronization/waitable_event.h" #include "base/synchronization/waitable_event.h"
#include <numeric> #include <string>
#include "base/threading/simple_thread.h" #include "base/threading/simple_thread.h"
#include "base/time/time.h"
#include "base/timer/elapsed_timer.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "testing/perf/perf_test.h" #include "testing/perf/perf_test.h"
...@@ -16,48 +18,46 @@ namespace { ...@@ -16,48 +18,46 @@ namespace {
class TraceWaitableEvent { class TraceWaitableEvent {
public: public:
TraceWaitableEvent(size_t samples) TraceWaitableEvent() = default;
: event_(WaitableEvent::ResetPolicy::AUTOMATIC,
WaitableEvent::InitialState::NOT_SIGNALED),
samples_(samples) {
signal_times_.reserve(samples);
wait_times_.reserve(samples);
}
~TraceWaitableEvent() = default; ~TraceWaitableEvent() = default;
void Signal() { void Signal() {
TimeTicks start = TimeTicks::Now(); ElapsedTimer timer;
event_.Signal(); event_.Signal();
signal_times_.push_back(TimeTicks::Now() - start); total_signal_time_ += timer.Elapsed();
++signal_samples_;
} }
void Wait() { void Wait() {
TimeTicks start = TimeTicks::Now(); ElapsedTimer timer;
event_.Wait(); event_.Wait();
wait_times_.push_back(TimeTicks::Now() - start); total_wait_time_ += timer.Elapsed();
++wait_samples_;
} }
bool TimedWaitUntil(const TimeTicks& end_time) { bool TimedWaitUntil(const TimeTicks& end_time) {
TimeTicks start = TimeTicks::Now(); ElapsedTimer timer;
bool signaled = event_.TimedWaitUntil(end_time); const bool signaled = event_.TimedWaitUntil(end_time);
wait_times_.push_back(TimeTicks::Now() - start); total_wait_time_ += timer.Elapsed();
++wait_samples_;
return signaled; return signaled;
} }
bool IsSignaled() { return event_.IsSignaled(); } bool IsSignaled() { return event_.IsSignaled(); }
const std::vector<TimeDelta>& signal_times() const { return signal_times_; } TimeDelta total_signal_time() const { return total_signal_time_; }
const std::vector<TimeDelta>& wait_times() const { return wait_times_; } TimeDelta total_wait_time() const { return total_wait_time_; }
size_t samples() const { return samples_; } size_t signal_samples() const { return signal_samples_; }
size_t wait_samples() const { return wait_samples_; }
private: private:
WaitableEvent event_; WaitableEvent event_{WaitableEvent::ResetPolicy::AUTOMATIC};
std::vector<TimeDelta> signal_times_; TimeDelta total_signal_time_;
std::vector<TimeDelta> wait_times_; TimeDelta total_wait_time_;
const size_t samples_; size_t signal_samples_ = 0U;
size_t wait_samples_ = 0U;
DISALLOW_COPY_AND_ASSIGN(TraceWaitableEvent); DISALLOW_COPY_AND_ASSIGN(TraceWaitableEvent);
}; };
...@@ -85,28 +85,24 @@ class SignalerThread : public SimpleThread { ...@@ -85,28 +85,24 @@ class SignalerThread : public SimpleThread {
void RequestStop() { stop_event_.Signal(); } void RequestStop() { stop_event_.Signal(); }
private: private:
WaitableEvent stop_event_{WaitableEvent::ResetPolicy::MANUAL, WaitableEvent stop_event_;
WaitableEvent::InitialState::NOT_SIGNALED};
TraceWaitableEvent* waiter_; TraceWaitableEvent* waiter_;
TraceWaitableEvent* signaler_; TraceWaitableEvent* signaler_;
DISALLOW_COPY_AND_ASSIGN(SignalerThread); DISALLOW_COPY_AND_ASSIGN(SignalerThread);
}; };
void PrintPerfWaitableEvent(const TraceWaitableEvent* event, void PrintPerfWaitableEvent(const TraceWaitableEvent* event,
const std::string& modifier,
const std::string& trace) { const std::string& trace) {
TimeDelta signal_time = std::accumulate(
event->signal_times().begin(), event->signal_times().end(), TimeDelta());
TimeDelta wait_time = std::accumulate(event->wait_times().begin(),
event->wait_times().end(), TimeDelta());
perf_test::PrintResult( perf_test::PrintResult(
"signal_time", modifier, trace, "WaitableEvent_SignalTime", "", trace,
static_cast<size_t>(signal_time.InNanoseconds()) / event->samples(), static_cast<size_t>(event->total_signal_time().InMicroseconds()) /
"ns/sample", true); event->signal_samples(),
"us/sample", true);
perf_test::PrintResult( perf_test::PrintResult(
"wait_time", modifier, trace, "WaitableEvent_WaitTime", "", trace,
static_cast<size_t>(wait_time.InNanoseconds()) / event->samples(), static_cast<size_t>(event->total_wait_time().InMicroseconds()) /
"ns/sample", true); event->wait_samples(),
"us/sample", true);
} }
} // namespace } // namespace
...@@ -114,21 +110,21 @@ void PrintPerfWaitableEvent(const TraceWaitableEvent* event, ...@@ -114,21 +110,21 @@ void PrintPerfWaitableEvent(const TraceWaitableEvent* event,
TEST(WaitableEventPerfTest, SingleThread) { TEST(WaitableEventPerfTest, SingleThread) {
const size_t kSamples = 1000; const size_t kSamples = 1000;
TraceWaitableEvent event(kSamples); TraceWaitableEvent event;
for (size_t i = 0; i < kSamples; ++i) { for (size_t i = 0; i < kSamples; ++i) {
event.Signal(); event.Signal();
event.Wait(); event.Wait();
} }
PrintPerfWaitableEvent(&event, "", "singlethread-1000-samples"); PrintPerfWaitableEvent(&event, "singlethread-1000-samples");
} }
TEST(WaitableEventPerfTest, MultipleThreads) { TEST(WaitableEventPerfTest, MultipleThreads) {
const size_t kSamples = 1000; const size_t kSamples = 1000;
TraceWaitableEvent waiter(kSamples); TraceWaitableEvent waiter;
TraceWaitableEvent signaler(kSamples); TraceWaitableEvent signaler;
// The other thread will wait and signal on the respective opposite events. // The other thread will wait and signal on the respective opposite events.
SignalerThread thread(&signaler, &waiter); SignalerThread thread(&signaler, &waiter);
...@@ -146,20 +142,17 @@ TEST(WaitableEventPerfTest, MultipleThreads) { ...@@ -146,20 +142,17 @@ TEST(WaitableEventPerfTest, MultipleThreads) {
thread.Join(); thread.Join();
PrintPerfWaitableEvent(&waiter, "_waiter", "multithread-1000-samples"); PrintPerfWaitableEvent(&waiter, "multithread-1000-samples_waiter");
PrintPerfWaitableEvent(&signaler, "_signaler", "multithread-1000-samples"); PrintPerfWaitableEvent(&signaler, "multithread-1000-samples_signaler");
} }
// See crbug.com/848499. TEST(WaitableEventPerfTest, Throughput) {
TEST(WaitableEventPerfTest, DISABLED_Throughput) { TraceWaitableEvent event;
// Reserve a lot of sample space.
const size_t kCapacity = 500000;
TraceWaitableEvent event(kCapacity);
SignalerThread thread(nullptr, &event); SignalerThread thread(nullptr, &event);
thread.Start(); thread.Start();
TimeTicks end_time = TimeTicks::Now() + TimeDelta::FromSeconds(1); const TimeTicks end_time = TimeTicks::Now() + TimeDelta::FromSeconds(1);
size_t count = 0; size_t count = 0;
while (event.TimedWaitUntil(end_time)) { while (event.TimedWaitUntil(end_time)) {
++count; ++count;
...@@ -169,11 +162,7 @@ TEST(WaitableEventPerfTest, DISABLED_Throughput) { ...@@ -169,11 +162,7 @@ TEST(WaitableEventPerfTest, DISABLED_Throughput) {
thread.Join(); thread.Join();
perf_test::PrintResult("counts", "", "throughput", count, "signals", true); perf_test::PrintResult("counts", "", "throughput", count, "signals", true);
PrintPerfWaitableEvent(&event, "", "throughput"); PrintPerfWaitableEvent(&event, "throughput");
// Make sure that allocation didn't happen during the test.
EXPECT_LE(event.signal_times().capacity(), kCapacity);
EXPECT_LE(event.wait_times().capacity(), kCapacity);
} }
} // namespace base } // namespace base
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment