Merge pull request #4269 from clemahieu/stats_lasy_time_sampling

Lazy steady clock sampling within nano::stats since the timestamps are only needed when stat sampling is configured.
Change stat data structure to an unordered map for O(1) access.
This commit is contained in:
clemahieu 2023-08-31 10:04:50 +01:00 committed by GitHub
commit 8ebea1176e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 44 additions and 34 deletions

View file

@ -391,52 +391,62 @@ void nano::stats::update (uint32_t key_a, uint64_t value)
static file_writer log_count (config.log_counters_filename);
static file_writer log_sample (config.log_samples_filename);
auto now (std::chrono::steady_clock::now ());
nano::unique_lock<nano::mutex> lock{ stat_mutex };
if (!stopped)
{
auto entry (get_entry_impl (key_a, config.interval, config.capacity));
auto has_interval_counter = [&] () {
return config.log_interval_counters > 0;
};
auto has_sampling = [&] () {
return config.sampling_enabled && entry->sample_interval > 0;
};
// Counters
auto old (entry->counter.get_value ());
entry->counter.add (value);
entry->counter.add (value, has_sampling ()); // Only update timestamp when sampling is enabled as this has a performance impact
entry->count_observers.notify (old, entry->counter.get_value ());
std::chrono::duration<double, std::milli> duration = now - log_last_count_writeout;
if (config.log_interval_counters > 0 && duration.count () > config.log_interval_counters)
if (has_interval_counter () || has_sampling ())
{
log_counters_impl (log_count);
log_last_count_writeout = now;
}
// Samples
if (config.sampling_enabled && entry->sample_interval > 0)
{
entry->sample_current.add (value, false);
std::chrono::duration<double, std::milli> duration = now - entry->sample_start_time;
if (duration.count () > entry->sample_interval)
auto now = std::chrono::steady_clock::now (); // Only sample clock if necessary as this impacts node performance due to frequent usage
if (has_interval_counter ())
{
entry->sample_start_time = now;
// Make a snapshot of samples for thread safety and to get a stable container
entry->sample_current.set_timestamp (std::chrono::system_clock::now ());
entry->samples.push_back (entry->sample_current);
entry->sample_current.set_value (0);
if (!entry->sample_observers.empty ())
std::chrono::duration<double, std::milli> duration = now - log_last_count_writeout;
if (duration.count () > config.log_interval_counters)
{
auto snapshot (entry->samples);
entry->sample_observers.notify (snapshot);
log_counters_impl (log_count);
log_last_count_writeout = now;
}
}
// Log sink
duration = now - log_last_sample_writeout;
if (config.log_interval_samples > 0 && duration.count () > config.log_interval_samples)
// Samples
if (has_sampling ())
{
entry->sample_current.add (value, false);
std::chrono::duration<double, std::milli> duration = now - entry->sample_start_time;
if (duration.count () > entry->sample_interval)
{
log_samples_impl (log_sample);
log_last_sample_writeout = now;
entry->sample_start_time = now;
// Make a snapshot of samples for thread safety and to get a stable container
entry->sample_current.set_timestamp (std::chrono::system_clock::now ());
entry->samples.push_back (entry->sample_current);
entry->sample_current.set_value (0);
if (!entry->sample_observers.empty ())
{
auto snapshot (entry->samples);
entry->sample_observers.notify (snapshot);
}
// Log sink
duration = now - log_last_sample_writeout;
if (config.log_interval_samples > 0 && duration.count () > config.log_interval_samples)
{
log_samples_impl (log_sample);
log_last_sample_writeout = now;
}
}
}
}

View file

@ -9,10 +9,10 @@
#include <chrono>
#include <initializer_list>
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include <unordered_map>
namespace nano
{
@ -440,7 +440,7 @@ private:
nano::stats_config config;
/** Stat entries are sorted by key to simplify processing of log output */
std::map<uint32_t, std::shared_ptr<nano::stat_entry>> entries;
std::unordered_map<uint32_t, std::shared_ptr<nano::stat_entry>> entries;
std::chrono::steady_clock::time_point log_last_count_writeout{ std::chrono::steady_clock::now () };
std::chrono::steady_clock::time_point log_last_sample_writeout{ std::chrono::steady_clock::now () };