GitOrigin-RevId: b12f1c4a66
tags/v1.7.0
| @@ -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); | |||
| @@ -225,6 +225,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(); | |||