-
Notifications
You must be signed in to change notification settings - Fork 172
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
282 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
#include "quill/StopWatch.h" | ||
#include "quill/Backend.h" | ||
#include "quill/Frontend.h" | ||
#include "quill/LogMacros.h" | ||
#include "quill/Logger.h" | ||
#include "quill/sinks/ConsoleSink.h" | ||
|
||
#include <thread> | ||
|
||
/** | ||
* Stopwatch logging example | ||
*/ | ||
|
||
int main() | ||
{ | ||
quill::BackendOptions backend_options; | ||
quill::Backend::start(backend_options); | ||
|
||
// Frontend | ||
auto console_sink = quill::Frontend::create_or_get_sink<quill::ConsoleSink>( | ||
"sink_id_1", quill::ConsoleSink::ColourMode::Automatic); | ||
|
||
quill::Logger* logger = quill::Frontend::create_or_get_logger("root", std::move(console_sink)); | ||
|
||
{ | ||
quill::StopWatchTsc swt; | ||
LOG_INFO(logger, "Begin Tsc StopWatch"); | ||
std::this_thread::sleep_for(std::chrono::seconds(1)); | ||
LOG_INFO(logger, "After 1s, elapsed: {:.6}s", swt); | ||
std::this_thread::sleep_for(std::chrono::milliseconds(500)); | ||
LOG_INFO(logger, "After 500ms, elapsed: {}s", swt); | ||
LOG_INFO(logger, "elapsed: {}", swt.elapsed_as<std::chrono::nanoseconds>()); | ||
LOG_INFO(logger, "elapsed: {}", swt.elapsed_as<std::chrono::seconds>()); | ||
LOG_INFO(logger, "Reset"); | ||
std::this_thread::sleep_for(std::chrono::milliseconds(500)); | ||
LOG_INFO(logger, "After 500ms, elapsed: {}", swt); | ||
} | ||
|
||
{ | ||
quill::StopWatchChrono swt; | ||
LOG_INFO(logger, "Begin Chrono StopWatch"); | ||
std::this_thread::sleep_for(std::chrono::seconds(1)); | ||
LOG_INFO(logger, "After 1s, elapsed: {:.6}s", swt); | ||
std::this_thread::sleep_for(std::chrono::milliseconds(500)); | ||
LOG_INFO(logger, "After 500ms, elapsed: {}s", swt); | ||
LOG_INFO(logger, "elapsed: {}", swt.elapsed_as<std::chrono::nanoseconds>()); | ||
LOG_INFO(logger, "elapsed: {}", swt.elapsed_as<std::chrono::seconds>()); | ||
LOG_INFO(logger, "Reset"); | ||
std::this_thread::sleep_for(std::chrono::milliseconds(500)); | ||
LOG_INFO(logger, "After 500ms, elapsed: {}", swt); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
/** | ||
* @page copyright | ||
* Copyright(c) 2020-present, Odysseas Georgoudis & quill contributors. | ||
* Distributed under the MIT License (http://opensource.org/licenses/MIT) | ||
*/ | ||
|
||
#pragma once | ||
|
||
#include <cstdint> | ||
|
||
#include "quill/TriviallyCopyableCodec.h" | ||
#include "quill/backend/RdtscClock.h" | ||
#include "quill/core/Attributes.h" | ||
#include "quill/core/Common.h" | ||
#include "quill/core/Rdtsc.h" | ||
|
||
#include "quill/bundled/fmt/format.h" | ||
#include "quill/std/Chrono.h" | ||
|
||
QUILL_BEGIN_NAMESPACE | ||
|
||
namespace detail | ||
{ | ||
/** | ||
* A stopwatch utility for measuring elapsed time since construction. | ||
* | ||
* Displays elapsed time as seconds (double) or any specified duration type. | ||
* | ||
* Can use either TSC-based or system clock-based timing, depending on the | ||
* template parameter `ClockType`. | ||
* | ||
* Example: | ||
* @code | ||
* quill::StopWatchTsc swt; | ||
* LOG_INFO(logger, "Begin"); | ||
* std::this_thread::sleep_for(std::chrono::seconds(1)); | ||
* LOG_INFO(logger, "After 1s, elapsed: {:.6}", swt); // => elapsed: ~1.000000 | ||
* std::this_thread::sleep_for(std::chrono::milliseconds(500)); | ||
* LOG_INFO(logger, "After 500ms, elapsed: {}", swt); // => elapsed: ~1.500000 | ||
* @endcode | ||
*/ | ||
template <ClockSourceType ClockType> | ||
class StopWatch | ||
{ | ||
public: | ||
static_assert((ClockType == ClockSourceType::Tsc) || (ClockType == ClockSourceType::System), | ||
"Invalid ClockType"); | ||
|
||
/** | ||
* Constructor | ||
*/ | ||
StopWatch() | ||
{ | ||
if constexpr (ClockType == ClockSourceType::Tsc) | ||
{ | ||
_ns_per_tick = RdtscClock::RdtscTicks::instance().ns_per_tick(); | ||
_start_tp = rdtsc(); | ||
} | ||
else | ||
{ | ||
_start_tp = std::chrono::duration_cast<std::chrono::nanoseconds>( | ||
std::chrono::steady_clock::now().time_since_epoch()) | ||
.count(); | ||
} | ||
} | ||
|
||
/** | ||
* Returns the elapsed time since construction. | ||
* @return The elapsed time as a `std::chrono::duration<double>` in seconds. | ||
*/ | ||
[[nodiscard]] std::chrono::duration<double> elapsed() const | ||
{ | ||
if constexpr (ClockType == ClockSourceType::Tsc) | ||
{ | ||
return std::chrono::duration<double>( | ||
std::chrono::nanoseconds{static_cast<uint64_t>((rdtsc() - _start_tp) * _ns_per_tick)}); | ||
} | ||
else | ||
{ | ||
return std::chrono::duration<double>(std::chrono::nanoseconds{ | ||
std::chrono::steady_clock::now().time_since_epoch().count() - _start_tp}); | ||
} | ||
} | ||
|
||
/** | ||
* Returns the elapsed time since construction as the specified duration type. | ||
* @return The elapsed time converted to the specified duration type. | ||
*/ | ||
template <typename T> | ||
[[nodiscard]] T elapsed_as() const | ||
{ | ||
if constexpr (ClockType == ClockSourceType::Tsc) | ||
{ | ||
return std::chrono::duration_cast<T>( | ||
std::chrono::nanoseconds{static_cast<uint64_t>((rdtsc() - _start_tp) * _ns_per_tick)}); | ||
} | ||
else | ||
{ | ||
return std::chrono::duration_cast<T>(std::chrono::nanoseconds{ | ||
std::chrono::steady_clock::now().time_since_epoch().count() - _start_tp}); | ||
} | ||
} | ||
|
||
/** | ||
* Resets the stopwatch, starting the measurement from the current time. | ||
*/ | ||
void reset() | ||
{ | ||
if constexpr (ClockType == ClockSourceType::Tsc) | ||
{ | ||
_start_tp = rdtsc(); | ||
} | ||
else | ||
{ | ||
_start_tp = std::chrono::duration_cast<std::chrono::nanoseconds>( | ||
std::chrono::steady_clock::now().time_since_epoch()) | ||
.count(); | ||
} | ||
} | ||
|
||
private: | ||
double _ns_per_tick{0}; | ||
uint64_t _start_tp{0}; | ||
}; | ||
} // namespace detail | ||
|
||
/** | ||
* Stopwatch using TSC (Time Stamp Counter) for high-resolution timing. | ||
*/ | ||
using StopWatchTsc = detail::StopWatch<ClockSourceType::Tsc>; | ||
|
||
/** | ||
* Stopwatch using the system clock for timing based on `std::chrono`. | ||
*/ | ||
using StopWatchChrono = detail::StopWatch<ClockSourceType::System>; | ||
|
||
QUILL_END_NAMESPACE | ||
|
||
template <quill::ClockSourceType ClockType> | ||
struct quill::Codec<quill::detail::StopWatch<ClockType>> | ||
: quill::TriviallyCopyableTypeCodec<quill::detail::StopWatch<ClockType>> | ||
{ | ||
}; | ||
|
||
template <quill::ClockSourceType ClockType> | ||
struct fmtquill::formatter<quill::detail::StopWatch<ClockType>> : fmtquill::formatter<double> | ||
{ | ||
template <typename FormatContext> | ||
auto format(quill::detail::StopWatch<ClockType> const& sw, FormatContext& ctx) const | ||
-> decltype(ctx.out()) | ||
{ | ||
return fmtquill::formatter<double>::format(sw.elapsed().count(), ctx); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
#include "doctest/doctest.h" | ||
|
||
#include "quill/StopWatch.h" | ||
#include <thread> | ||
|
||
TEST_SUITE_BEGIN("StopWatch"); | ||
|
||
TEST_CASE("stopwatch_tsc") | ||
{ | ||
quill::StopWatchTsc swt; | ||
std::this_thread::sleep_for(std::chrono::seconds{1}); | ||
|
||
// greater than 1 second | ||
REQUIRE_GE(swt.elapsed().count(), 1.0); | ||
REQUIRE_GE(swt.elapsed_as<std::chrono::nanoseconds>().count(), 1'000'000'000); | ||
REQUIRE_GE(swt.elapsed_as<std::chrono::microseconds>().count(), 1'000'000); | ||
REQUIRE_GE(swt.elapsed_as<std::chrono::milliseconds>().count(), 1'000); | ||
REQUIRE_GE(swt.elapsed_as<std::chrono::seconds>().count(), 1); | ||
|
||
swt.reset(); | ||
REQUIRE_LT(swt.elapsed().count(), 1.0); | ||
|
||
std::this_thread::sleep_for(std::chrono::seconds{2}); | ||
|
||
// greater than 2 seconds | ||
REQUIRE_GE(swt.elapsed().count(), 2.0); | ||
REQUIRE_GE(swt.elapsed_as<std::chrono::nanoseconds>().count(), 2'000'000'000); | ||
REQUIRE_GE(swt.elapsed_as<std::chrono::microseconds>().count(), 2'000'000); | ||
REQUIRE_GE(swt.elapsed_as<std::chrono::milliseconds>().count(), 2'000); | ||
REQUIRE_GE(swt.elapsed_as<std::chrono::seconds>().count(), 2); | ||
} | ||
|
||
TEST_CASE("stopwatch_chrono") | ||
{ | ||
quill::StopWatchChrono swt; | ||
std::this_thread::sleep_for(std::chrono::seconds{1}); | ||
|
||
// greater than 1 second | ||
REQUIRE_GE(swt.elapsed().count(), 1.0); | ||
REQUIRE_GE(swt.elapsed_as<std::chrono::nanoseconds>().count(), 1'000'000'000); | ||
REQUIRE_GE(swt.elapsed_as<std::chrono::microseconds>().count(), 1'000'000); | ||
REQUIRE_GE(swt.elapsed_as<std::chrono::milliseconds>().count(), 1'000); | ||
REQUIRE_GE(swt.elapsed_as<std::chrono::seconds>().count(), 1); | ||
|
||
swt.reset(); | ||
REQUIRE_LT(swt.elapsed().count(), 1.0); | ||
|
||
std::this_thread::sleep_for(std::chrono::seconds{2}); | ||
|
||
// greater than 2 seconds | ||
REQUIRE_GE(swt.elapsed().count(), 2.0); | ||
REQUIRE_GE(swt.elapsed_as<std::chrono::nanoseconds>().count(), 2'000'000'000); | ||
REQUIRE_GE(swt.elapsed_as<std::chrono::microseconds>().count(), 2'000'000); | ||
REQUIRE_GE(swt.elapsed_as<std::chrono::milliseconds>().count(), 2'000); | ||
REQUIRE_GE(swt.elapsed_as<std::chrono::seconds>().count(), 2); | ||
} | ||
|
||
TEST_SUITE_END(); |