GitOrigin-RevId: b12f1c4a66
release-1.7
@@ -15,6 +15,8 @@ | |||
#include "megdnn/dtype.h" | |||
#include "megdnn/internal/defs.h" | |||
#include <memory> | |||
#if MEGDNN_CC_HOST | |||
#include <cstdarg> | |||
#include <string> | |||
@@ -402,31 +404,94 @@ struct TensorLayout : public TensorShape { | |||
MGE_WIN_DECLSPEC_FUC size_t access_bytes() const; | |||
}; | |||
class RefPtr { | |||
std::shared_ptr<void*> m_ref; | |||
size_t m_offset; | |||
bool m_mutable; | |||
public: | |||
RefPtr() { | |||
m_ref = std::make_shared<void*>((void*)nullptr); | |||
m_offset = 0; | |||
m_mutable = true; | |||
} | |||
RefPtr(void* ref_ptr, const size_t offset = 0) { | |||
m_ref = std::make_shared<void*>(ref_ptr); | |||
m_offset = offset; | |||
m_mutable = true; | |||
} | |||
explicit RefPtr( | |||
std::shared_ptr<void*> ref_ptr, const size_t offset = 0, | |||
bool is_mutable = true) { | |||
m_ref = ref_ptr; | |||
m_offset = offset; | |||
m_mutable = is_mutable; | |||
} | |||
void* get_ptr() const { | |||
return static_cast<void*>( | |||
(*m_ref != NULL) ? static_cast<dt_byte*>(*m_ref) + m_offset : nullptr); | |||
} | |||
bool is_mutable() const { return m_mutable; } | |||
void reset(const void* ptr, size_t offset = 0); | |||
RefPtr& operator+=(size_t offset) { | |||
m_offset += offset; | |||
return *this; | |||
} | |||
bool operator==(const RefPtr& other) const { | |||
return *m_ref == *other.m_ref && m_offset == other.m_offset; | |||
} | |||
template <typename T> | |||
T* ptr() const { | |||
return static_cast<T*>(get_ptr()); | |||
} | |||
}; | |||
/** | |||
* \brief A simple encapsulation class for n-dimensional tensor. | |||
*/ | |||
struct TensorND { | |||
void* raw_ptr; | |||
TensorLayout layout; | |||
TensorND() : raw_ptr(NULL) {} | |||
TensorND() : m_ref_ptr(RefPtr((void*)nullptr)) {} | |||
TensorND(void* raw_ptr_, const TensorLayout& layout_) | |||
: raw_ptr(raw_ptr_), layout(layout_) {} | |||
: layout(layout_), m_ref_ptr(raw_ptr_) {} | |||
TensorND(const TensorLayout& layout_, const RefPtr& ref_ptr) | |||
: layout(layout_), m_ref_ptr(ref_ptr) {} | |||
MGE_WIN_DECLSPEC_FUC void reset_ptr(void* ptr, size_t offset = 0); | |||
void* raw_ptr() const { return m_ref_ptr.get_ptr(); } | |||
const RefPtr get_ref_ptr() const { return m_ref_ptr; } | |||
RefPtr& get_ref_ptr() { return m_ref_ptr; } | |||
//! get typed pointer; type check is performed | |||
template <typename T> | |||
T* ptr() const { | |||
layout.dtype.assert_is_ctype<T>(); | |||
return static_cast<T*>(raw_ptr); | |||
return static_cast<T*>(m_ref_ptr.get_ptr()); | |||
} | |||
//! get typed pointer of compatible type | |||
template <typename T> | |||
T* compatible_ptr() const { | |||
layout.dtype.assert_is_compatible_ctype<T>(); | |||
return reinterpret_cast<T*>(raw_ptr); | |||
return reinterpret_cast<T*>(m_ref_ptr.get_ptr()); | |||
} | |||
private: | |||
RefPtr m_ref_ptr; | |||
}; | |||
#if MEGDNN_CC_HOST | |||
@@ -605,4 +605,14 @@ std::string TensorLayout::serialize() const { | |||
return rst; | |||
} | |||
void RefPtr::reset(const void* ptr, size_t offset) { | |||
megdnn_assert(m_mutable, "this RefPtr can't change."); | |||
*m_ref = const_cast<void*>(ptr); | |||
m_offset = offset; | |||
} | |||
void TensorND::reset_ptr(void* ptr, size_t offset) { | |||
m_ref_ptr.reset(ptr, offset); | |||
} | |||
// vim: syntax=cpp.doxygen |
@@ -342,7 +342,7 @@ void param_pack_concat_execute( | |||
[comp_node](dt_byte* ptr) { comp_node.free_host(ptr); }}; | |||
TensorLayout srcs_layout = TensorLayout{{nr_inputs}, dtype::Int32()}; | |||
for (size_t i = 0; i < nr_inputs; ++i) { | |||
srcs_raw_ptr[i] = inputs[i]->dev_tensor().as_megdnn().raw_ptr; | |||
srcs_raw_ptr[i] = inputs[i]->dev_tensor().as_megdnn().raw_ptr(); | |||
} | |||
HostTensorStorage srcs_storage; | |||
srcs_storage.reset(comp_node, srcs_size, srcs_ptr); | |||
@@ -392,7 +392,7 @@ SmallVector<TensorPtr> param_pack_concat_apply_on_physical_tensor( | |||
src_shapes, inputs.back()->shape(), TensorShape{}); | |||
} | |||
for (size_t i = 0; i < nr_inputs; ++i) { | |||
srcs_raw_ptr[i] = inputs[i]->dev_tensor().as_megdnn().raw_ptr; | |||
srcs_raw_ptr[i] = inputs[i]->dev_tensor().as_megdnn().raw_ptr(); | |||
} | |||
HostTensorStorage srcs_storage; | |||
srcs_storage.reset(comp_node, srcs_size, srcs_ptr); | |||
@@ -26,7 +26,7 @@ TensorChecksumCalc::ChecksumResult TensorChecksumCalc::calc(TensorPtr ptr) { | |||
auto span = dt.layout().span(); | |||
megdnn::TensorND tensor; | |||
tensor.raw_ptr = dt.raw_ptr() + span.low_byte; | |||
tensor.reset_ptr(dt.raw_ptr() + span.low_byte); | |||
tensor.layout.init_contiguous_stride({span.dist_byte()}); | |||
tensor.layout.dtype = dtype::Byte(); | |||
@@ -527,6 +527,10 @@ void NetworkImplDft::update_input() { | |||
config_in.lite_tensor->set_layout( | |||
to_lite_layout(in_tensor_iter.second->layout())); | |||
} | |||
TensorHelper::implement(config_in.lite_tensor) | |||
->cast_final_safe<TensorImplDft>() | |||
.m_record_reset = | |||
m_user_config->options.comp_node_seq_record_level > 0; | |||
if (config_in.config_layout.ndim && | |||
!(config_in.config_layout == config_in.lite_tensor->get_layout())) { | |||
config_in.lite_tensor->set_layout(config_in.config_layout); | |||
@@ -541,6 +545,10 @@ void NetworkImplDft::update_input() { | |||
TensorHelper::implement(io_in.lite_tensor) | |||
->cast_final_safe<TensorImplDft>() | |||
.m_host_tensor = in_tensor_iter.second; | |||
TensorHelper::implement(io_in.lite_tensor) | |||
->cast_final_safe<TensorImplDft>() | |||
.m_record_reset = | |||
m_user_config->options.comp_node_seq_record_level > 0; | |||
io_in.lite_tensor->update_from_implement(); | |||
m_network_io->inputs.push_back(io_in); | |||
} | |||
@@ -603,6 +611,10 @@ void NetworkImplDft::update_output() { | |||
} | |||
try_infer_tensor_layout(out_it->lite_tensor, var); | |||
output_tensor_copy_optimize(var, out_it->lite_tensor); | |||
TensorHelper::implement(out_it->lite_tensor) | |||
->cast_final_safe<TensorImplDft>() | |||
.m_record_reset = | |||
m_user_config->options.comp_node_seq_record_level > 0; | |||
} | |||
//! user not set, use default output | |||
} else { | |||
@@ -631,6 +643,10 @@ void NetworkImplDft::update_output() { | |||
lite_tensor = output.lite_tensor; | |||
} | |||
output_tensor_copy_optimize(out, lite_tensor); | |||
TensorHelper::implement(lite_tensor) | |||
->cast_final_safe<TensorImplDft>() | |||
.m_record_reset = | |||
m_user_config->options.comp_node_seq_record_level > 0; | |||
} | |||
} | |||
} | |||
@@ -643,14 +659,20 @@ void NetworkImplDft::output_tensor_copy_optimize( | |||
"Can't set force_output_use_user_specified_memory and " | |||
"force_output_dynamic_alloc at the same time."); | |||
if (m_user_config->options.force_output_use_user_specified_memory) { | |||
bool in_record = m_user_config->options.comp_node_seq_record_level > 0; | |||
TensorHelper::implement(tensor) | |||
->cast_final_safe<TensorImplDft>() | |||
.set_reset_callback([var](TensorImplDft* dft_tensor) { | |||
.set_reset_callback([var, in_record](TensorImplDft* dft_tensor) { | |||
dft_tensor->device_share_host_memory(); | |||
auto dv = dft_tensor->dev_tensor().get(); | |||
dv->comp_node(var.node()->comp_node(), true); | |||
var.node()->init_mem_plan(dv); | |||
var.node()->reset_dev_tensor_from_tensor(*dv); | |||
if (in_record) { | |||
auto&& device_tensor = var.node()->mutable_dev_tensor(); | |||
device_tensor.only_reset_raw_storage(dv->storage()); | |||
} else { | |||
var.node()->reset_dev_tensor_from_tensor(*dv); | |||
} | |||
}); | |||
} | |||
if (m_user_config->options.force_output_dynamic_alloc) { | |||
@@ -314,14 +314,22 @@ void TensorImplDft::reset(void* prepared_data) { | |||
size_t size = mge_layout.span().dist_byte(); | |||
mgb::HostTensorStorage storage; | |||
storage.reset(cn, size, raw_storage); | |||
m_host_tensor->reset(storage, mge_layout); | |||
if (m_record_reset) { | |||
m_host_tensor->only_reset_raw_storage(storage); | |||
} else { | |||
m_host_tensor->reset(storage, mge_layout); | |||
} | |||
} else { | |||
auto cn = m_dev_tensor->comp_node(); | |||
auto mge_layout = m_dev_tensor->layout(); | |||
size_t size = mge_layout.span().dist_byte(); | |||
mgb::DeviceTensorStorage storage; | |||
storage.reset(cn, size, raw_storage); | |||
m_dev_tensor->reset(storage, mge_layout); | |||
if (m_record_reset) { | |||
m_dev_tensor->only_reset_raw_storage(storage); | |||
} else { | |||
m_dev_tensor->reset(storage, mge_layout); | |||
} | |||
} | |||
if (m_reset_callback) { | |||
m_reset_callback(this); | |||
@@ -455,14 +463,9 @@ void TensorImplDft::device_share_host_memory() { | |||
m_host_tensor->comp_node(), m_host_tensor->layout()); | |||
} | |||
if (m_host_tensor->raw_ptr() != m_dev_tensor->raw_ptr()) { | |||
auto raw_storage = std::shared_ptr<mgb::dt_byte>( | |||
m_host_tensor->raw_ptr(), [](void*) {}); | |||
auto cn = m_host_tensor->comp_node(); | |||
auto mge_layout = m_host_tensor->layout(); | |||
size_t size = mge_layout.span().dist_byte(); | |||
mgb::DeviceTensorStorage storage; | |||
storage.reset(cn, size, raw_storage); | |||
m_dev_tensor->reset(storage, mge_layout); | |||
auto&& storage = | |||
mgb::DeviceTensorStorage::make_proxy(m_host_tensor->storage()); | |||
m_dev_tensor->only_reset_raw_storage(storage); | |||
} | |||
} | |||
} | |||
@@ -126,6 +126,7 @@ private: | |||
void set_mge_tensor_compnode(const mgb::CompNode& comp_node); | |||
private: | |||
bool m_record_reset = false; | |||
std::function<void(TensorImplDft*)> m_get_memory_callback; | |||
std::function<void(TensorImplDft*)> m_reset_callback; | |||
std::shared_ptr<mgb::HostTensorND> m_host_tensor; | |||
@@ -412,9 +412,12 @@ TEST(TestNetWork, ResetOutput) { | |||
compare_lite_tensor<float>(output_tensor, result_mgb); | |||
} | |||
TEST(TestNetWork, OutputNoCopy) { | |||
namespace { | |||
void test_output_no_copy(int record) { | |||
Config config; | |||
config.options.force_output_use_user_specified_memory = true; | |||
config.options.comp_node_seq_record_level = record; | |||
auto tensor = get_input_data("./input_data.npy"); | |||
std::string model_path = "./shufflenet.mge"; | |||
std::string input_name = "data"; | |||
@@ -453,6 +456,65 @@ TEST(TestNetWork, OutputNoCopy) { | |||
} | |||
} | |||
void test_input_no_copy(int record) { | |||
Config config; | |||
config.options.force_output_use_user_specified_memory = true; | |||
config.options.comp_node_seq_record_level = record; | |||
std::string model_path = "./shufflenet.mge"; | |||
std::string input_name = "data"; | |||
Layout layout_in{{1, 3, 224, 224}, 4}; | |||
std::vector<std::shared_ptr<Tensor>> inputs; | |||
std::vector<std::shared_ptr<Tensor>> outputs; | |||
for (int i = 0; i < 3; i++) { | |||
auto tmp_in = std::make_shared<Tensor>(LiteDeviceType::LITE_CPU, layout_in); | |||
auto ptr = static_cast<float*>(tmp_in->get_memory_ptr()); | |||
for (size_t id = 0; id < 2 * 224 * 224; id++) { | |||
ptr[id] = i + 1; | |||
} | |||
inputs.push_back(tmp_in); | |||
outputs.push_back(mgb_lar(model_path, config, input_name, tmp_in)); | |||
} | |||
std::shared_ptr<Network> network = std::make_shared<Network>(config); | |||
network->load_model(model_path); | |||
std::shared_ptr<Tensor> input_tensor = network->get_io_tensor(input_name); | |||
std::shared_ptr<Tensor> output_tensor = network->get_output_tensor(0); | |||
for (int i = 0; i < 3; i++) { | |||
auto ptr = inputs[i]->get_memory_ptr(); | |||
input_tensor->reset(ptr, layout_in); | |||
auto tmp_out = std::make_shared<Tensor>( | |||
LiteDeviceType::LITE_CPU, | |||
Layout{{1, 1000}, 2, LiteDataType::LITE_FLOAT}); | |||
output_tensor->reset(tmp_out->get_memory_ptr(), output_tensor->get_layout()); | |||
network->forward(); | |||
network->wait(); | |||
compare_lite_tensor<float>(output_tensor, outputs[i]); | |||
} | |||
} | |||
} // namespace | |||
TEST(TestNetWork, OutputNoCopy) { | |||
test_output_no_copy(0); | |||
} | |||
TEST(TestNetWork, OutputNoCopyRecord) { | |||
test_output_no_copy(1); | |||
} | |||
TEST(TestNetWork, IONoCopy) { | |||
test_input_no_copy(0); | |||
} | |||
TEST(TestNetWork, IONoCopyRecord) { | |||
test_input_no_copy(1); | |||
} | |||
TEST(TestNetWork, OutputDynamicAlloc) { | |||
Config config; | |||
config.options.force_output_dynamic_alloc = true; | |||
@@ -250,9 +250,14 @@ std::unique_ptr<CompNodeSeqRecorder> ComputingGraphImpl::ComputingSequence:: | |||
"graph."); | |||
return {}; | |||
} | |||
auto is_graph_dest_varnode = [&](VarNode* var) { | |||
return ComputingGraphImpl::downcast(owner_graph())->var_receiver(var).size() == | |||
0; | |||
}; | |||
for (auto i : *m_opr_seq) { | |||
for (auto j : i->output()) { | |||
if (!is_static_var_storage(j)) { | |||
if (!is_static_var_storage(j) && !is_graph_dest_varnode(j)) { | |||
mgb_log_error( | |||
"can not enable CompNodeSeqRecorder because var " | |||
"storage not static: %s", | |||
@@ -319,7 +319,7 @@ bool VarNodeMemManager::DynamicAllocOprInfo::check_if_mem_status_change() { | |||
for (size_t i = 0; i < dev_val_input.size(); i++) { | |||
auto&& t = prev_dev_val_input[i]; | |||
auto s = dev_val_input[i]->dev_tensor().as_megdnn(); | |||
if (t.raw_ptr != s.raw_ptr || !t.layout.eq_layout(s.layout)) { | |||
if (t.raw_ptr() != s.raw_ptr() || !t.layout.eq_layout(s.layout)) { | |||
same = false; | |||
t = s; | |||
} | |||
@@ -233,6 +233,7 @@ TensorStorage<Trait>& TensorStorage<Trait>::operator=(const TensorStorage& rhs) | |||
m_capacity = rhs.m_capacity; | |||
m_offset = rhs.m_offset; | |||
m_data = rhs.m_data; | |||
m_ref_ptr = rhs.m_ref_ptr; | |||
return *this; | |||
} | |||
@@ -264,7 +265,8 @@ TensorStorage<Trait> TensorStorage<Trait>::sub(ptrdiff_t offset) const { | |||
m_size - offset, | |||
m_capacity - offset, | |||
static_cast<size_t>(toff), | |||
m_data}; | |||
m_data, | |||
m_ref_ptr}; | |||
} | |||
template <class Trait> | |||
@@ -278,8 +280,10 @@ dt_byte* TensorStorage<Trait>::apply_lazy_and_get_ptr() { | |||
mgb_throw_if(!ptr, SystemError, "failed to allocate memory"); | |||
CompNode cn = m_comp_node; | |||
m_data.reset(ptr, [cn](void* p) { Trait::free(cn, p); }); | |||
m_ref_ptr = std::make_shared<void*>(static_cast<void*>(nullptr)); | |||
m_capacity = m_size; | |||
} | |||
*m_ref_ptr = static_cast<void*>(m_data.get()); | |||
return m_data.get() + m_offset; | |||
} | |||
@@ -305,6 +309,19 @@ void TensorStorage<Trait>::reset(CompNode node, size_t size, RawStorage data) { | |||
m_capacity = size; | |||
m_offset = 0; | |||
m_data = std::move(data); | |||
m_ref_ptr = std::make_shared<void*>(static_cast<void*>(m_data.get())); | |||
} | |||
template <class Trait> | |||
void TensorStorage<Trait>::only_reset_raw_storage( | |||
CompNode node, size_t size, RawStorage data, size_t offset) { | |||
mgb_assert(m_allow_realloc); | |||
m_comp_node = node; | |||
m_size = size; | |||
m_capacity = size; | |||
m_offset = offset; | |||
m_data = std::move(data); | |||
*m_ref_ptr = static_cast<void*>(m_data.get()); | |||
} | |||
template <class Trait> | |||
@@ -316,8 +333,8 @@ TensorStorage<Trait> TensorStorage<Trait>::make_proxy( | |||
"proxy source should be on CPU; got %s", | |||
src.comp_node().to_string().c_str()); | |||
src.ptr(); | |||
return {true, src.m_comp_node, src.m_size, | |||
src.m_capacity, src.m_offset, src.m_data}; | |||
return {true, src.m_comp_node, src.m_size, src.m_capacity, | |||
src.m_offset, src.m_data, src.m_ref_ptr}; | |||
} | |||
template <class Trait> | |||
@@ -481,6 +498,17 @@ DEF(reset, &)(TensorStorage storage, const TensorLayout& layout) { | |||
return static_cast<ChainReturnType&>(*this); | |||
} | |||
DEF(only_reset_raw_storage, &)(TensorStorage storage) { | |||
//! The storage to be reset is either satisfy the layout or empty. | |||
//! Empty storage is used after weight preprocess for saving memory and | |||
//! checking layout when running | |||
mgb_assert(storage.valid_span(m_layout.span()) || storage.empty()); | |||
m_storage.only_reset_raw_storage( | |||
storage.comp_node(), storage.size(), storage.raw_storage(), | |||
storage.offset()); | |||
return static_cast<ChainReturnType&>(*this); | |||
} | |||
DEF(comp_node, &)(CompNode comp_node, bool allow_mem_node_change) { | |||
auto orig_cn = m_storage.comp_node_allow_invalid(); | |||
m_storage.comp_node(comp_node, allow_mem_node_change); | |||
@@ -226,6 +226,12 @@ public: | |||
MGE_WIN_DECLSPEC_FUC void reset(CompNode node, size_t size, RawStorage data); | |||
/*! | |||
* \brief reset the tensor storage to given memory area | |||
*/ | |||
MGE_WIN_DECLSPEC_FUC void only_reset_raw_storage( | |||
CompNode node, size_t size, RawStorage data, size_t offset); | |||
/*! | |||
* \brief make a TensorStorage that shares memory with another | |||
* TensorStorage some different storage type | |||
* | |||
@@ -270,6 +276,11 @@ public: | |||
return m_data; | |||
} | |||
std::shared_ptr<void*> get_ref_ptr() const { | |||
ptr(); | |||
return m_ref_ptr; | |||
} | |||
private: | |||
template <class T> | |||
friend class TensorStorage; | |||
@@ -289,16 +300,20 @@ private: | |||
RawStorage m_data; | |||
std::shared_ptr<void*> m_ref_ptr = std::make_shared<void*>((void*)nullptr); | |||
//! used internally for returning a predefined TensorStorage | |||
TensorStorage( | |||
bool allow_realloc, CompNode comp_node, size_t size, size_t capacity, | |||
size_t offset, const RawStorage& data) | |||
size_t offset, const RawStorage& data, | |||
std::shared_ptr<void*> ref_ptr = std::make_shared<void*>((void*)nullptr)) | |||
: m_allow_realloc(allow_realloc), | |||
m_comp_node(comp_node), | |||
m_size(size), | |||
m_capacity(capacity), | |||
m_offset(offset), | |||
m_data(data) {} | |||
m_data(data), | |||
m_ref_ptr(ref_ptr) {} | |||
void check_comp_node_valid() const { | |||
if (mgb_unlikely(!m_comp_node.valid())) | |||
@@ -423,6 +438,8 @@ public: | |||
MGE_WIN_DECLSPEC_FUC ChainReturnType& reset( | |||
TensorStorage storage, const TensorLayout& layout); | |||
MGE_WIN_DECLSPEC_FUC ChainReturnType& only_reset_raw_storage(TensorStorage storage); | |||
/* ================= getter and setters ================= */ | |||
/*! | |||
@@ -501,7 +518,8 @@ public: | |||
//! convert to megdnn::TensorND | |||
megdnn::TensorND as_megdnn() const { | |||
return {const_cast<void*>(static_cast<const void*>(raw_ptr())), m_layout}; | |||
megdnn::RefPtr ref_ptr(m_storage.get_ref_ptr(), m_storage.offset(), false); | |||
return {m_layout, ref_ptr}; | |||
} | |||
/* ================= misc ================= */ | |||
@@ -816,4 +816,79 @@ TYPED_TEST(TestCPUCompSeqRec, run_multi_thread_default) { | |||
} | |||
} // anonymous namespace | |||
#include "megbrain/opr/basic_arith_wrapper.h" | |||
#include "megbrain/opr/io.h" | |||
#include "megbrain/opr/tensor_manip.h" | |||
#include "megbrain/opr/utility.h" | |||
TEST(TestCPUCompSeqRec, run_dyn_ptr) { | |||
CompNode cn = CompNode::load("cpux"); | |||
HostTensorGenerator<> gen; | |||
auto host_x0 = gen({4, 1}, cn), host_y0 = gen({4, 1}, cn), | |||
host_z0 = gen({4, 1}, cn); | |||
auto host_x1 = gen({4, 1}, cn), host_y1 = gen({4, 1}, cn), | |||
host_z1 = gen({4, 1}, cn); | |||
auto dev_x0 = std::make_shared<DeviceTensorND>(cn); | |||
auto dev_y0 = std::make_shared<DeviceTensorND>(cn); | |||
auto dev_z0 = std::make_shared<DeviceTensorND>(cn); | |||
auto dev_x1 = std::make_shared<DeviceTensorND>(cn); | |||
auto dev_y1 = std::make_shared<DeviceTensorND>(cn); | |||
auto dev_z1 = std::make_shared<DeviceTensorND>(cn); | |||
(*dev_x0).comp_node(cn).copy_from(*host_x0).sync(); | |||
(*dev_y0).comp_node(cn).copy_from(*host_y0).sync(); | |||
(*dev_z0).comp_node(cn).copy_from(*host_z0).sync(); | |||
(*dev_x1).comp_node(cn).copy_from(*host_x1).sync(); | |||
(*dev_y1).comp_node(cn).copy_from(*host_y1).sync(); | |||
(*dev_z1).comp_node(cn).copy_from(*host_z1).sync(); | |||
auto check = [&]() { | |||
HostTensorND ret(CompNode::load("cpux"), host_x0->shape()); | |||
auto px = host_x0->ptr<float>(), py = host_y0->ptr<float>(), | |||
pz = host_z0->ptr<float>(), pw = ret.ptr<float>(); | |||
auto sz0 = host_x0->shape()[0], sz1 = host_x0->shape()[1]; | |||
for (size_t i = 0; i < sz0; ++i) { | |||
for (size_t j = 0; j < sz1; ++j) { | |||
pw[i * sz1 + j] = px[i * sz1 + j] * py[i * sz1 + j] + pz[i * sz1 + j]; | |||
} | |||
} | |||
return ret; | |||
}; | |||
auto graph = ComputingGraph::make(); | |||
// test record on first run | |||
graph->options().var_sanity_check_first_run = false; | |||
graph->options().graph_opt_level = 0; | |||
graph->options().comp_node_seq_record_level = 1; | |||
graph->options().fake_next_exec = true; | |||
auto x = opr::VolatileSharedDeviceTensor::make(*graph, dev_x0), | |||
y = opr::VolatileSharedDeviceTensor::make(*graph, dev_y0), | |||
z = opr::VolatileSharedDeviceTensor::make(*graph, dev_z0), | |||
w = opr::Elemwise::make({x, y, z}, opr::Elemwise::Mode::FUSE_MUL_ADD3); | |||
HostTensorND host_w; | |||
auto func = graph->compile({{w, [&host_w](DeviceTensorND& d) { | |||
host_w = mgb::HostTensorND::make_proxy(d); | |||
}}}); | |||
func->execute(); | |||
for (int i = 0; i < 4; ++i) { | |||
if (i == 2) { | |||
*host_x0 = *host_x1; | |||
*host_y0 = *host_y1; | |||
*host_z0 = *host_z1; | |||
dev_x0->only_reset_raw_storage(dev_x1->storage()); | |||
dev_y0->only_reset_raw_storage(dev_y1->storage()); | |||
dev_z0->only_reset_raw_storage(dev_z1->storage()); | |||
} | |||
func->execute(); | |||
auto expect = check(); | |||
MGB_ASSERT_TENSOR_EQ(expect, host_w) << "iter " << i; | |||
} | |||
} | |||
// vim: syntax=cpp.doxygen foldmethod=marker foldmarker=f{{{,f}}} |
@@ -13,6 +13,7 @@ | |||
#include "./network.h" | |||
#include "megbrain/comp_node_env.h" | |||
#include "megbrain/opr/basic_arith.h" | |||
#include "megbrain/opr/tensor_manip.h" | |||
#include "megbrain/test/helper.h" | |||
using namespace mgb; | |||
@@ -20,9 +21,11 @@ using namespace mgb; | |||
struct TestGraph { | |||
CompNode m_cn; | |||
HostTensorGenerator<> m_gen; | |||
HostTensorGenerator<dtype::Int32> m_gen_int; | |||
std::unique_ptr<Network> m_network; | |||
SymbolVar m_out_var; | |||
std::shared_ptr<HostTensorND> input_tensor; | |||
std::shared_ptr<HostTensorND> input_tensor2; | |||
TestGraph() { | |||
m_cn = CompNode::load("cpu0"); | |||
@@ -41,6 +44,78 @@ struct TestGraph { | |||
m_out_var = m_network->add_pooling(f, {2, 2}, {2, 2}); | |||
} | |||
void create_graph_with_subtensor_forward() { | |||
input_tensor = m_gen({2, 3, 32, 32}, m_cn); | |||
auto input = opr::Host2DeviceCopy::make(*m_network->graph, input_tensor, m_cn) | |||
.rename("input"); | |||
auto cv = [&](int v) { | |||
auto rst = input.make_scalar(v); | |||
return rst; | |||
}; | |||
using Ad = opr::Subtensor::AxisIndexer; | |||
auto sub = | |||
opr::Subtensor::make(input, {Ad::make_interval(0, cv(1), cv(2), None)}); | |||
auto f = m_network->add_conv( | |||
sub, 4, {3, 3}, dtype::Float32(), true, {2, 2}, {0, 0}); | |||
f = m_network->add_elemwise( | |||
{f}, dtype::Float32(), opr::Elemwise::Param::Mode::EXP); | |||
f = m_network->add_conv(f, 8, {3, 3}, dtype::Float32(), true, {1, 1}, {1, 1}); | |||
m_out_var = m_network->add_pooling(f, {2, 2}, {2, 2}); | |||
} | |||
void create_graph_with_subtensor_relayout() { | |||
input_tensor = m_gen({2, 3, 32, 40}, m_cn); | |||
auto input = opr::Host2DeviceCopy::make(*m_network->graph, input_tensor, m_cn) | |||
.rename("input"); | |||
auto cv = [&](int v) { | |||
auto rst = input.make_scalar(v); | |||
return rst; | |||
}; | |||
using Ad = opr::Subtensor::AxisIndexer; | |||
auto sub = opr::Subtensor::make( | |||
input, {Ad::make_interval(0, cv(1), cv(2), None), | |||
Ad::make_interval(3, cv(0), cv(32), None)}); | |||
auto f = m_network->add_conv( | |||
sub, 4, {3, 3}, dtype::Float32(), true, {2, 2}, {0, 0}); | |||
f = m_network->add_elemwise( | |||
{f}, dtype::Float32(), opr::Elemwise::Param::Mode::EXP); | |||
f = m_network->add_conv(f, 8, {3, 3}, dtype::Float32(), true, {1, 1}, {1, 1}); | |||
m_out_var = m_network->add_pooling(f, {2, 2}, {2, 2}); | |||
} | |||
void create_graph_with_setsubtensor() { | |||
input_tensor = m_gen({1, 3, 32, 32}, m_cn); | |||
input_tensor2 = m_gen({1, 1, 32, 32}, m_cn); | |||
auto input = opr::Host2DeviceCopy::make(*m_network->graph, input_tensor, m_cn) | |||
.rename("input"); | |||
auto input_sub = | |||
opr::Host2DeviceCopy::make(*m_network->graph, input_tensor2, m_cn) | |||
.rename("input2"); | |||
auto cv = [&](int v) { | |||
auto rst = input.make_scalar(v); | |||
return rst; | |||
}; | |||
using Ad = opr::Subtensor::AxisIndexer; | |||
input = opr::SetSubtensor::make( | |||
input, input_sub, {Ad::make_interval(1, cv(1), cv(2), None)}); | |||
auto f = m_network->add_conv( | |||
input, 4, {3, 3}, dtype::Float32(), true, {2, 2}, {0, 0}); | |||
f = m_network->add_elemwise( | |||
{f}, dtype::Float32(), opr::Elemwise::Param::Mode::EXP); | |||
f = m_network->add_conv(f, 8, {3, 3}, dtype::Float32(), true, {1, 1}, {1, 1}); | |||
m_out_var = m_network->add_pooling(f, {2, 2}, {2, 2}); | |||
} | |||
std::unique_ptr<cg::AsyncExecutable> compile_without_copy() { | |||
return m_network->graph->compile({{m_out_var, nullptr}}); | |||
} | |||
@@ -51,8 +126,11 @@ struct TestGraph { | |||
} | |||
}; | |||
TEST(TestNoCopy, BasicInputNoCopy) { | |||
namespace { | |||
void test_basic_input_no_copy(bool record) { | |||
auto test_graph = TestGraph(); | |||
auto compute_graph = test_graph.m_network->graph; | |||
compute_graph->options().comp_node_seq_record_level = record; | |||
test_graph.create_graph(); | |||
HostTensorND out, out_pre; | |||
auto func = test_graph.compile_with_copy(out); | |||
@@ -68,7 +146,11 @@ TEST(TestNoCopy, BasicInputNoCopy) { | |||
for (size_t d = 0; d < length; d++) { | |||
ptr[d] = i; | |||
} | |||
input_tensor->reset(storage, layout); | |||
if (record) { | |||
input_tensor->only_reset_raw_storage(storage); | |||
} else { | |||
input_tensor->reset(storage, layout); | |||
} | |||
} | |||
func->execute(); | |||
func->wait(); | |||
@@ -78,6 +160,11 @@ TEST(TestNoCopy, BasicInputNoCopy) { | |||
out_pre.copy_from(out).sync(); | |||
} | |||
} | |||
} // namespace | |||
TEST(TestNoCopy, InputNoCopyPtrEQ) { | |||
test_basic_input_no_copy(0); | |||
} | |||
TEST(TestNoCopy, IONoCopyPtrEQ) { | |||
auto test_graph = TestGraph(); | |||
@@ -158,8 +245,112 @@ TEST(TestNoCopy, IONoCopyCorrect) { | |||
} | |||
} | |||
TEST(TestNoCopy, InputNoCopyRecord) {} | |||
TEST(TestNoCopy, InputNoCopyRecord) { | |||
test_basic_input_no_copy(1); | |||
} | |||
TEST(TestNoCopy, IONoCopyRecord) { | |||
auto test_graph = TestGraph(); | |||
auto compute_graph = test_graph.m_network->graph; | |||
compute_graph->options().force_output_use_user_specified_memory = true; | |||
compute_graph->options().comp_node_seq_record_level = 1; | |||
test_graph.create_graph(); | |||
HostTensorND truth; | |||
auto func = test_graph.compile_without_copy(); | |||
auto&& outvar = func->get_output_vars()[0]; | |||
DeviceTensorND tmp(test_graph.m_cn, {1, 8, 7, 7}); | |||
outvar->init_mem_plan(&tmp); | |||
size_t times = 10; | |||
for (size_t i = 0; i < times; i++) { | |||
auto input_tensor = test_graph.input_tensor; | |||
auto layout = input_tensor->layout(); | |||
size_t length = layout.total_nr_elems(); | |||
auto storage = TensorStorage<HostTensorStorageTrait>(test_graph.m_cn); | |||
storage.ensure_size(length * sizeof(float)); | |||
float* ptr = storage.ptr()->as<float>(); | |||
for (size_t d = 0; d < length; d++) { | |||
ptr[d] = i / 5 + 3; | |||
} | |||
input_tensor->only_reset_raw_storage(storage); | |||
DeviceTensorND dv(test_graph.m_cn, {1, 8, 7, 7}); | |||
dv.raw_ptr(); | |||
auto& dev_tensor = outvar->mutable_dev_tensor(); | |||
dev_tensor.only_reset_raw_storage(dv.storage()); | |||
func->execute(); | |||
func->wait(); | |||
if (i % 5 == 0) { | |||
truth.copy_from(dv).sync(); | |||
continue; | |||
} | |||
HostTensorND to_check; | |||
to_check.copy_from(dv).sync(); | |||
MGB_ASSERT_TENSOR_EQ(to_check, truth); | |||
} | |||
} | |||
namespace { | |||
void test_subtensor_record(int level) { | |||
auto test_graph = TestGraph(); | |||
auto compute_graph = test_graph.m_network->graph; | |||
compute_graph->options().force_output_use_user_specified_memory = true; | |||
compute_graph->options().comp_node_seq_record_level = 1; | |||
if (level == 2) { | |||
test_graph.create_graph_with_setsubtensor(); | |||
} else if (level == 1) { | |||
test_graph.create_graph_with_subtensor_forward(); | |||
} else { | |||
test_graph.create_graph_with_subtensor_relayout(); | |||
} | |||
HostTensorND truth; | |||
auto func = test_graph.compile_without_copy(); | |||
auto&& outvar = func->get_output_vars()[0]; | |||
DeviceTensorND tmp(test_graph.m_cn, {1, 8, 7, 7}); | |||
outvar->init_mem_plan(&tmp); | |||
size_t times = 10; | |||
for (size_t i = 0; i < times; i++) { | |||
auto input_tensor = test_graph.input_tensor; | |||
auto layout = input_tensor->layout(); | |||
size_t length = layout.total_nr_elems(); | |||
auto storage = TensorStorage<HostTensorStorageTrait>(test_graph.m_cn); | |||
storage.ensure_size(length * sizeof(float)); | |||
float* ptr = storage.ptr()->as<float>(); | |||
for (size_t d = 0; d < length; d++) { | |||
ptr[d] = i / 5 + 3; | |||
} | |||
input_tensor->only_reset_raw_storage(storage); | |||
DeviceTensorND dv(test_graph.m_cn, {1, 8, 7, 7}); | |||
dv.raw_ptr(); | |||
auto& dev_tensor = outvar->mutable_dev_tensor(); | |||
dev_tensor.only_reset_raw_storage(dv.storage()); | |||
func->execute(); | |||
func->wait(); | |||
if (i % 5 == 0) { | |||
truth.copy_from(dv).sync(); | |||
continue; | |||
} | |||
HostTensorND to_check; | |||
to_check.copy_from(dv).sync(); | |||
MGB_ASSERT_TENSOR_EQ(to_check, truth); | |||
} | |||
} | |||
} // namespace | |||
TEST(TestNoCopy, IONoCopyRecordSubTensor) { | |||
test_subtensor_record(0); | |||
} | |||
TEST(TestNoCopy, IONoCopyRecordSubTensorRelayout) { | |||
test_subtensor_record(1); | |||
} | |||
TEST(TestNoCopy, OutputNoCopyRecord) {} | |||
//! TODO: the test should fix compnode memory copy, which now not record reference | |||
//! ptr, when support it, the test will pass | |||
/*TEST(TestNoCopy, IONoCopyRecordSetSubTensor) { | |||
test_subtensor_record(2); | |||
}*/ | |||
// vim: syntax=cpp.doxygen foldmethod=marker foldmarker=f{{{,f}}} |
@@ -133,7 +133,7 @@ void setup_and_launch(const JITExecutor* fusion_opr, CUfunction func, int block_ | |||
host_init_pvisitor<out_dim>(pvisitors[i], args.inputs[i].layout); | |||
} | |||
datum[nr_inps] = reinterpret_cast<CUdeviceptr>( | |||
args.outputs[0].from->dev_tensor().as_megdnn().raw_ptr); | |||
args.outputs[0].from->dev_tensor().as_megdnn().raw_ptr()); | |||
size_t num_elements = args.outputs[0].layout.total_nr_elems(); | |||
mgb_assert( | |||
num_elements <= UINT32_MAX, | |||
@@ -152,11 +152,11 @@ void setup_and_launch(const JITExecutor* fusion_opr, CUfunction func, int block_ | |||
exec_args[0] = datum.data(); | |||
exec_args[2] = pvisitors.data(); | |||
} else { | |||
datum_dev = args.outputs[1].from->dev_tensor().as_megdnn().raw_ptr; | |||
datum_dev = args.outputs[1].from->dev_tensor().as_megdnn().raw_ptr(); | |||
MGB_CUDA_CHECK(cudaMemcpyAsync( | |||
datum_dev, datum.data(), (nr_inps + 1) * sizeof(CUdeviceptr), | |||
cudaMemcpyHostToDevice, env.cuda_env().stream)); | |||
p_visitors_dev = args.outputs[2].from->dev_tensor().as_megdnn().raw_ptr; | |||
p_visitors_dev = args.outputs[2].from->dev_tensor().as_megdnn().raw_ptr(); | |||
MGB_CUDA_CHECK(cudaMemcpyAsync( | |||
p_visitors_dev, pvisitors.data(), | |||
nr_inps * sizeof(ParamElemVisitor<out_dim>), cudaMemcpyHostToDevice, | |||
@@ -1269,7 +1269,9 @@ void Reduce::KernScheduler::update_ptr( | |||
mgb_assert( | |||
dest.shape().total_nr_elems() == | |||
m_kern_param.back().output.layout.total_nr_elems()); | |||
m_kern_param[0].input.raw_ptr = const_cast<dt_byte*>(input.raw_ptr()); | |||
auto in_tensor = input.as_megdnn(); | |||
in_tensor.layout = m_kern_param[0].input.layout; | |||
m_kern_param[0].input = in_tensor; | |||
dt_byte *workspace_begin = workspace_size() | |||
? const_cast<dt_byte*>(workspace.raw_ptr()) | |||
@@ -1280,12 +1282,14 @@ void Reduce::KernScheduler::update_ptr( | |||
*kern_workspace = workspace_begin + m_workspace_spec[2].offset; | |||
for (size_t i = 0; i < m_kern_param.size() - 1; ++i) { | |||
auto optr = tmp_reduce_ptr[i % 2]; | |||
m_kern_param[i].output.raw_ptr = optr; | |||
m_kern_param[i + 1].input.raw_ptr = optr; | |||
m_kern_param[i].output.reset_ptr(optr); | |||
m_kern_param[i + 1].input.reset_ptr(optr); | |||
} | |||
for (auto&& i : m_kern_param) | |||
i.workspace.raw_ptr = kern_workspace; | |||
m_kern_param.back().output.raw_ptr = const_cast<dt_byte*>(dest.raw_ptr()); | |||
auto out_tensor = dest.as_megdnn(); | |||
out_tensor.layout = m_kern_param.back().output.layout; | |||
m_kern_param.back().output = out_tensor; | |||
} | |||
void Reduce::KernScheduler::execute( | |||
@@ -1343,8 +1347,8 @@ void Reduce::KernScheduler::execute( | |||
} | |||
mgb_assert( | |||
input.layout().is_contiguous() && | |||
input.raw_ptr() == m_kern_param[0].input.raw_ptr && | |||
dest.raw_ptr() == m_kern_param.back().output.raw_ptr); | |||
input.raw_ptr() == m_kern_param[0].input.raw_ptr() && | |||
dest.raw_ptr() == m_kern_param.back().output.raw_ptr()); | |||
for (auto&& i : m_kern_param) { | |||
opr->param() = i.KernParam::kparam; | |||
opr->exec(i.input, i.output, i.workspace); | |||
@@ -1157,7 +1157,7 @@ void CondExecMerge::scn_do_execute() { | |||
if (forwarded[oidx]) { | |||
ovar->shape_alloc(ovar->shape()); | |||
auto&& own_dest = ovar->dev_tensor().as_megdnn(); | |||
mgb_assert(own_dest.raw_ptr != dest.raw_ptr); | |||
mgb_assert(own_dest.raw_ptr() != dest.raw_ptr()); | |||
dnn_opr->exec({dest, src}, own_dest); | |||
forwarded[oidx] = false; | |||
} else { | |||
@@ -241,9 +241,9 @@ void NvOf::scn_do_execute() { | |||
} | |||
nv_flow_extractor->extract_flow( | |||
static_cast<unsigned char*>(input(0)->dev_tensor().as_megdnn().raw_ptr), | |||
static_cast<unsigned char*>(input(0)->dev_tensor().as_megdnn().raw_ptr()), | |||
vshape, | |||
reinterpret_cast<int16_t*>(output(0)->dev_tensor().as_megdnn().raw_ptr)); | |||
reinterpret_cast<int16_t*>(output(0)->dev_tensor().as_megdnn().raw_ptr())); | |||
} | |||
void NvOf::init_output_static_infer_desc() { | |||
@@ -1425,7 +1425,7 @@ void ParamPackConcat::scn_do_execute() { | |||
m_inp_ptr.resize(inputs.size() - 1); | |||
auto ptr = m_inp_ptr.data(); | |||
for (size_t i = 0; i < inputs.size() - 1; i++) { | |||
ptr[i] = inputs[i]->dev_tensor().as_megdnn().raw_ptr; | |||
ptr[i] = inputs[i]->dev_tensor().as_megdnn().raw_ptr(); | |||
} | |||
auto offsets = inputs.back()->dev_tensor().as_megdnn(); | |||
megdnn::TensorND srcs( | |||
@@ -2572,8 +2572,8 @@ TEST_F(TestWeightPreprocess, PreprocessCalledOnlyOnce) { | |||
ASSERT_EQ(pf->tensors.size(), 2); | |||
ASSERT_TRUE(pf->tensors[0].layout.eq_shape({1, 2, 3, 4})); | |||
ASSERT_TRUE(pf->tensors[1].layout.eq_shape({5, 6, 7, 8})); | |||
ASSERT_NE(pf->tensors[0].raw_ptr, nullptr); | |||
ASSERT_NE(pf->tensors[1].raw_ptr, nullptr); | |||
ASSERT_NE(pf->tensors[0].raw_ptr(), nullptr); | |||
ASSERT_NE(pf->tensors[1].raw_ptr(), nullptr); | |||
pf->tensors[0].ptr<float>()[0] = 114.514f; | |||
pf->tensors[1].ptr<float>()[0] = 1926.0817f; | |||
})); | |||
@@ -178,7 +178,7 @@ VarSanityCheck::ChecksumResult VarSanityCheck::calc_checksum(VarNode* var) { | |||
auto span = dt.layout().span(); | |||
megdnn::TensorND tensor; | |||
tensor.raw_ptr = dt.raw_ptr() + span.low_byte; | |||
tensor.reset_ptr(dt.raw_ptr() + span.low_byte); | |||
tensor.layout.init_contiguous_stride({span.dist_byte()}); | |||
tensor.layout.dtype = dtype::Byte(); | |||