diff options
Diffstat (limited to 'staging_vespalib')
-rw-r--r-- | staging_vespalib/src/tests/clock/.gitignore | 1 | ||||
-rw-r--r-- | staging_vespalib/src/tests/clock/CMakeLists.txt | 6 | ||||
-rw-r--r-- | staging_vespalib/src/tests/clock/clock_benchmark.cpp | 171 | ||||
-rw-r--r-- | staging_vespalib/src/tests/clock/clock_test.cpp | 2 | ||||
-rw-r--r-- | staging_vespalib/src/vespa/vespalib/util/clock.cpp | 1 |
5 files changed, 179 insertions, 2 deletions
diff --git a/staging_vespalib/src/tests/clock/.gitignore b/staging_vespalib/src/tests/clock/.gitignore index 1826ba1563b..b0fcd343176 100644 --- a/staging_vespalib/src/tests/clock/.gitignore +++ b/staging_vespalib/src/tests/clock/.gitignore @@ -2,3 +2,4 @@ Makefile clock_test staging_vespalib_clock_test_app +staging_vespalib_clock_benchmark_app diff --git a/staging_vespalib/src/tests/clock/CMakeLists.txt b/staging_vespalib/src/tests/clock/CMakeLists.txt index e62ef47e18a..825c0ae36cb 100644 --- a/staging_vespalib/src/tests/clock/CMakeLists.txt +++ b/staging_vespalib/src/tests/clock/CMakeLists.txt @@ -1,4 +1,10 @@ # Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(staging_vespalib_clock_benchmark_app TEST + SOURCES + clock_benchmark.cpp + DEPENDS + staging_vespalib +) vespa_add_executable(staging_vespalib_clock_test_app TEST SOURCES clock_test.cpp diff --git a/staging_vespalib/src/tests/clock/clock_benchmark.cpp b/staging_vespalib/src/tests/clock/clock_benchmark.cpp new file mode 100644 index 00000000000..c5229adea31 --- /dev/null +++ b/staging_vespalib/src/tests/clock/clock_benchmark.cpp @@ -0,0 +1,171 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/vespalib/util/clock.h> +#include <cassert> +#include <vector> +#include <atomic> +#include <cstring> + +using vespalib::Clock; +using fastos::TimeStamp; + +struct UpdateClock { + virtual ~UpdateClock() {} + virtual void update() = 0; +}; + +struct NSValue : public UpdateClock { + void update() override { _value = std::chrono::steady_clock::now().time_since_epoch().count(); } + int64_t _value; +}; + +struct NSVolatile : public UpdateClock { + void update() override { _value = std::chrono::steady_clock::now().time_since_epoch().count(); } + volatile int64_t _value; +}; +struct NSAtomic : public UpdateClock { + void update() override { _value.store(std::chrono::steady_clock::now().time_since_epoch().count()); } + std::atomic<int64_t> _value; +}; + +class TestClock : public FastOS_Runnable +{ +private: + int _timePeriodMS; + std::mutex _lock; + std::condition_variable _cond; + UpdateClock &_clock; + bool _stop; + + void Run(FastOS_ThreadInterface *thisThread, void *arguments) override; + +public: + TestClock(UpdateClock & clock, double timePeriod) + : _timePeriodMS(static_cast<uint32_t>(timePeriod*1000)), + _lock(), + _cond(), + _clock(clock), + _stop(false) + { } + ~TestClock() { + std::lock_guard<std::mutex> guard(_lock); + _stop = true; + _cond.notify_all(); + } +}; + +void TestClock::Run(FastOS_ThreadInterface *thread, void *) +{ + std::unique_lock<std::mutex> guard(_lock); + while ( ! thread->GetBreakFlag() && !_stop) { + _clock.update(); + _cond.wait_for(guard, std::chrono::milliseconds(_timePeriodMS)); + } +} + +struct SamplerBase : public FastOS_Runnable { + SamplerBase(uint32_t threadId) + : _thread(nullptr), + _threadId(threadId), + _samples(0), + _count() + { + memset(_count, 0, sizeof(_count)); + } + FastOS_ThreadInterface * _thread; + uint32_t _threadId; + uint64_t _samples; + uint64_t _count[3]; +}; + +template<typename Func> +struct Sampler : public SamplerBase { + Sampler(Func func, uint32_t threadId) + : SamplerBase(threadId), + _func(func) + { } + void Run(FastOS_ThreadInterface *, void *) override { + uint64_t samples; + fastos::SteadyTimeStamp prev = _func(); + for (samples = 0; (samples < _samples); samples++) { + fastos::SteadyTimeStamp now = _func(); + fastos::TimeStamp diff = now - prev; + if (diff > 0) prev = now; + _count[1 + ((diff == 0) ? 0 : (diff > 0) ? 1 : -1)]++; + } + + } + Func _func; +}; + +template<typename Func> +void benchmark(const char * desc, FastOS_ThreadPool & pool, uint64_t samples, uint32_t numThreads, Func func) { + std::vector<std::unique_ptr<SamplerBase>> threads; + threads.reserve(numThreads); + fastos::SteadyTimeStamp start = fastos::ClockSteady::now(); + for (uint32_t i(0); i < numThreads; i++) { + SamplerBase * sampler = new Sampler<Func>(func, i); + sampler->_samples = samples; + sampler->_thread = pool.NewThread(sampler, nullptr); + threads.emplace_back(sampler); + } + uint64_t count[3]; + memset(count, 0, sizeof(count)); + for (const auto & sampler : threads) { + sampler->_thread->Join(); + for (uint32_t i(0); i < 3; i++) { + count[i] += sampler->_count[i]; + } + } + printf("%s: Took %ld clock samples in %2.3f with [%ld, %ld, %ld] counts\n", desc, samples, (fastos::ClockSteady::now() - start).sec(), count[0], count[1], count[2]); +} + +int +main(int , char *argv[]) +{ + uint64_t frequency = atoll(argv[1]); + uint32_t numThreads = atoi(argv[2]); + uint64_t samples = atoll(argv[3]); + FastOS_ThreadPool pool(0x10000); + NSValue nsValue; + NSVolatile nsVolatile; + NSAtomic nsAtomic; + Clock clock(1.0/frequency); + TestClock nsClock(nsValue, 1.0/frequency); + TestClock nsVolatileClock(nsVolatile, 1.0/frequency); + TestClock nsAtomicClock(nsAtomic, 1.0/frequency); + assert(pool.NewThread(&clock, nullptr) != nullptr); + assert(pool.NewThread(&nsClock, nullptr) != nullptr); + assert(pool.NewThread(&nsVolatileClock, nullptr) != nullptr); + assert(pool.NewThread(&nsAtomicClock, nullptr) != nullptr); + + benchmark("vespalib::Clock", pool, samples, numThreads, [&clock]() { + return clock.getTimeNSAssumeRunning(); + }); + benchmark("uint64_t", pool, samples, numThreads, [&nsValue]() { + return fastos::SteadyTimeStamp(nsValue._value) ; + }); + benchmark("volatile uint64_t", pool, samples, numThreads, [&nsVolatile]() { + return fastos::SteadyTimeStamp(nsVolatile._value) ; + }); + benchmark("memory_order_relaxed", pool, samples, numThreads, [&nsAtomic]() { + return fastos::SteadyTimeStamp(nsAtomic._value.load(std::memory_order_relaxed)) ; + }); + benchmark("memory_order_consume", pool, samples, numThreads, [&nsAtomic]() { + return fastos::SteadyTimeStamp(nsAtomic._value.load(std::memory_order_consume)) ; + }); + benchmark("memory_order_acquire", pool, samples, numThreads, [&nsAtomic]() { + return fastos::SteadyTimeStamp(nsAtomic._value.load(std::memory_order_acquire)) ; + }); + benchmark("memory_order_seq_cst", pool, samples, numThreads, [&nsAtomic]() { + return fastos::SteadyTimeStamp(nsAtomic._value.load(std::memory_order_seq_cst)) ; + }); + + benchmark("fastos::ClockSteady::now()", pool, samples, numThreads, []() { + return fastos::ClockSteady::now(); + }); + + pool.Close(); + clock.stop(); + return 0; +} diff --git a/staging_vespalib/src/tests/clock/clock_test.cpp b/staging_vespalib/src/tests/clock/clock_test.cpp index 45f17bbba92..c8c93272e85 100644 --- a/staging_vespalib/src/tests/clock/clock_test.cpp +++ b/staging_vespalib/src/tests/clock/clock_test.cpp @@ -20,7 +20,7 @@ Test::Main() Clock clock(0.050); FastOS_ThreadPool pool(0x10000); - ASSERT_TRUE(pool.NewThread(&clock, NULL) != NULL); + ASSERT_TRUE(pool.NewThread(&clock, nullptr) != nullptr); fastos::SteadyTimeStamp start = clock.getTimeNS(); FastOS_Thread::Sleep(5000); fastos::SteadyTimeStamp stop = clock.getTimeNS(); diff --git a/staging_vespalib/src/vespa/vespalib/util/clock.cpp b/staging_vespalib/src/vespa/vespalib/util/clock.cpp index 18892a3a7ea..e935a80bd6b 100644 --- a/staging_vespalib/src/vespa/vespalib/util/clock.cpp +++ b/staging_vespalib/src/vespa/vespalib/util/clock.cpp @@ -6,7 +6,6 @@ namespace vespalib { - Clock::Clock(double timePeriod) : _timeNS(0u), _timePeriodMS(static_cast<uint32_t>(timePeriod*1000)), |