diff options
Diffstat (limited to 'runtime/libs/benchmark/src/MemoryPoller.cpp')
-rw-r--r-- | runtime/libs/benchmark/src/MemoryPoller.cpp | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/runtime/libs/benchmark/src/MemoryPoller.cpp b/runtime/libs/benchmark/src/MemoryPoller.cpp new file mode 100644 index 000000000..436d536e4 --- /dev/null +++ b/runtime/libs/benchmark/src/MemoryPoller.cpp @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "benchmark/MemoryPoller.h" +#include <vector> +#include <fstream> +#include <sstream> +#include <stdexcept> +#include <cassert> +#include <iostream> + +namespace +{ + +const std::string proc_status_path("/proc/self/status"); +const std::string gpu_memory_path("/sys/kernel/debug/mali0/gpu_memory"); + +bool isStrNumber(const std::string &s) +{ + return !s.empty() && + std::find_if(s.begin(), s.end(), [](char c) { return !std::isdigit(c); }) == s.end(); +} + +std::vector<std::string> splitLine(std::string line, std::string delimiters = " \n\t") +{ + std::vector<std::string> words; + size_t prev = 0, pos; + + while ((pos = line.find_first_of(delimiters, prev)) != std::string::npos) + { + if (pos > prev) + words.emplace_back(line.substr(prev, pos - prev)); + prev = pos + 1; + } + + if (prev < line.length()) + words.emplace_back(line.substr(prev, std::string::npos)); + + return words; +} + +std::vector<std::string> getValueFromFileStatus(const std::string &file, const std::string &key) +{ + std::ifstream ifs(file); + assert(ifs.is_open()); + + std::string line; + std::vector<std::string> val; + + bool found = false; + while (std::getline(ifs, line)) + { + if (line.find(key) != std::string::npos) + { + found = true; + break; + } + } + ifs.close(); + + if (!found) + { + // NOTE. the process which uses gpu resources cannot be there yet at the model-load phase. + // At that time, just return empty. + return val; + } + + val = splitLine(line); + return val; +} + +} // namespace anonymous + +namespace benchmark +{ + +MemoryPoller::MemoryPoller(std::chrono::milliseconds duration, bool gpu_poll) + : _duration(duration), _run(false), _term(false), _gpu_poll(gpu_poll) +{ + if (prepareMemoryPolling() == false) + throw std::runtime_error("failed to prepare memory pooling"); + + _thread = std::thread{&MemoryPoller::process, this}; +} + +bool MemoryPoller::start(Phase phase) +{ + if (std::find(_phases.begin(), _phases.end(), phase) != _phases.end()) + { + std::cerr << getPhaseString(phase) << " is already processing/processed..." << std::endl; + return false; + } + + { + std::lock_guard<std::mutex> lock(_mutex); + _phases.emplace_back(phase); + _rss_map[phase] = 0; + _hwm_map[phase] = 0; + } + + _run = true; + _cond_var_started.notify_all(); + return true; +} + +bool MemoryPoller::end(Phase phase) +{ + if (std::find(_phases.begin(), _phases.end(), phase) == _phases.end()) + { + std::cerr << getPhaseString(phase) << " is not started..." << std::endl; + return false; + } + + uint32_t mem = 0; + bool stop = false; + { + std::lock_guard<std::mutex> lock(_mutex); + _phases.remove(phase); + stop = (_phases.size() == 0); + } + + if (_rss_map[phase] == 0) + { + uint32_t mem = getVmRSS(); + if (_gpu_poll) + { + mem += getGpuMemory(); + } + _rss_map[phase] = mem; + } + + if (_hwm_map[phase] == 0) + { + uint32_t mem = getVmHWM(); + if (_gpu_poll) + { + mem += getGpuMemory(); + } + _hwm_map[phase] = mem; + } + + if (stop) + { + _run = false; + _cond_var_started.notify_all(); + } + + return true; +} + +void MemoryPoller::process() +{ + std::unique_lock<std::mutex> lock_started(_mutex_started); + while (true) + { + _cond_var_started.wait(lock_started, [&]() { return _run || _term; }); + if (_term) + break; + + std::unique_lock<std::mutex> lock(_mutex); + + uint32_t cur_rss = getVmRSS(); + uint32_t cur_hwm = getVmHWM(); + if (_gpu_poll) + { + auto gpu_mem = getGpuMemory(); + cur_rss += gpu_mem; + cur_hwm += gpu_mem; + } + + for (auto &phase : _phases) + { + auto &rss = _rss_map.at(phase); + if (rss < cur_rss) + rss = cur_rss; + // hwm is gradually increasing + auto &hwm = _hwm_map.at(phase); + hwm = cur_hwm; + } + + lock.unlock(); + + std::this_thread::sleep_for(std::chrono::milliseconds(_duration)); + } +} + +bool MemoryPoller::prepareMemoryPolling() +{ + // VmRSS + { + std::ifstream ifs(proc_status_path); + if (!ifs.is_open()) + { + std::cerr << "failed to open " << proc_status_path << std::endl; + return false; + } + ifs.close(); + } + + // (Additionally) GpuMemory + if (_gpu_poll) + { + std::ifstream ifs(gpu_memory_path); + if (!ifs.is_open()) + { + std::cerr << "failed to open " << gpu_memory_path << std::endl; + return false; + } + ifs.close(); + + // Needs process name + auto val = getValueFromFileStatus(proc_status_path, "Name"); + assert(val.size() != 0); + _process_name = val[1]; + } + + return true; +} + +uint32_t MemoryPoller::getVmRSS() +{ + auto val = getValueFromFileStatus(proc_status_path, "VmRSS"); + if (val.size() == 0) + return 0; + assert(isStrNumber(val[1])); + return std::stoul(val[1]); +} + +uint32_t MemoryPoller::getVmHWM() +{ + auto val = getValueFromFileStatus(proc_status_path, "VmHWM"); + if (val.size() == 0) + return 0; + // key: value + assert(isStrNumber(val[1])); + return std::stoul(val[1]); +} + +uint32_t MemoryPoller::getGpuMemory() +{ + assert(!_process_name.empty()); + auto val = getValueFromFileStatus(gpu_memory_path, _process_name); + if (val.size() == 0) + return 0; + // process_name -> pid -> gpu_mem -> max_gpu_mem + assert(isStrNumber(val[2])); + return std::stoul(val[2]); +} + +} // namespace benchmark |