#include <algorithm>
#include <memory>
#include <set>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include "graph/interface/c_types_map.hpp"
#include "graph/interface/value.hpp"
#include "graph/backend/dnnl/common.hpp"
#include "graph/backend/dnnl/op_executable.hpp"
#include "graph/backend/dnnl/passes/constant_propagation.hpp"
#include "graph/backend/dnnl/passes/memory_planning.hpp"
#include "graph/backend/dnnl/passes/utils.hpp"
#include "oneapi/dnnl/dnnl.hpp"
#define VCHECK_MEMORY_PLANNING(cond, status, msg, ...) \
VCONDCHECK(graph, create, check, memory_planning, (cond), status, msg, \
##__VA_ARGS__);
namespace dnnl {
namespace impl {
namespace graph {
namespace dnnl_impl {
using op_t = op_t;
using ltw = logical_tensor_wrapper_t;
struct op_inplace_pair_t {
op_inplace_pair_t(size_t in_idx, size_t out_idx)
: in_idx_(in_idx), out_idx_(out_idx) {}
const size_t in_idx_; const size_t out_idx_;
};
std::vector<op_inplace_pair_t> get_op_inplace_pairs(op_t &op) {
const static std::set<op_kind_t> ops {
op_kind::_mul_scales,
op_kind::_add_zps,
op_kind::_binary,
op_kind::_eltwise,
op_kind::_softmax,
op_kind::_logsoftmax,
op_kind::_softmax_bwd,
op_kind::_logsoftmax_bwd,
op_kind::_identity,
};
std::vector<op_inplace_pair_t> pairs;
if (op.has_attr(op_attr::fusion_info)) {
const fusion_info_t &fusion_info
= op.get_attr<fusion_info_t>(op_attr::fusion_info);
const auto &pops = fusion_info.get_post_ops();
size_t index = 1;
if (op.get_kind() == op_kind::_convolution
|| op.get_kind() == op_kind::_matmul
|| op.get_kind() == op_kind::_convtranspose) {
index = op.has_attr(op_attr::with_bias)
&& op.get_attr<bool>(op_attr::with_bias)
? 3 : 2; if (fusion_info.with_runtime_scales(true, 0)) { index += 1; }
if (fusion_info.with_runtime_scales(true, 1)) { index += 1; }
if (fusion_info.with_runtime_zero_points(true, 0)) { index += 1; }
if (fusion_info.with_runtime_zero_points(true, 1)) { index += 1; }
} else if (op.get_kind() == op_kind::_binary) {
index = 2;
} else {
}
std::shared_ptr<value_t> post_sum_input;
for (size_t i = 0; i < pops.size(); i++) {
if (pops[i]->is_post_sum()) {
post_sum_input = op.get_input_value(index);
break; } else if (pops[i]->get_op()->get_kind() == op_kind::_binary) {
index++;
} else if (pops[i]->get_op()->get_kind() == op_kind::_convolution) {
index++;
} else {
}
}
if (post_sum_input) {
bool can_inplace = false;
auto post_sum_input_lt = post_sum_input->get_logical_tensor();
auto output_lt = op.get_output_logical_tensor(0);
auto post_sum_input_desc = make_dnnl_memory_desc(post_sum_input_lt);
auto output_desc = make_dnnl_memory_desc(output_lt);
if (op.get_kind() == op_kind::_convolution
&& post_sum_input_lt.data_type == data_type::s8
&& output_lt.data_type == data_type::u8) {
auto format_tag = md2fmt_tag_str(post_sum_input_desc.get());
const auto &dims = post_sum_input_desc.get_dims();
dnnl_memory_desc_t temp_md;
dnnl_memory_desc_create_with_string_tag(&temp_md,
static_cast<int>(dims.size()), dims.data(),
static_cast<dnnl_data_type_t>(output_lt.data_type),
format_tag.data());
can_inplace = output_desc == temp_md;
} else {
can_inplace = output_desc == post_sum_input_desc;
}
if (can_inplace) { pairs.emplace_back(index, 0); }
}
} else if (ops.count(op.get_kind())) {
auto in0 = op.get_input_logical_tensor(0);
auto out0 = op.get_output_logical_tensor(0);
const bool can_inplace
= make_dnnl_memory_desc(in0) == make_dnnl_memory_desc(out0);
if (can_inplace) { pairs.emplace_back(0, 0); }
} else if (op.get_kind() == op_kind::_layernorm_bwd) {
auto diff_dst = op.get_input_logical_tensor(1);
auto diff_src = op.get_output_logical_tensor(0);
const bool can_inplace = make_dnnl_memory_desc(diff_dst)
== make_dnnl_memory_desc(diff_src);
if (can_inplace) { pairs.emplace_back(1, 0); }
} else if (op.get_kind() == op_kind::_transpose
|| op.get_kind() == op_kind::_reshape) {
pairs.emplace_back(0, 0);
} else {
}
return pairs;
}
std::shared_ptr<execution_args_set_t> execution_args_set_t::clone() const {
auto ret = std::make_shared<execution_args_set_t>();
ret->value_mem_map_.reserve(value_mem_map_.size());
for (auto &val_mem : value_mem_map_) {
memory cloned_mem;
if (val_mem.second.get_desc().get_format_kind()
== dnnl::memory::format_kind::host_scalar) {
DNNL_HOST_SCALAR_TYPE_SWITCH(
val_mem.second.get_desc().get_data_type(), DType, {
cloned_mem = dnnl::memory(val_mem.second.get_desc(),
static_cast<DType>(0));
});
} else if (val_mem.second.get_engine().get_kind()
== dnnl::engine::kind::gpu) {
#if DNNL_GPU_RUNTIME == DNNL_RUNTIME_OCL
dnnl::ocl_interop::memory_kind m_kind
= dnnl::ocl_interop::get_memory_kind(val_mem.second);
if (m_kind == dnnl::ocl_interop::memory_kind::usm) {
cloned_mem = dnnl::ocl_interop::make_memory(
val_mem.second.get_desc(), val_mem.second.get_engine(),
dnnl::ocl_interop::memory_kind::usm, nullptr);
} else {
cloned_mem = dnnl::ocl_interop::make_memory(
val_mem.second.get_desc(), val_mem.second.get_engine(),
nullptr);
}
#else
cloned_mem = memory(val_mem.second.get_desc(),
val_mem.second.get_engine(), nullptr);
#endif
} else {
cloned_mem = memory(val_mem.second.get_desc(),
val_mem.second.get_engine(), nullptr);
}
ret->value_mem_map_.insert({val_mem.first, cloned_mem});
}
auto find_val = [&](const memory &mem) -> value_t * {
auto pos = std::find_if(value_mem_map_.begin(), value_mem_map_.end(),
[&](const std::pair<value_t *, memory> &val_mem) {
return val_mem.second.get() == mem.get();
});
assertm(pos != value_mem_map_.end(), "can't find such mem");
if (pos != value_mem_map_.end())
return pos->first;
else
return nullptr;
};
ret->mems_use_external_inputs_.reserve(mems_use_external_inputs_.size());
for (const auto &mem_idx : mems_use_external_inputs_) {
ret->mems_use_external_inputs_.emplace_back(
ret->value_mem_map_.at(find_val(mem_idx.first)),
mem_idx.second);
}
ret->mems_use_external_outputs_.reserve(mems_use_external_outputs_.size());
for (const auto &mem_idx : mems_use_external_outputs_) {
ret->mems_use_external_outputs_.emplace_back(
ret->value_mem_map_.at(find_val(mem_idx.first)),
mem_idx.second);
}
ret->mems_use_internal_temporary_.reserve(
mems_use_internal_temporary_.size());
for (const auto &mem_offkey : mems_use_internal_temporary_) {
ret->mems_use_internal_temporary_.emplace_back(
ret->value_mem_map_.at(find_val(mem_offkey.first)),
mem_offkey.second);
}
ret->mems_use_internal_persistent_.reserve(
mems_use_internal_persistent_.size());
for (const auto &mem_offkey : mems_use_internal_persistent_) {
ret->mems_use_internal_persistent_.emplace_back(
ret->value_mem_map_.at(find_val(mem_offkey.first)),
mem_offkey.second);
}
ret->topo_ordered_exec_args_.reserve(topo_ordered_exec_args_.size());
for (const auto &args : topo_ordered_exec_args_) {
std::unordered_map<int, memory> new_args;
for (const auto &kv : args) {
int idx = kv.first;
const memory &mem = kv.second;
new_args.insert({idx, ret->value_mem_map_.at(find_val(mem))});
}
ret->topo_ordered_exec_args_.emplace_back(new_args);
}
return ret;
}
void execution_args_set_t::clear() {
mems_use_external_inputs_.clear();
mems_use_external_outputs_.clear();
mems_use_internal_temporary_.clear();
mems_use_internal_persistent_.clear();
value_mem_map_.clear();
topo_ordered_exec_args_.clear();
}
void alias_analyzer_t::clear() {
alias_map_.clear();
reverse_alias_map_.clear();
}
status_t alias_analyzer_t::run(std::shared_ptr<subgraph_t> &sg) {
clear();
for (auto &cur_op : sg->get_ops()) {
if (!is_preprocess_op(*cur_op)) continue;
value_t *out = cur_op->get_output_value(0).get();
value_t *in = cur_op->get_input_value(0).get();
alias_map_.insert({out, in});
reverse_alias_map_.insert({in, out});
}
return status::success;
}
std::vector<const value_t *> alias_analyzer_t::get_alias_outputs(
const value_t *input) const {
std::vector<const value_t *> alias_output;
for (const auto &in_out : reverse_alias_map_) {
if (in_out.first != input) continue;
alias_output.emplace_back(in_out.second);
}
return alias_output;
}
const value_t *alias_analyzer_t::get_alias_input(const value_t *output) const {
if (alias_map_.count(output)) { return alias_map_.at(output); }
return nullptr;
}
std::vector<const value_t *> alias_analyzer_t::get_all_aliases(
const value_t *val) const {
std::queue<const value_t *> q;
std::set<const value_t *> visited;
q.push(val);
visited.insert(val);
while (!q.empty()) {
auto temp = q.front();
q.pop();
auto alias_outputs = get_alias_outputs(temp);
for (const auto &alias : alias_outputs) {
if (visited.count(alias)) continue;
q.push(alias);
visited.insert(alias);
}
auto alias_input = get_alias_input(temp);
if (alias_input && !visited.count(alias_input)) {
q.push(alias_input);
visited.insert(alias_input);
}
}
std::vector<const value_t *> ret;
ret.reserve(visited.size() - 1);
for (auto &alias : visited) {
if (alias == val) continue;
ret.emplace_back(alias);
}
return ret;
}
status_t memory_planner_t::assign_external_inputs_buffer(
std::shared_ptr<subgraph_t> &sg,
const std::vector<logical_tensor_t> &inputs) {
auto sg_ins = sg->get_input_values();
std::sort(sg_ins.begin(), sg_ins.end());
sg_ins.erase(std::unique(sg_ins.begin(), sg_ins.end()), sg_ins.end());
for (auto &val : sg_ins) {
for (size_t i = 0; i < inputs.size(); i++) {
if (val->get_logical_tensor().id == inputs[i].id) {
assign_info_t info(external_input, i);
buffer_assignments_.insert(std::make_pair(val, info));
auto aliases = alias_analyzer_.get_all_aliases(val);
for (auto &alias : aliases) {
VCHECK_MEMORY_PLANNING(!buffer_assignments_.count(alias),
status::runtime_error,
"alias of input has been assigned buffer");
buffer_assignments_.insert(std::make_pair(alias, info));
}
break;
}
}
}
size_t time_point = 0;
status_t ret;
ret = topo_order_visit(sg->get_output_ops(), [&](op_t *op) {
auto in_vals = op->get_input_values();
for (auto &in_val : in_vals) {
if (!buffer_assignments_.count(in_val.get())) continue;
const auto &info = buffer_assignments_.at(in_val.get());
if (info.kind_ != external_input) continue;
external_inputs_live_range_[&info] = time_bound_t {0, time_point};
}
time_point++;
return status::success;
});
return ret;
}
status_t memory_planner_t::assign_external_outputs_buffer(
std::shared_ptr<subgraph_t> &sg,
const std::vector<logical_tensor_t> &outputs) {
for (const auto &op : sg->get_ops()) {
for (const auto &val : op->get_output_values()) {
for (size_t i = 0; i < outputs.size(); i++) {
if (val->get_logical_tensor().id == outputs[i].id) {
assign_info_t orig_info = buffer_assignments_.at(val.get());
assign_info_t updated_info(external_output, i);
std::queue<const value_t *> q;
std::set<const value_t *> visited;
q.push(val.get());
while (!q.empty()) {
auto cur_val = q.front();
q.pop();
if (visited.count(cur_val)) continue;
buffer_assignments_[cur_val] = updated_info;
visited.insert(cur_val);
auto aliases = alias_analyzer_.get_all_aliases(cur_val);
for (const value_t *alias : aliases) {
if (buffer_assignments_[alias].kind_
== external_input)
continue;
q.push(alias);
}
if (!cur_val->has_producer()) continue;
auto &producer = cur_val->get_producer();
auto op_inplace_pairs = get_op_inplace_pairs(producer);
for (auto &pair : op_inplace_pairs) {
if (pair.out_idx_ != cur_val->get_offset())
continue;
auto in_val
= producer.get_input_value(pair.in_idx_);
if (buffer_assignments_.at(in_val.get())
!= orig_info
|| buffer_assignments_.at(in_val.get())
.kind_
== external_input)
continue;
q.push(in_val.get());
}
}
}
}
}
}
return status::success;
}
status_t memory_planner_t::assign_internal_persistent_buffer(
std::shared_ptr<subgraph_t> &sg) {
for (auto &val : get_constant_block_output_values(sg)) {
assign_info_t orig_info = buffer_assignments_.at(val);
if (orig_info.kind_ != internal_temporary) continue;
size_t idx = persistent_buffer_assigner_.request(
make_dnnl_memory_desc(val->get_logical_tensor()).get_size());
assign_info_t updated_info(internal_persistent, idx);
std::queue<const value_t *> q;
std::set<const value_t *> visited;
q.push(val);
while (!q.empty()) {
auto cur_val = q.front();
q.pop();
if (visited.count(cur_val) || !cur_val->has_producer()) continue;
buffer_assignments_[cur_val] = updated_info;
visited.insert(cur_val);
auto aliases = alias_analyzer_.get_all_aliases(cur_val);
for (const value_t *alias : aliases) {
q.push(alias);
}
auto &producer = cur_val->get_producer();
auto op_inplace_pairs = get_op_inplace_pairs(producer);
for (auto &pair : op_inplace_pairs) {
if (pair.out_idx_ != cur_val->get_offset()) continue;
auto in_val = producer.get_input_value(pair.in_idx_);
if (buffer_assignments_.at(in_val.get()) != orig_info) continue;
q.push(in_val.get());
}
}
}
return status::success;
}
status_t memory_planner_t::assign_internal_temporary_buffer(
std::shared_ptr<subgraph_t> &sg,
const std::unordered_map<value_t *, size_t> &edge_ref_count,
bool enable_standard_sharing) {
std::unordered_map<size_t, size_t> temporary_buffer_ref_count;
auto func = [&](op_t *op) {
auto inputs = op->get_input_values();
for (auto &in : inputs) {
auto alias_outputs = alias_analyzer_.get_alias_outputs(in.get());
for (auto &alias : alias_outputs) {
if (buffer_assignments_.count(alias)) { continue; }
assign_info_t info = buffer_assignments_.at(in.get());
buffer_assignments_.insert(std::make_pair(alias, info));
temporary_buffer_ref_count[info.index_]
+= edge_ref_count.at(const_cast<value_t *>(alias));
}
}
auto op_inplace_pairs = get_op_inplace_pairs(*op);
if (!op_inplace_pairs.empty()) {
for (const auto &pair : op_inplace_pairs) {
value_t *in = op->get_input_value(pair.in_idx_).get();
assign_info_t info = buffer_assignments_.at(in);
if (info.kind_ != internal_temporary) continue;
bool reuse_in_buffer
= temporary_buffer_ref_count[info.index_] == 1;
if (reuse_in_buffer) {
value_t *out = op->get_output_value(pair.out_idx_).get();
if (!buffer_assignments_.count(out)) {
buffer_assignments_.insert(std::make_pair(out, info));
temporary_buffer_ref_count[info.index_]
+= edge_ref_count.at(out);
}
}
}
}
for (auto &out : op->get_output_values()) {
if (buffer_assignments_.count(out.get())) continue;
auto lt = out->get_logical_tensor();
size_t idx = temporary_buffer_assigner_.request(
make_dnnl_memory_desc(lt).get_size());
buffer_assignments_.insert(std::make_pair(
out.get(), assign_info_t(internal_temporary, idx)));
temporary_buffer_ref_count[idx] = edge_ref_count.at(out.get());
}
for (auto &in : op->get_input_values()) {
assign_info_t info = buffer_assignments_.at(in.get());
if (info.kind_ != internal_temporary) continue;
--temporary_buffer_ref_count[info.index_];
if (enable_standard_sharing
&& temporary_buffer_ref_count[info.index_] == 0) {
temporary_buffer_assigner_.release(info.index_);
}
}
for (auto &out : op->get_output_values()) {
assign_info_t info = buffer_assignments_.at(out.get());
if (info.kind_ != internal_temporary) continue;
const auto &consumers = out->get_consumers();
if (consumers.empty()) {
--temporary_buffer_ref_count[info.index_];
if (enable_standard_sharing) {
temporary_buffer_assigner_.release(info.index_);
}
}
}
return status::success;
};
return topo_order_visit(sg->get_output_ops(), func);
}
status_t memory_planner_t::prepare_subgraph_inplace_pairs(
std::shared_ptr<subgraph_t> &sg, bool enable_standard_sharing) {
size_t time_point = 0;
status_t ret;
ret = topo_order_visit(sg->get_output_ops(), [&](op_t *cur_op) {
auto out_vals = cur_op->get_output_values();
for (auto &out_val : out_vals) {
auto out_buf = buffer_assignments_.at(out_val.get());
if (out_buf.kind_ != external_output) continue;
logical_tensor_t out_lt = sg->outs_[out_buf.index_];
logical_tensor_t in_lt = zero_logical_tensor();
bool inplace_shared = false;
auto op_inplace_pairs = get_op_inplace_pairs(*cur_op);
for (const auto &pair : op_inplace_pairs) {
if (pair.out_idx_ != out_val->get_offset()) continue;
auto in_val = cur_op->get_input_value(pair.in_idx_);
auto in_buf = buffer_assignments_.at(in_val.get());
if (in_buf.kind_ != external_input) continue;
in_lt = sg->ins_[in_buf.index_];
inplace_shared = true;
break;
}
bool standard_shared = false;
if (enable_standard_sharing && !inplace_shared) {
std::vector<logical_tensor_t> candidates;
for (auto &ex_in : external_inputs_live_range_) {
if (ex_in.second.end_ >= time_point) continue;
auto in_md = make_dnnl_memory_desc(
sg->ins_[ex_in.first->index_]);
auto out_md
= make_dnnl_memory_desc(sg->outs_[out_buf.index_]);
if (in_md.get_size() != out_md.get_size()) continue;
candidates.emplace_back(sg->ins_[ex_in.first->index_]);
}
if (!candidates.empty()) {
in_lt = candidates[0];
for (const auto &tmp : candidates) {
if (tmp.id > in_lt.id) { in_lt = tmp; }
}
standard_shared = true;
}
}
if (!inplace_shared && !standard_shared) continue;
bool have_shared = false;
for (const auto &pair : inplace_pairs_) {
if (pair.output_id == out_lt.id || pair.input_id == in_lt.id)
have_shared = true;
}
if (have_shared) continue;
ltw in_ltw(in_lt), out_ltw(out_lt);
bool can_share = in_ltw.property_type() != property_type::constant
&& in_ltw.layout_type() == out_ltw.layout_type();
if (can_share)
inplace_pairs_.push_back({in_ltw.id(), out_ltw.id()});
}
time_point++;
return status::success;
});
return ret;
}
status_t memory_planner_t::book_buffers(std::shared_ptr<subgraph_t> &sg) {
std::vector<value_t *> to_be_booked;
topo_order_visit(sg->get_output_ops(), [&](op_t *op) {
for (auto &in : op->get_input_values()) {
to_be_booked.emplace_back(in.get());
}
for (auto &out : op->get_output_values()) {
to_be_booked.emplace_back(out.get());
}
return status::success;
});
registrar_t temporary_registrar = temporary_registry_.registrar();
registrar_t persistent_registrar = persistent_registry_.registrar();
for (const value_t *val : to_be_booked) {
const assign_info_t &info = buffer_assignments_.at(val);
switch (info.kind_) {
case external_input:
case external_output: break;
case internal_temporary:
temporary_registrar.book(info.index_,
temporary_buffer_assigner_.query_size(info.index_));
break;
case internal_persistent:
persistent_registrar.book(info.index_,
persistent_buffer_assigner_.query_size(info.index_));
break;
default:
VCHECK_MEMORY_PLANNING(false, status::unimplemented,
"booking memory failed for unimplemented buffer kind "
"%d",
info.kind_);
}
}
return status::success;
}
status_t memory_planner_t::prepare_execution_args_set(
std::shared_ptr<subgraph_t> &sg, const dnnl::engine &p_engine) {
status_t ret;
auto classify_mem = [&, this](const dnnl::memory &mem, const value_t *val) {
const assign_info_t &info = buffer_assignments_.at(val);
switch (info.kind_) {
case external_input:
exec_args_set_.add_mem_use_external_inputs({mem, info.index_});
break;
case external_output:
exec_args_set_.add_mem_use_external_outputs({mem, info.index_});
break;
case internal_temporary:
exec_args_set_.add_mem_use_internal_temporary(
{mem, info.index_});
break;
case internal_persistent:
exec_args_set_.add_mem_use_internal_persistent(
{mem, info.index_});
break;
default: break;
}
};
std::unordered_set<value_t *> prepared;
ret = topo_order_visit(sg->get_output_ops(), [&](op_t *op) {
for (auto &in : op->get_input_values()) {
if (prepared.count(in.get())) continue;
const logical_tensor_t in_lt = in->get_logical_tensor();
const logical_tensor_wrapper_t ltw(in_lt);
auto md = make_dnnl_memory_desc(in_lt);
dnnl::memory mem;
if (ltw.is_host_scalar()) {
DNNL_HOST_SCALAR_TYPE_SWITCH(md.get_data_type(), DType,
{ mem = dnnl::memory(md, static_cast<DType>(0)); });
} else {
mem = make_dnnl_memory(md, p_engine, nullptr);
}
exec_args_set_.add_value_mem_map({in.get(), mem});
classify_mem(mem, in.get());
prepared.insert(in.get());
}
for (auto &out : op->get_output_values()) {
auto md = make_dnnl_memory_desc(out->get_logical_tensor());
auto mem = make_dnnl_memory(md, p_engine, nullptr);
exec_args_set_.add_value_mem_map({out.get(), mem});
classify_mem(mem, out.get());
prepared.insert(out.get());
}
return status::success;
});
VCHECK_MEMORY_PLANNING(
ret == status::success, ret, "prepare memory failed");
ret = topo_order_visit(sg->get_output_ops(), [&](op_t *op) {
auto getter = op_func_t::get_arg_indices_getter(op->get_kind());
VCHECK_MEMORY_PLANNING(getter != nullptr, status::invalid_graph_op,
"no arg indices getter in the schema of op: %s",
op->get_name().c_str());
auto arg_indices = getter(op);
exec_args dnnl_exec_args;
for (auto arg_idx : arg_indices) {
int dnnl_arg = arg_idx.first;
indices_t::type_t type = arg_idx.second.type_;
size_t index = arg_idx.second.value_;
value_t *val = type == indices_t::type_t::input
? op->get_input_value(index).get()
: op->get_output_value(index).get();
dnnl::memory mem;
if (!exec_args_set_.find_value_mem_map(val, mem)) {
VCHECK_MEMORY_PLANNING(false, status::invalid_arguments,
"can't find memory for value id: %zu",
val->get_logical_tensor().id);
} else {
dnnl_exec_args.insert({dnnl_arg, mem});
}
}
exec_args_set_.add_exec_args(dnnl_exec_args);
return status::success;
});
return ret;
}
status_t memory_planner_t::run(std::shared_ptr<subgraph_t> &sg) {
const auto &p_engine = *(sg->p_engine_);
const auto &inputs = sg->ins_;
const auto &outputs = sg->outs_;
clear();
alias_analyzer_.run(sg);
std::unordered_map<value_t *, size_t> edge_ref_count;
for (auto &cur_op : sg->get_ops()) {
auto in_vals = cur_op->get_input_values();
for (auto &val : in_vals) {
edge_ref_count[val.get()]++;
}
}
for (const auto &val : sg->get_output_values()) {
edge_ref_count[val]++;
}
bool enable_memory_sharing
= graph::utils::getenv_int_internal("ENABLE_MEM_REUSE", 1) > 0;
if (!enable_memory_sharing) {
for (auto &val_count : edge_ref_count) {
val_count.second++;
}
}
CHECK(assign_external_inputs_buffer(sg, inputs));
CHECK(assign_internal_temporary_buffer(sg, edge_ref_count, false));
CHECK(assign_external_outputs_buffer(sg, outputs));
CHECK(assign_internal_persistent_buffer(sg));
temporary_buffer_assigner_.clear();
for (auto it = buffer_assignments_.begin();
it != buffer_assignments_.end();) {
if (it->second.kind_ == internal_temporary) {
it = buffer_assignments_.erase(it);
} else {
it++;
}
}
CHECK(assign_internal_temporary_buffer(sg, edge_ref_count, true));
CHECK(prepare_subgraph_inplace_pairs(sg, false));
CHECK(book_buffers(sg));
CHECK(prepare_execution_args_set(sg, p_engine));
return status::success;
}
} } } }