From 6ed303881c43d2730ce345bcbd010bcf362a25ca Mon Sep 17 00:00:00 2001 From: Ronen Friedman Date: Mon, 17 Jun 2024 13:23:26 -0500 Subject: [PATCH] common: MemoryModel: use charconv to parse /proc/status Also - stopping the parsing of /proc/status once all the needed fields have been found, The resulting code is measurably faster than the previous version. Signed-off-by: Ronen Friedman --- src/common/MemoryModel.cc | 99 +++++++++++++++++++++++++++------------ src/common/MemoryModel.h | 17 ++++--- 2 files changed, 80 insertions(+), 36 deletions(-) diff --git a/src/common/MemoryModel.cc b/src/common/MemoryModel.cc index 4a19946133c..354d66bad83 100644 --- a/src/common/MemoryModel.cc +++ b/src/common/MemoryModel.cc @@ -1,6 +1,8 @@ -#include "MemoryModel.h" -#include "include/compat.h" #include "debug.h" + +#include "include/compat.h" + +#include "MemoryModel.h" #if defined(__linux__) #include #endif @@ -15,15 +17,37 @@ using namespace std; using mem_snap_t = MemoryModel::mem_snap_t; -MemoryModel::MemoryModel(CephContext *cct_) - : cct(cct_) +MemoryModel::MemoryModel(CephContext *cct_) : cct(cct_) {} + +inline bool MemoryModel::cmp_against( + const std::string &ln, + std::string_view param, + long &v) const { + if (ln.size() < (param.size() + 10)) { + return false; + } + if (ln.starts_with(param)) { + auto p = ln.c_str(); + auto s = p + param.size(); + // charconv does not like leading spaces + while (*s && isblank(*s)) { + s++; + } + from_chars(s, p + ln.size(), v); + return true; + } + return false; } + std::optional MemoryModel::get_mapped_heap() { if (!proc_maps.is_open()) { - ldout(cct, 0) << fmt::format("MemoryModel::get_mapped_heap() unable to open {}", proc_maps_fn) << dendl; + ldout(cct, 0) << fmt::format( + "MemoryModel::get_mapped_heap() unable to open {}", + proc_maps_fn) + << dendl; return std::nullopt; } // always rewind before reading @@ -38,15 +62,17 @@ std::optional MemoryModel::get_mapped_heap() const char *start = line.c_str(); const char *dash = start; - while (*dash && *dash != '-') dash++; + while (*dash && *dash != '-') + dash++; if (!*dash) continue; const char *end = dash + 1; - while (*end && *end != ' ') end++; + while (*end && *end != ' ') + end++; if (!*end) continue; unsigned long long as = strtoll(start, 0, 16); - unsigned long long ae = strtoll(dash+1, 0, 16); + unsigned long long ae = strtoll(dash + 1, 0, 16); end++; const char *mode = end; @@ -54,7 +80,8 @@ std::optional MemoryModel::get_mapped_heap() int skip = 4; while (skip--) { end++; - while (*end && *end != ' ') end++; + while (*end && *end != ' ') + end++; } if (*end) end++; @@ -72,35 +99,47 @@ std::optional MemoryModel::get_mapped_heap() } - -void MemoryModel::_sample(mem_snap_t *psnap) +std::optional MemoryModel::full_sample() { if (!proc_status.is_open()) { - ldout(cct, 0) << fmt::format("MemoryModel::sample() unable to open {}", proc_stat_fn) << dendl; - return; + ldout(cct, 0) << fmt::format( + "MemoryModel::sample() unable to open {}", + proc_stat_fn) + << dendl; + return std::nullopt; } // always rewind before reading proc_status.clear(); proc_status.seekg(0); - while (!proc_status.eof()) { - string line; - getline(proc_status, line); - - if (strncmp(line.c_str(), "VmSize:", 7) == 0) - psnap->size = atol(line.c_str() + 7); - else if (strncmp(line.c_str(), "VmRSS:", 6) == 0) - psnap->rss = atol(line.c_str() + 7); - else if (strncmp(line.c_str(), "VmHWM:", 6) == 0) - psnap->hwm = atol(line.c_str() + 7); - else if (strncmp(line.c_str(), "VmLib:", 6) == 0) - psnap->lib = atol(line.c_str() + 7); - else if (strncmp(line.c_str(), "VmPeak:", 7) == 0) - psnap->peak = atol(line.c_str() + 7); - else if (strncmp(line.c_str(), "VmData:", 7) == 0) - psnap->data = atol(line.c_str() + 7); + mem_snap_t s; + // we will be looking for 6 entries + int yet_to_find = 6; + + while (!proc_status.eof() && yet_to_find > 0) { + string ln; + getline(proc_status, ln); + + if (cmp_against(ln, "VmSize:", s.size) || + cmp_against(ln, "VmRSS:", s.rss) || cmp_against(ln, "VmHWM:", s.hwm) || + cmp_against(ln, "VmLib:", s.lib) || + cmp_against(ln, "VmPeak:", s.peak) || + cmp_against(ln, "VmData:", s.data)) { + yet_to_find--; + } } // get heap size - psnap->heap = static_cast(get_mapped_heap().value_or(0)); + s.heap = static_cast(get_mapped_heap().value_or(0)); + return s; +} + +void MemoryModel::sample(mem_snap_t *p) +{ + auto s = full_sample(); + if (s) { + last = *s; + if (p) + *p = last; + } } diff --git a/src/common/MemoryModel.h b/src/common/MemoryModel.h index 7191e6e31f9..bae37026967 100644 --- a/src/common/MemoryModel.h +++ b/src/common/MemoryModel.h @@ -17,6 +17,7 @@ #include #include +#include #include "include/common_fwd.h" #include "include/compat.h" @@ -50,16 +51,20 @@ private: std::ifstream proc_maps{proc_maps_fn}; CephContext *cct; - void _sample(mem_snap_t *p); std::optional get_mapped_heap(); + /** + * @brief Compare a line against an expected data label + * + * If the line starts with the expected label, extract the value and store it in v. + * \retval true if the line starts with the expected label + */ + bool cmp_against(const std::string& ln, std::string_view param, long& v) const; + public: explicit MemoryModel(CephContext *cct); - void sample(mem_snap_t *p = 0) { - _sample(&last); - if (p) - *p = last; - } + std::optional full_sample(); + void sample(mem_snap_t *p = nullptr); }; #endif -- 2.39.5