From 8382636de77cd75bd2b1148ec6e90fbe9f176d12 Mon Sep 17 00:00:00 2001 From: Misha Shneerson Date: Thu, 30 Jun 2016 17:22:39 -0700 Subject: [PATCH] stop_watch is moved to folly Summary: moved stop_watch to folly defined aliases for backward compact. These alias will be removed in next diff. Reviewed By: juchem Differential Revision: D3474035 fbshipit-source-id: 74ee8bb7f2db46434c937eecf121d1cba473178a --- folly/Makefile.am | 1 + folly/stop_watch.h | 317 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 318 insertions(+) create mode 100644 folly/stop_watch.h diff --git a/folly/Makefile.am b/folly/Makefile.am index 212c0a17..998ea6d4 100644 --- a/folly/Makefile.am +++ b/folly/Makefile.am @@ -304,6 +304,7 @@ nobase_follyinclude_HEADERS = \ stats/MultiLevelTimeSeries.h \ stats/TimeseriesHistogram-defs.h \ stats/TimeseriesHistogram.h \ + stop_watch.h \ String.h \ String-inl.h \ Subprocess.h \ diff --git a/folly/stop_watch.h b/folly/stop_watch.h new file mode 100644 index 00000000..bc01935a --- /dev/null +++ b/folly/stop_watch.h @@ -0,0 +1,317 @@ +/* + * Copyright 2016 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include + +namespace folly { + +#ifdef CLOCK_MONOTONIC_COARSE +struct monotonic_coarse_clock { + typedef std::chrono::milliseconds::rep rep; + typedef std::chrono::milliseconds::period period; + typedef std::chrono::milliseconds duration; + typedef std::chrono::time_point time_point; + constexpr static bool is_steady = true; + + static time_point now() { + timespec ts; + auto ret = clock_gettime(CLOCK_MONOTONIC_COARSE, &ts); + if (ret != 0) { + throw std::runtime_error("Error using CLOCK_MONOTONIC_COARSE."); + } + return time_point( + duration((ts.tv_sec * 1000) + ((ts.tv_nsec / 1000) / 1000))); + } +}; +#else +using monotonic_coarse_clock = std::chrono::steady_clock; +#endif + +using monotonic_clock = std::chrono::steady_clock; + +/** + * Calculates the duration of time intervals. Prefer this over directly using + * monotonic clocks. It is very lightweight and provides convenient facilitles + * to avoid common pitfalls. + * + * There are two type aliases that should be preferred over instantiating this + * class directly: `coarse_stop_watch` and `stop_watch`. + * + * Arguments: + * - Clock: the monotonic clock to use when calculating time intervals + * - Duration: (optional) the duration to use when reporting elapsed time. + * Defaults to the clock's duration. + * + * Example 1: + * + * coarse_stop_watch watch; + * do_something(); + * std::cout << "time elapsed: " << watch.elapsed() << std::endl; + * + * auto const ttl = 60_s; + * if (watch.elapsed(ttl)) { + * process_expiration(); + * } + * + * Example 2: + * + * struct run_every_n_seconds { + * using callback = std::function; + * run_every_n_seconds(std::chrono::seconds period, callback action) + * period_(period), + * action_(std::move(action)) + * { + * // watch_ is correctly initialized to the current time + * } + * + * void run() { + * while (true) { + * if (watch_.lap(period_)) { + * action_(); + * } + * std::this_thread::yield(); + * } + * } + * + * private: + * stop_watch<> watch_; + * std::chrono::seconds period_; + * callback action_; + * }; + * + * @author: Marcelo Juchem + */ +template +struct custom_stop_watch { + using clock_type = Clock; + using duration = Duration; + using time_point = std::chrono::time_point; + + static_assert( + std::ratio_less_equal< + typename clock_type::duration::period, + typename duration::period>::value, + "clock must be at least as precise as the requested duration"); + + static_assert( + Clock::is_steady, + "only monotonic clocks should be used to track time intervals"); + + /** + * Initializes the stop watch with the current time as its checkpoint. + * + * Example: + * + * stop_watch<> watch; + * do_something(); + * std::cout << "time elapsed: " << watch.elapsed() << std::endl; + * + * @author: Marcelo Juchem + */ + custom_stop_watch() : checkpoint_(clock_type::now()) {} + + /** + * Initializes the stop watch with the given time as its checkpoint. + * + * NOTE: this constructor should be seldomly used. It is only provided so + * that, in the rare occasions it is needed, one does not have to reimplement + * the `custom_stop_watch` class. + * + * Example: + * + * custom_stop_watch watch(monotonic_clock::now()); + * do_something(); + * std::cout << "time elapsed: " << watch.elapsed() << std::endl; + * + * @author: Marcelo Juchem + */ + explicit custom_stop_watch(typename clock_type::time_point checkpoint) + : checkpoint_(std::move(checkpoint)) {} + + /** + * Updates the stop watch checkpoint to the current time. + * + * Example: + * + * struct some_resource { + * // ... + * + * void on_reloaded() { + * time_alive.reset(); + * } + * + * void report() { + * std::cout << "resource has been alive for " << time_alive.elapsed(); + * } + * + * private: + * stop_watch<> time_alive; + * }; + * + * @author: Marcelo Juchem + */ + void reset() { + checkpoint_ = clock_type::now(); + } + + /** + * Tells the elapsed time since the last update. + * + * The stop watch's checkpoint remains unchanged. + * + * Example: + * + * stop_watch<> watch; + * do_something(); + * std::cout << "time elapsed: " << watch.elapsed() << std::endl; + * + * @author: Marcelo Juchem + */ + duration elapsed() const { + return std::chrono::duration_cast( + clock_type::now() - checkpoint_); + } + + /** + * Tells whether the given duration has already elapsed since the last + * checkpoint. + * + * Example: + * + * auto const ttl = 60_s; + * stop_watch<> watch; + * + * do_something(); + * + * std::cout << "has the TTL expired? " std::boolalpha<< watch.elapsed(ttl); + * + * @author: Marcelo Juchem + */ + template + bool elapsed(UDuration&& amount) const { + return clock_type::now() - checkpoint_ >= amount; + } + + /** + * Tells the elapsed time since the last update, and updates the checkpoint + * to the current time. + * + * Example: + * + * struct some_resource { + * // ... + * + * void on_reloaded() { + * auto const alive = time_alive.lap(); + * std::cout << "resource reloaded after being alive for " << alive; + * } + * + * private: + * stop_watch<> time_alive; + * }; + * + * @author: Marcelo Juchem + */ + duration lap() { + auto lastCheckpoint = checkpoint_; + + checkpoint_ = clock_type::now(); + + return std::chrono::duration_cast(checkpoint_ - lastCheckpoint); + } + + /** + * Tells whether the given duration has already elapsed since the last + * checkpoint. If so, update the checkpoint to the current time. If not, + * the checkpoint remains unchanged. + * + * Example: + * + * void run_every_n_seconds( + * std::chrono::seconds period, + * std::function action + * ) { + * for (stop_watch<> watch;; ) { + * if (watch.lap(period)) { + * action(); + * } + * std::this_thread::yield(); + * } + * } + * + * @author: Marcelo Juchem + */ + template + bool lap(UDuration&& amount) { + auto now = clock_type::now(); + + if (now - checkpoint_ < amount) { + return false; + } + + checkpoint_ = now; + return true; + } + + private: + typename clock_type::time_point checkpoint_; +}; + +/** + * A type alias for `custom_stop_watch` that uses a coarse monotonic clock as + * the time source. Refer to the documentation of `custom_stop_watch` for full + * documentation. + * + * Arguments: + * - Duration: (optional) the duration to use when reporting elapsed time. + * Defaults to the clock's duration. + * + * Example: + * + * coarse_stop_watch watch; + * do_something(); + * std::cout << "time elapsed: " << watch.elapsed() << std::endl; + * + * @author: Marcelo Juchem + */ +template +using coarse_stop_watch = custom_stop_watch; + +/** + * A type alias for `custom_stop_watch` that uses a monotonic clock as the time + * source. Refer to the documentation of `custom_stop_watch` for full + * documentation. + * + * Arguments: + * - Duration: (optional) the duration to use when reporting elapsed time. + * Defaults to the clock's duration. + * + * Example: + * + * stop_watch watch; + * do_something(); + * std::cout << "time elapsed: " << watch.elapsed() << std::endl; + * + * @author: Marcelo Juchem + */ +template +using stop_watch = custom_stop_watch; +} -- 2.34.1