Skip to content

Commit 6ed3038

Browse files
committed
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 <[email protected]>
1 parent 7752cee commit 6ed3038

File tree

2 files changed

+80
-36
lines changed

2 files changed

+80
-36
lines changed

src/common/MemoryModel.cc

Lines changed: 69 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
#include "MemoryModel.h"
2-
#include "include/compat.h"
31
#include "debug.h"
2+
3+
#include "include/compat.h"
4+
5+
#include "MemoryModel.h"
46
#if defined(__linux__)
57
#include <malloc.h>
68
#endif
@@ -15,15 +17,37 @@
1517
using namespace std;
1618
using mem_snap_t = MemoryModel::mem_snap_t;
1719

18-
MemoryModel::MemoryModel(CephContext *cct_)
19-
: cct(cct_)
20+
MemoryModel::MemoryModel(CephContext *cct_) : cct(cct_) {}
21+
22+
inline bool MemoryModel::cmp_against(
23+
const std::string &ln,
24+
std::string_view param,
25+
long &v) const
2026
{
27+
if (ln.size() < (param.size() + 10)) {
28+
return false;
29+
}
30+
if (ln.starts_with(param)) {
31+
auto p = ln.c_str();
32+
auto s = p + param.size();
33+
// charconv does not like leading spaces
34+
while (*s && isblank(*s)) {
35+
s++;
36+
}
37+
from_chars(s, p + ln.size(), v);
38+
return true;
39+
}
40+
return false;
2141
}
2242

43+
2344
std::optional<int64_t> MemoryModel::get_mapped_heap()
2445
{
2546
if (!proc_maps.is_open()) {
26-
ldout(cct, 0) << fmt::format("MemoryModel::get_mapped_heap() unable to open {}", proc_maps_fn) << dendl;
47+
ldout(cct, 0) << fmt::format(
48+
"MemoryModel::get_mapped_heap() unable to open {}",
49+
proc_maps_fn)
50+
<< dendl;
2751
return std::nullopt;
2852
}
2953
// always rewind before reading
@@ -38,23 +62,26 @@ std::optional<int64_t> MemoryModel::get_mapped_heap()
3862

3963
const char *start = line.c_str();
4064
const char *dash = start;
41-
while (*dash && *dash != '-') dash++;
65+
while (*dash && *dash != '-')
66+
dash++;
4267
if (!*dash)
4368
continue;
4469
const char *end = dash + 1;
45-
while (*end && *end != ' ') end++;
70+
while (*end && *end != ' ')
71+
end++;
4672
if (!*end)
4773
continue;
4874
unsigned long long as = strtoll(start, 0, 16);
49-
unsigned long long ae = strtoll(dash+1, 0, 16);
75+
unsigned long long ae = strtoll(dash + 1, 0, 16);
5076

5177
end++;
5278
const char *mode = end;
5379

5480
int skip = 4;
5581
while (skip--) {
5682
end++;
57-
while (*end && *end != ' ') end++;
83+
while (*end && *end != ' ')
84+
end++;
5885
}
5986
if (*end)
6087
end++;
@@ -72,35 +99,47 @@ std::optional<int64_t> MemoryModel::get_mapped_heap()
7299
}
73100

74101

75-
76-
void MemoryModel::_sample(mem_snap_t *psnap)
102+
std::optional<mem_snap_t> MemoryModel::full_sample()
77103
{
78104
if (!proc_status.is_open()) {
79-
ldout(cct, 0) << fmt::format("MemoryModel::sample() unable to open {}", proc_stat_fn) << dendl;
80-
return;
105+
ldout(cct, 0) << fmt::format(
106+
"MemoryModel::sample() unable to open {}",
107+
proc_stat_fn)
108+
<< dendl;
109+
return std::nullopt;
81110
}
82111
// always rewind before reading
83112
proc_status.clear();
84113
proc_status.seekg(0);
85114

86-
while (!proc_status.eof()) {
87-
string line;
88-
getline(proc_status, line);
89-
90-
if (strncmp(line.c_str(), "VmSize:", 7) == 0)
91-
psnap->size = atol(line.c_str() + 7);
92-
else if (strncmp(line.c_str(), "VmRSS:", 6) == 0)
93-
psnap->rss = atol(line.c_str() + 7);
94-
else if (strncmp(line.c_str(), "VmHWM:", 6) == 0)
95-
psnap->hwm = atol(line.c_str() + 7);
96-
else if (strncmp(line.c_str(), "VmLib:", 6) == 0)
97-
psnap->lib = atol(line.c_str() + 7);
98-
else if (strncmp(line.c_str(), "VmPeak:", 7) == 0)
99-
psnap->peak = atol(line.c_str() + 7);
100-
else if (strncmp(line.c_str(), "VmData:", 7) == 0)
101-
psnap->data = atol(line.c_str() + 7);
115+
mem_snap_t s;
116+
// we will be looking for 6 entries
117+
int yet_to_find = 6;
118+
119+
while (!proc_status.eof() && yet_to_find > 0) {
120+
string ln;
121+
getline(proc_status, ln);
122+
123+
if (cmp_against(ln, "VmSize:", s.size) ||
124+
cmp_against(ln, "VmRSS:", s.rss) || cmp_against(ln, "VmHWM:", s.hwm) ||
125+
cmp_against(ln, "VmLib:", s.lib) ||
126+
cmp_against(ln, "VmPeak:", s.peak) ||
127+
cmp_against(ln, "VmData:", s.data)) {
128+
yet_to_find--;
129+
}
102130
}
103131

104132
// get heap size
105-
psnap->heap = static_cast<long>(get_mapped_heap().value_or(0));
133+
s.heap = static_cast<long>(get_mapped_heap().value_or(0));
134+
return s;
135+
}
136+
137+
void MemoryModel::sample(mem_snap_t *p)
138+
{
139+
auto s = full_sample();
140+
if (s) {
141+
last = *s;
142+
if (p)
143+
*p = last;
144+
}
106145
}

src/common/MemoryModel.h

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include <fstream>
1919
#include <optional>
20+
#include <string_view>
2021
#include "include/common_fwd.h"
2122
#include "include/compat.h"
2223

@@ -50,16 +51,20 @@ class MemoryModel {
5051
std::ifstream proc_maps{proc_maps_fn};
5152

5253
CephContext *cct;
53-
void _sample(mem_snap_t *p);
5454
std::optional<int64_t> get_mapped_heap();
5555

56+
/**
57+
* @brief Compare a line against an expected data label
58+
*
59+
* If the line starts with the expected label, extract the value and store it in v.
60+
* \retval true if the line starts with the expected label
61+
*/
62+
bool cmp_against(const std::string& ln, std::string_view param, long& v) const;
63+
5664
public:
5765
explicit MemoryModel(CephContext *cct);
58-
void sample(mem_snap_t *p = 0) {
59-
_sample(&last);
60-
if (p)
61-
*p = last;
62-
}
66+
std::optional<mem_snap_t> full_sample();
67+
void sample(mem_snap_t *p = nullptr);
6368
};
6469

6570
#endif

0 commit comments

Comments
 (0)