You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
179 lines
5.0 KiB
179 lines
5.0 KiB
// Copyright 2017 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "base/synchronization/waitable_event.h"
|
|
|
|
#include <numeric>
|
|
|
|
#include "base/threading/simple_thread.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
#include "testing/perf/perf_test.h"
|
|
|
|
namespace base {
|
|
|
|
namespace {
|
|
|
|
class TraceWaitableEvent {
|
|
public:
|
|
TraceWaitableEvent(size_t samples)
|
|
: event_(WaitableEvent::ResetPolicy::AUTOMATIC,
|
|
WaitableEvent::InitialState::NOT_SIGNALED),
|
|
samples_(samples) {
|
|
signal_times_.reserve(samples);
|
|
wait_times_.reserve(samples);
|
|
}
|
|
|
|
~TraceWaitableEvent() = default;
|
|
|
|
void Signal() {
|
|
TimeTicks start = TimeTicks::Now();
|
|
event_.Signal();
|
|
signal_times_.push_back(TimeTicks::Now() - start);
|
|
}
|
|
|
|
void Wait() {
|
|
TimeTicks start = TimeTicks::Now();
|
|
event_.Wait();
|
|
wait_times_.push_back(TimeTicks::Now() - start);
|
|
}
|
|
|
|
bool TimedWaitUntil(const TimeTicks& end_time) {
|
|
TimeTicks start = TimeTicks::Now();
|
|
bool signaled = event_.TimedWaitUntil(end_time);
|
|
wait_times_.push_back(TimeTicks::Now() - start);
|
|
return signaled;
|
|
}
|
|
|
|
bool IsSignaled() { return event_.IsSignaled(); }
|
|
|
|
const std::vector<TimeDelta>& signal_times() const { return signal_times_; }
|
|
const std::vector<TimeDelta>& wait_times() const { return wait_times_; }
|
|
size_t samples() const { return samples_; }
|
|
|
|
private:
|
|
WaitableEvent event_;
|
|
|
|
std::vector<TimeDelta> signal_times_;
|
|
std::vector<TimeDelta> wait_times_;
|
|
|
|
const size_t samples_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(TraceWaitableEvent);
|
|
};
|
|
|
|
class SignalerThread : public SimpleThread {
|
|
public:
|
|
SignalerThread(TraceWaitableEvent* waiter, TraceWaitableEvent* signaler)
|
|
: SimpleThread("WaitableEventPerfTest signaler"),
|
|
waiter_(waiter),
|
|
signaler_(signaler) {}
|
|
|
|
~SignalerThread() override = default;
|
|
|
|
void Run() override {
|
|
while (!stop_event_.IsSignaled()) {
|
|
if (waiter_)
|
|
waiter_->Wait();
|
|
if (signaler_)
|
|
signaler_->Signal();
|
|
}
|
|
}
|
|
|
|
// Signals the thread to stop on the next iteration of its loop (which
|
|
// will happen immediately if no |waiter_| is present or is signaled.
|
|
void RequestStop() { stop_event_.Signal(); }
|
|
|
|
private:
|
|
WaitableEvent stop_event_{WaitableEvent::ResetPolicy::MANUAL,
|
|
WaitableEvent::InitialState::NOT_SIGNALED};
|
|
TraceWaitableEvent* waiter_;
|
|
TraceWaitableEvent* signaler_;
|
|
DISALLOW_COPY_AND_ASSIGN(SignalerThread);
|
|
};
|
|
|
|
void PrintPerfWaitableEvent(const TraceWaitableEvent* event,
|
|
const std::string& modifier,
|
|
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(
|
|
"signal_time", modifier, trace,
|
|
static_cast<size_t>(signal_time.InNanoseconds()) / event->samples(),
|
|
"ns/sample", true);
|
|
perf_test::PrintResult(
|
|
"wait_time", modifier, trace,
|
|
static_cast<size_t>(wait_time.InNanoseconds()) / event->samples(),
|
|
"ns/sample", true);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TEST(WaitableEventPerfTest, SingleThread) {
|
|
const size_t kSamples = 1000;
|
|
|
|
TraceWaitableEvent event(kSamples);
|
|
|
|
for (size_t i = 0; i < kSamples; ++i) {
|
|
event.Signal();
|
|
event.Wait();
|
|
}
|
|
|
|
PrintPerfWaitableEvent(&event, "", "singlethread-1000-samples");
|
|
}
|
|
|
|
TEST(WaitableEventPerfTest, MultipleThreads) {
|
|
const size_t kSamples = 1000;
|
|
|
|
TraceWaitableEvent waiter(kSamples);
|
|
TraceWaitableEvent signaler(kSamples);
|
|
|
|
// The other thread will wait and signal on the respective opposite events.
|
|
SignalerThread thread(&signaler, &waiter);
|
|
thread.Start();
|
|
|
|
for (size_t i = 0; i < kSamples; ++i) {
|
|
signaler.Signal();
|
|
waiter.Wait();
|
|
}
|
|
|
|
// Signal the stop event and then make sure the signaler event it is
|
|
// waiting on is also signaled.
|
|
thread.RequestStop();
|
|
signaler.Signal();
|
|
|
|
thread.Join();
|
|
|
|
PrintPerfWaitableEvent(&waiter, "_waiter", "multithread-1000-samples");
|
|
PrintPerfWaitableEvent(&signaler, "_signaler", "multithread-1000-samples");
|
|
}
|
|
|
|
TEST(WaitableEventPerfTest, Throughput) {
|
|
// Reserve a lot of sample space.
|
|
const size_t kCapacity = 500000;
|
|
TraceWaitableEvent event(kCapacity);
|
|
|
|
SignalerThread thread(nullptr, &event);
|
|
thread.Start();
|
|
|
|
TimeTicks end_time = TimeTicks::Now() + TimeDelta::FromSeconds(1);
|
|
size_t count = 0;
|
|
while (event.TimedWaitUntil(end_time)) {
|
|
++count;
|
|
}
|
|
|
|
thread.RequestStop();
|
|
thread.Join();
|
|
|
|
perf_test::PrintResult("counts", "", "throughput", count, "signals", true);
|
|
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
|