2 * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "benchmark/MemoryPoller.h"
18 #include "benchmark/Types.h"
30 const std::string proc_status_path("/proc/self/status");
31 const std::string gpu_memory_path("/sys/kernel/debug/mali0/gpu_memory");
32 const std::string proc_smaps_path("/proc/self/smaps");
34 bool isStrNumber(const std::string &s)
37 std::find_if(s.begin(), s.end(), [](char c) { return !std::isdigit(c); }) == s.end();
40 std::vector<std::string> splitLine(std::string line, std::string delimiters = " \n\t")
42 std::vector<std::string> words;
45 while ((pos = line.find_first_of(delimiters, prev)) != std::string::npos)
48 words.emplace_back(line.substr(prev, pos - prev));
52 if (prev < line.length())
53 words.emplace_back(line.substr(prev, std::string::npos));
58 std::vector<std::string> getValueFromFileStatus(const std::string &file, const std::string &key)
60 std::ifstream ifs(file);
61 assert(ifs.is_open());
64 std::vector<std::string> val;
67 while (std::getline(ifs, line))
69 if (line.find(key) != std::string::npos)
79 // NOTE. the process which uses gpu resources cannot be there yet at the model-load phase.
80 // At that time, just return empty.
84 val = splitLine(line);
88 // Because of smaps' structure, returns sum value as uint32_t
89 uint32_t getSumValueFromFileSmaps(const std::string &file, const std::string &key)
91 std::ifstream ifs(file);
92 assert(ifs.is_open());
96 while (std::getline(ifs, line))
98 if (line.find(key) != std::string::npos)
100 // an example by splitLine()
102 // val[0]: "Pss:", val[1]: "0" val[2]: "kB"
103 auto val = splitLine(line);
104 assert(val.size() != 0);
105 // SwapPss could show so that check where Pss is at the beginning
106 if (val[0].find("Pss") != 0)
110 sum += std::stoul(val[1]);
122 MemoryPoller::MemoryPoller(std::chrono::milliseconds duration, bool gpu_poll)
123 : _duration(duration), _run(false), _term(false), _gpu_poll(gpu_poll)
125 if (prepareMemoryPolling() == false)
126 throw std::runtime_error("failed to prepare memory pooling");
128 _thread = std::thread{&MemoryPoller::process, this};
131 bool MemoryPoller::start(PhaseEnum phase)
133 if (std::find(_phases.begin(), _phases.end(), phase) != _phases.end())
135 std::cerr << getPhaseString(phase) << " is already processing/processed..." << std::endl;
140 std::lock_guard<std::mutex> lock(_mutex);
141 _phases.emplace_back(phase);
148 _cond_var_started.notify_all();
152 bool MemoryPoller::end(PhaseEnum phase)
154 if (std::find(_phases.begin(), _phases.end(), phase) == _phases.end())
156 std::cerr << getPhaseString(phase) << " is not started..." << std::endl;
163 std::lock_guard<std::mutex> lock(_mutex);
164 _phases.remove(phase);
165 stop = (_phases.size() == 0);
168 if (_rss_map[phase] == 0)
170 uint32_t mem = getVmRSS();
173 mem += getGpuMemory();
175 _rss_map[phase] = mem;
178 if (_hwm_map[phase] == 0)
180 uint32_t mem = getVmHWM();
183 mem += getGpuMemory();
185 _hwm_map[phase] = mem;
188 if (_pss_map[phase] == 0)
190 uint32_t mem = getPssSum();
191 _pss_map[phase] = mem;
197 _cond_var_started.notify_all();
203 void MemoryPoller::process()
205 std::unique_lock<std::mutex> lock_started(_mutex_started);
208 _cond_var_started.wait(lock_started, [&]() { return _run || _term; });
212 std::unique_lock<std::mutex> lock(_mutex);
214 uint32_t cur_rss = getVmRSS();
215 uint32_t cur_hwm = getVmHWM();
218 auto gpu_mem = getGpuMemory();
222 uint32_t cur_pss = getPssSum();
224 for (auto &phase : _phases)
226 auto &rss = _rss_map.at(phase);
229 // hwm is gradually increasing
230 auto &hwm = _hwm_map.at(phase);
232 auto &pss = _pss_map.at(phase);
239 std::this_thread::sleep_for(std::chrono::milliseconds(_duration));
243 bool MemoryPoller::prepareMemoryPolling()
247 std::ifstream ifs(proc_status_path);
250 std::cerr << "failed to open " << proc_status_path << std::endl;
256 // (Additionally) GpuMemory
259 std::ifstream ifs(gpu_memory_path);
262 std::cerr << "failed to open " << gpu_memory_path << std::endl;
267 // Needs process name
268 auto val = getValueFromFileStatus(proc_status_path, "Name");
269 assert(val.size() != 0);
270 _process_name = val[1];
275 std::ifstream ifs(proc_smaps_path);
278 std::cerr << "failed to open " << proc_smaps_path << std::endl;
287 uint32_t MemoryPoller::getVmRSS()
289 auto val = getValueFromFileStatus(proc_status_path, "VmRSS");
292 assert(isStrNumber(val[1]));
293 return std::stoul(val[1]);
296 uint32_t MemoryPoller::getVmHWM()
298 auto val = getValueFromFileStatus(proc_status_path, "VmHWM");
302 assert(isStrNumber(val[1]));
303 return std::stoul(val[1]);
306 uint32_t MemoryPoller::getGpuMemory()
308 assert(!_process_name.empty());
309 auto val = getValueFromFileStatus(gpu_memory_path, _process_name);
312 // process_name -> pid -> gpu_mem -> max_gpu_mem
313 assert(isStrNumber(val[2]));
314 return std::stoul(val[2]);
317 uint32_t MemoryPoller::getPssSum() { return getSumValueFromFileSmaps(proc_smaps_path, "Pss"); }
319 } // namespace benchmark