[GPU] Peak memory usage optimizations (#14799)

This commit is contained in:
Sergey Shlyapnikov authored and GitHub committed 2023-01-02 09:51:19 +04:00
1 parent f78fa5b769
commit 19fde3fee6
11 files changed
+144 -5

No files matched your search

@@ -42,6 +42,9 @@ enum class LogLevel : int8_t {
#ifdef GPU_DEBUG_CONFIG
#define GPU_DEBUG_IF(cond) if (cond)
#define GPU_DEBUG_CODE(...) __VA_ARGS__
#define GPU_DEBUG_DEFINE_MEM_LOGGER(stage) \
cldnn::instrumentation::mem_usage_logger mem_logger{stage, cldnn::debug_configuration::get_instance()->verbose >= 2};
#define GPU_DEBUG_PROFILED_STAGE(stage) \
auto stage_prof = cldnn::instrumentation::profiled_stage<primitive_inst>(\
!cldnn::debug_configuration::get_instance()->dump_profiling_data.empty(), *this, stage)
@@ -52,6 +55,8 @@ enum class LogLevel : int8_t {
#define GPU_DEBUG_LOG_RAW(min_verbose_level) GPU_DEBUG_LOG_RAW_INT(static_cast<std::underlying_type<ov::intel_gpu::LogLevel>::type>(min_verbose_level))
#else
#define GPU_DEBUG_IF(cond) if (0)
#define GPU_DEBUG_CODE(...)
#define GPU_DEBUG_DEFINE_MEM_LOGGER(stage)
#define GPU_DEBUG_PROFILED_STAGE(stage)
#define GPU_DEBUG_PROFILED_STAGE_CACHE_HIT(val)
#define GPU_DEBUG_LOG_RAW(min_verbose_level) if (0) std::cout << cldnn::debug_configuration::prefix
@@ -9,6 +9,20 @@
#include <vector>
#include <string>
#if defined(_WIN32)
#ifndef NOMINMAX
#define NOMINMAX
#endif
#ifndef NOGDI
#define NOGDI
#endif
#include <windows.h>
#include "Psapi.h"
#elif !defined(__APPLE__) && !defined(__MACOSX)
#include <fstream>
#endif
#include "layout.hpp"
#include "utils.hpp"
#include "debug_configuration.hpp"
@@ -163,6 +177,83 @@ private:
bool cache_hit = false;
};
class mem_usage_logger {
public:
struct memory_footprint {
memory_footprint() : rss(0), peak_rss(0) {}
memory_footprint(int64_t rss, int64_t peak_rss) : rss(rss), peak_rss(peak_rss) {}
int64_t rss;
int64_t peak_rss;
};
mem_usage_logger(const std::string& stage_name, bool lifetime_logging_mode = true, bool print_mem_usage = true)
: _stage_name(stage_name)
, _lifetime_logging_mode(lifetime_logging_mode)
, _print_mem_usage(print_mem_usage) {
if (_lifetime_logging_mode)
start_logging();
}
~mem_usage_logger() {
if (_lifetime_logging_mode)
stop_logging();
if (_print_mem_usage && _is_active)
print_mem_usage_info();
}
void start_logging() {
_is_active = true;
_before = get_memory_footprint();
}
void stop_logging() {
_after = get_memory_footprint();
}
memory_footprint get_elapsed_mem_usage() {
return memory_footprint{ _after.rss - _before.rss, _after.peak_rss - _before.peak_rss };
}
void print_mem_usage_info() {
auto mem_usage = get_elapsed_mem_usage();
GPU_DEBUG_LOG << "Memory usage for " << _stage_name << ": " << mem_usage.rss << " KB (current RSS: "
<< _after.rss << " KB; peak RSS: " << _after.peak_rss << " KB)" << std::endl;
}
private:
memory_footprint get_memory_footprint() {
memory_footprint footprint;
#if defined(_WIN32)
PROCESS_MEMORY_COUNTERS pmc;
GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc));
footprint.rss = (int64_t)(pmc.WorkingSetSize/1024);
footprint.peak_rss = (int64_t)(pmc.PeakWorkingSetSize/1024);
#elif !defined(__APPLE__) && !defined(__MACOSX)
std::ifstream status("/proc/self/status");
if (!status.is_open())
return footprint;
std::string line, title;
while (std::getline(status, line)) {
std::istringstream iss(line);
iss >> title;
if (title == "VmHWM:")
iss >> footprint.peak_rss;
else if (title == "VmRSS:")
iss >> footprint.rss;
}
#endif
return footprint;
}
std::string _stage_name = {};
bool _lifetime_logging_mode = false;
bool _print_mem_usage = false;
bool _is_active = false;
memory_footprint _before;
memory_footprint _after;
};
/// @}
/// @}
} // namespace instrumentation
@@ -329,6 +329,12 @@ protected:
}
return kernel_strings;
}
void reset_kernels_source() override {
for (size_t i = 0; i < _kernel_data.kernels.size(); ++i) {
_kernel_data.kernels[i].code.kernelString.reset();
}
}
};
} // namespace ocl
@@ -65,6 +65,7 @@ struct primitive_impl {
return {};
}
virtual std::vector<std::shared_ptr<cldnn::kernel_string>> get_kernels_source() { return {}; }
virtual void reset_kernels_source() {}
virtual void set_kernels(std::vector<kernel::ptr>) {}
virtual std::vector<kernel::ptr> get_kernels() const { return {}; }
virtual void set_kernel_ids(std::vector<kernel_id> kernel_ids) {}
@@ -305,6 +305,7 @@ network::network(program::ptr program, stream::ptr stream, bool is_internal, boo
add_default_output_chains();
if (is_dynamic()) {
GPU_DEBUG_DEFINE_MEM_LOGGER("dynamic_network_initialization");
_kernels_cache = std::unique_ptr<kernels_cache>(new kernels_cache(program->get_engine(), program->get_id(),
kernel_selector::KernelBase::get_db().get_batch_header_str()));
_impls_cache = std::unique_ptr<ImplementationsCache>(new ImplementationsCache(_impls_cache_capacity));
@@ -529,6 +530,7 @@ network::ptr network::build_network(engine& engine,
}
void network::validate_primitives() {
GPU_DEBUG_DEFINE_MEM_LOGGER("validate_primitives");
for (auto const& prim : _exec_order) {
bool valid = prim->validate();
CLDNN_ERROR_NOT_EQUAL(prim->id(), "validate", valid, "", true, "has not a valid instance.");
@@ -582,6 +584,7 @@ void network::set_input_data(const primitive_id& id, memory::ptr data) {
}
void network::add_default_output_chains() {
GPU_DEBUG_DEFINE_MEM_LOGGER("add_default_output_chains");
for (auto& output : _outputs) {
add_output_chain(output);
}
@@ -760,6 +763,7 @@ layout network::get_node_output_layout(const primitive_id& output_id) const {
}
void network::allocate_primitives() {
GPU_DEBUG_DEFINE_MEM_LOGGER("allocate_primitives");
std::vector<std::shared_ptr<program_node>> nodes_to_allocate{};
auto& po = _program->get_processing_order();
for (auto node : po) {
@@ -822,6 +826,7 @@ void network::allocate_primitives() {
}
void network::configure_primitives_second_output() {
GPU_DEBUG_DEFINE_MEM_LOGGER("configure_primitives_second_output");
std::map<cldnn::memory::ptr, std::vector<const cldnn::program_node*>> mutable_datas_ptrs;
for (auto& inst : _primitives) {
auto& node = inst.second->get_node();
@@ -857,12 +862,14 @@ void network::configure_primitives_second_output() {
}
void network::build_insts_deps() {
GPU_DEBUG_DEFINE_MEM_LOGGER("build_insts_deps");
for (auto& inst : _primitives) {
inst.second->build_deps();
}
}
void network::build_exec_order() {
GPU_DEBUG_DEFINE_MEM_LOGGER("build_exec_order");
for (auto& node : _program->get_processing_order()) {
if (!node->is_type<data>() && !(node->is_type<mutable_data>() && node->get_dependencies().empty())) {
add_to_exec_order(node->id());
@@ -46,6 +46,7 @@ void program::nodes_ordering::calc_processing_order(program& p) {
output: BFS topological order.
*/
void program::nodes_ordering::calculate_BFS_processing_order() {
GPU_DEBUG_DEFINE_MEM_LOGGER("calculate_BFS_processing_order");
std::map<program_node*, int> distances;
for (auto itr : _processing_order) {
distances[itr] = -1;
@@ -49,6 +49,7 @@ void pass_manager::run(program& p, base_pass& pass) {
using Time = std::chrono::high_resolution_clock;
GPU_DEBUG_LOG << "Run pass " << pass.get_name() << std::endl;
GPU_DEBUG_DEFINE_MEM_LOGGER(pass.get_name());
auto start = Time::now();
pass.run(p);
auto stop = Time::now();
@@ -802,11 +802,14 @@ memory::ptr primitive_inst::allocate_output(engine& _engine, memory_pool& pool,
auto use_lockable_memory = is_output_buffer(_node) || is_cpu || is_any_user_cpu(_node.get_users()) ||
!_engine.supports_allocation(allocation_type::usm_device);
const auto& lockable_mem_type = _engine.get_lockable_preferred_memory_allocation_type(layout.format.is_image_2d());
const auto& alloc_type = use_lockable_memory ? lockable_mem_type
: usm_device_allocatable ? allocation_type::usm_device : lockable_mem_type;
auto alloc_type = use_lockable_memory ? lockable_mem_type
: usm_device_allocatable ? allocation_type::usm_device : lockable_mem_type;
if ((is_internal && (_node.can_be_optimized() || _node.is_type<generic_layer>())) || (memory_reuse_by_user == false)) {
GPU_DEBUG_LOG << "[" << _node.id() << ": output]" << std::endl;
// Use usm_device memory for weights reordering
if (is_internal && _node.is_type<generic_layer>() && _engine.supports_allocation(allocation_type::usm_device))
alloc_type = allocation_type::usm_device;
return get_memory_from_pool(_engine,
layout,
_node.id(),
+9 -1
View File
@@ -174,18 +174,23 @@ void program::init_primitives() {
}
void program::compile() {
GPU_DEBUG_DEFINE_MEM_LOGGER("compile");
_kernels_cache->build_all();
}
void program::init_kernels() {
GPU_DEBUG_DEFINE_MEM_LOGGER("init_kernels");
for (auto& n : get_processing_order()) {
if (n->get_selected_impl())
if (n->get_selected_impl()) {
n->get_selected_impl()->init_kernels(*_kernels_cache);
n->get_selected_impl()->reset_kernels_source();
}
}
}
void program::load_tuning_cache() {
OV_ITT_SCOPED_TASK(itt::domains::CLDNN, "ProgramImpl::LoadTuningCache");
GPU_DEBUG_DEFINE_MEM_LOGGER("ProgramImpl::LoadTuningCache");
try {
tuning_cache = kernel_selector::CreateTuningCacheFromFile(get_engine().configuration().tuning_cache_path);
} catch (...) {
@@ -239,6 +244,7 @@ program_node const& program::get_node(primitive_id const& id) const {
// TODO: Remove once we will get full support for input/output padding in all primitive implementations.
bool program::analyze_output_size_handling_need() {
GPU_DEBUG_DEFINE_MEM_LOGGER("analyze_output_size_handling_need");
bool handling_needed = false;
// Calculate output size and compare with specified.
@@ -700,6 +706,7 @@ void program::mark_if_data_flow(program_node& node) {
}
void program::transfer_memory_to_device() {
GPU_DEBUG_DEFINE_MEM_LOGGER("transfer_memory_to_device");
OV_ITT_SCOPED_TASK(itt::domains::CLDNN, "ProgramImpl::TransferMemory");
if (!get_engine().supports_allocation(allocation_type::usm_device))
return;
@@ -734,6 +741,7 @@ void program::transfer_memory_to_device() {
}
void program::cleanup() {
GPU_DEBUG_DEFINE_MEM_LOGGER("cleanup");
for (auto& node : processing_order)
node->get_output_layout();
@@ -108,6 +108,7 @@ void Plugin::TransformNetwork(std::shared_ptr<ov::Model>& model, const Config& c
InferenceEngine::CNNNetwork Plugin::CloneAndTransformNetwork(const InferenceEngine::CNNNetwork& network,
const Config& config) const {
OV_ITT_SCOPED_TASK(itt::domains::intel_gpu_plugin, "Plugin::CloneAndTransformNetwork");
GPU_DEBUG_DEFINE_MEM_LOGGER("Plugin::CloneAndTransformNetwork");
CNNNetwork clonedNetwork = InferenceEngine::details::cloneNetwork(network);
auto nGraphFunc = clonedNetwork.getFunction();
+17 -2
View File
@@ -2,6 +2,10 @@
// SPDX-License-Identifier: Apache-2.0
//
#if defined(__unix__) && !defined(__ANDROID__)
#include <malloc.h>
#endif
#include "intel_gpu/plugin/program.hpp"
#include "ngraph/ops.hpp"
#include "ov_ops/nms_ie_internal.hpp"
@@ -305,6 +309,14 @@ void Program::CleanupBuild() {
m_topology.reset();
m_networkInputs.clear();
m_networkOutputs.clear();
#if defined(__unix__) && !defined(__ANDROID__)
// NOTE: In linux, without malloc_trim, an amount of the memory used by compilation is not being returned to system thought they are freed.
// (It is at least 500 MB when we perform parallel compilation)
// It is observed that freeing the memory manually with malloc_trim saves significant amount of the memory.
// Also, this is not happening in Windows.
// So, added malloc_trim for linux build until we figure out a better solution.
malloc_trim(0);
#endif
}
std::shared_ptr<cldnn::program> Program::BuildProgram(const std::vector<std::shared_ptr<ngraph::Node>>& ops,
@@ -327,8 +339,11 @@ std::shared_ptr<cldnn::program> Program::BuildProgram(const std::vector<std::sha
options.set_option(cldnn::build_option::partial_build_program(true));
}
PrepareBuild(networkInputs, networkOutputs);
for (const auto& op : ops) {
CreateSingleLayerPrimitive(*m_topology, op);
{
GPU_DEBUG_DEFINE_MEM_LOGGER("CreateSingleLayerPrimitives");
for (const auto& op : ops) {
CreateSingleLayerPrimitive(*m_topology, op);
}
}
if (createTopologyOnly) {
return {};