From 87668d6ea2793910b5acaaf0ff03e5c61d8682da Mon Sep 17 00:00:00 2001 From: z00505269 Date: Fri, 31 Jul 2020 15:19:43 +0800 Subject: [PATCH] remove predict --- mindspore/ccsrc/CMakeLists.txt | 15 +- .../ccsrc/backend/session/ascend_session.cc | 7 - .../ccsrc/backend/session/cpu_session.cc | 3 - .../ccsrc/backend/session/gpu_session.cc | 5 - mindspore/ccsrc/pipeline/jit/init.cc | 4 - mindspore/ccsrc/predict/CMakeLists.txt | 14 - .../converter/attr_utils/convert_util.cc | 229 --- .../converter/attr_utils/convert_util.h | 60 - .../converter/attr_utils/op_attr_type.h | 65 - .../predict/converter/executor_tensor.cc | 49 - .../ccsrc/predict/converter/executor_tensor.h | 70 - .../ccsrc/predict/converter/kernel2ms.cc | 561 ------ mindspore/ccsrc/predict/converter/kernel2ms.h | 118 -- .../converter/lite_model/op_attr_packer.cc | 110 - .../converter/lite_model/op_attr_packer.h | 58 - .../operations/activation_packer.cc | 59 - .../lite_model/operations/add_packer.cc | 35 - .../lite_model/operations/addfold_packer.cc | 34 - .../lite_model/operations/argmax_packer.cc | 34 - .../operations/batchnormfold_packer.cc | 34 - .../lite_model/operations/biasadd_packer.cc | 37 - .../lite_model/operations/cast_packer.cc | 37 - .../lite_model/operations/conv2d_packer.cc | 63 - .../operations/fakequantwithminmax_packer.cc | 34 - .../fakequantwithminmaxperchannel_packer.cc | 34 - .../operations/fusedbatchnorm_packer.cc | 37 - .../lite_model/operations/matmul_packer.cc | 39 - .../lite_model/operations/mean_packer.cc | 37 - .../lite_model/operations/mul_packer.cc | 34 - .../lite_model/operations/mulflod_packer.cc | 35 - .../lite_model/operations/pooling_packer.cc | 61 - .../lite_model/operations/reshape_packer.cc | 36 - .../lite_model/operations/scale_packer.cc | 36 - .../lite_model/operations/softmax_packer.cc | 36 - .../lite_model/operations/squeeze_packer.cc | 38 - .../ccsrc/predict/generator/ir/ir_model.cc | 31 - .../ccsrc/predict/generator/ir/ir_model.h | 37 - .../predict/generator/ir/ir_task_info.cc | 244 --- .../ccsrc/predict/generator/ir/ir_task_info.h | 295 --- .../predict/generator/utils/ir_model_util.cc | 43 - .../predict/generator/utils/ir_model_util.h | 92 - mindspore/ccsrc/predict/predict.cc | 69 - mindspore/ccsrc/predict/predict.h | 32 - mindspore/ccsrc/predict/proto/DModel_ir.proto | 42 - mindspore/ccsrc/predict/proto/Graph_ir.proto | 125 -- .../predict/proto/ge_runtime_taskinfo.proto | 155 -- mindspore/ccsrc/predict/readme.txt | 17 - .../ccsrc/predict/schema/inner/readme.txt | 1 - mindspore/ccsrc/predict/schema/ms.fbs | 212 -- mindspore/ccsrc/predict/schema/op.fbs | 699 ------- .../device/ascend/ascend_stream_assign.cc | 8 - .../ccsrc/runtime/device/kernel_runtime.h | 1 - mindspore/ccsrc/utils/context/ms_context.cc | 2 - mindspore/ccsrc/utils/context/ms_context.h | 8 - mindspore/context.py | 21 +- mindspore/train/serialization.py | 9 +- predict/.gitignore | 14 - predict/CMakeLists.txt | 79 - predict/benchmark/CMakeLists.txt | 38 - predict/benchmark/README.md | 0 predict/benchmark/benchmark.cc | 451 ----- predict/benchmark/benchmark.h | 142 -- predict/benchmark/main.cc | 24 - predict/common/CMakeLists.txt | 17 - predict/common/common.h | 57 - predict/common/file_utils.cc | 79 - predict/common/file_utils.h | 39 - predict/common/flag_parser.cc | 179 -- predict/common/flag_parser.h | 291 --- predict/common/func_utils.cc | 77 - predict/common/func_utils.h | 35 - predict/common/graph_util.cc | 167 -- predict/common/graph_util.h | 71 - predict/common/module_registry.cc | 26 - predict/common/module_registry.h | 97 - predict/common/mslog.cc | 47 - predict/common/mslog.h | 230 --- predict/common/op_utils.h | 44 - predict/common/option.h | 119 -- predict/common/storage.cc | 50 - predict/common/storage.h | 36 - predict/common/utils.cc | 228 --- predict/common/utils.h | 154 -- predict/include/context.h | 56 - predict/include/errorcode.h | 52 - predict/include/session.h | 139 -- predict/include/tensor.h | 259 --- predict/module/CMakeLists.txt | 1 - predict/module/tvm_kernel/.gitignore | 27 - predict/module/tvm_kernel/.gitmodules | 4 - predict/module/tvm_kernel/CMakeLists.txt | 25 - predict/module/tvm_kernel/lite/CMakeLists.txt | 140 -- .../tvm_kernel/lite/include/lite/api/km_api.h | 94 - .../module/tvm_kernel/lite/python/__init__.py | 17 - .../lite/python/arm_cpu/__init__.py | 17 - .../tvm_kernel/lite/python/arm_cpu/conv2d.py | 470 ----- .../tvm_kernel/lite/python/arm_cpu/deconv.py | 477 ----- .../lite/python/arm_cpu/depthwise_conv2d.py | 289 --- .../tvm_kernel/lite/python/arm_cpu/matmul.py | 472 ----- .../tvm_kernel/lite/python/at_ops/__init__.py | 17 - .../lite/python/at_ops/at_gen_strip.py | 516 ----- .../tvm_kernel/lite/python/at_ops/at_lib.py | 1193 ----------- .../lite/python/at_ops/config_tool.py | 211 -- .../lite/python/at_rt/at_runtime_reset.py | 62 - .../tvm_kernel/lite/src/api/kernel_manager.cc | 1772 ----------------- .../tvm_kernel/lite/src/api/tvm_op_module.cc | 94 - .../tvm_kernel/lite/src/api/tvm_op_module.h | 34 - .../src/codegen/llvm/lite_rtfunc_reset.cc | 83 - predict/schema/inner/README | 1 - predict/schema/ms.fbs | 153 -- predict/schema/op.fbs | 351 ---- predict/src/CMakeLists.txt | 69 - predict/src/context.cc | 33 - predict/src/graph.cc | 378 ---- predict/src/graph.h | 101 - predict/src/graph_execution.cc | 293 --- predict/src/graph_execution.h | 70 - predict/src/node.cc | 148 -- predict/src/node.h | 68 - predict/src/op.cc | 25 - predict/src/op.h | 58 - predict/src/op_common.h | 83 - predict/src/op_factory.cc | 48 - predict/src/op_factory.h | 39 - predict/src/op_registry.cc | 49 - predict/src/op_registry.h | 71 - .../src/operator/cpu/common/op_func_comm.cc | 422 ---- .../src/operator/cpu/include/op_func_comm.h | 62 - predict/src/runtime/allocator.cc | 135 -- predict/src/runtime/allocator.h | 81 - predict/src/runtime/runtime_api.cc | 79 - predict/src/runtime/runtime_api.h | 39 - predict/src/runtime/thread_pool.cc | 447 ----- predict/src/runtime/thread_pool.h | 129 -- predict/src/runtime/workspace_pool.cc | 113 -- predict/src/runtime/workspace_pool.h | 44 - predict/src/session.cc | 154 -- predict/src/tensor.cc | 517 ----- predict/test/CMakeLists.txt | 49 - predict/test/benchmark/benchmark_tests.cc | 69 - predict/test/data/lenet/lenet.bin | Bin 3136 -> 0 bytes predict/test/data/lenet/lenet.ms | Bin 1725872 -> 0 bytes predict/test/data/lenet/lenet.out | 2 - predict/test/data/ms/mindspore.bin | 5 - predict/test/data/ms/mindspore.ms | Bin 249376 -> 0 bytes predict/test/data/ms/mindspore.out | 2 - predict/test/main.cc | 50 - predict/test/run_tests.sh | 28 - predict/test/src/graph_tests.cc | 148 -- predict/test/test_context.cc | 25 - predict/test/test_context.h | 36 - .../python/predict/test_predict_save_model.py | 93 - .../0001-RetBugFix-CustomRuntime_v06.patch | 203 -- 153 files changed, 6 insertions(+), 18577 deletions(-) delete mode 100644 mindspore/ccsrc/predict/CMakeLists.txt delete mode 100644 mindspore/ccsrc/predict/converter/attr_utils/convert_util.cc delete mode 100644 mindspore/ccsrc/predict/converter/attr_utils/convert_util.h delete mode 100644 mindspore/ccsrc/predict/converter/attr_utils/op_attr_type.h delete mode 100644 mindspore/ccsrc/predict/converter/executor_tensor.cc delete mode 100644 mindspore/ccsrc/predict/converter/executor_tensor.h delete mode 100644 mindspore/ccsrc/predict/converter/kernel2ms.cc delete mode 100644 mindspore/ccsrc/predict/converter/kernel2ms.h delete mode 100644 mindspore/ccsrc/predict/converter/lite_model/op_attr_packer.cc delete mode 100644 mindspore/ccsrc/predict/converter/lite_model/op_attr_packer.h delete mode 100644 mindspore/ccsrc/predict/converter/lite_model/operations/activation_packer.cc delete mode 100644 mindspore/ccsrc/predict/converter/lite_model/operations/add_packer.cc delete mode 100644 mindspore/ccsrc/predict/converter/lite_model/operations/addfold_packer.cc delete mode 100644 mindspore/ccsrc/predict/converter/lite_model/operations/argmax_packer.cc delete mode 100644 mindspore/ccsrc/predict/converter/lite_model/operations/batchnormfold_packer.cc delete mode 100644 mindspore/ccsrc/predict/converter/lite_model/operations/biasadd_packer.cc delete mode 100644 mindspore/ccsrc/predict/converter/lite_model/operations/cast_packer.cc delete mode 100644 mindspore/ccsrc/predict/converter/lite_model/operations/conv2d_packer.cc delete mode 100644 mindspore/ccsrc/predict/converter/lite_model/operations/fakequantwithminmax_packer.cc delete mode 100644 mindspore/ccsrc/predict/converter/lite_model/operations/fakequantwithminmaxperchannel_packer.cc delete mode 100644 mindspore/ccsrc/predict/converter/lite_model/operations/fusedbatchnorm_packer.cc delete mode 100644 mindspore/ccsrc/predict/converter/lite_model/operations/matmul_packer.cc delete mode 100644 mindspore/ccsrc/predict/converter/lite_model/operations/mean_packer.cc delete mode 100644 mindspore/ccsrc/predict/converter/lite_model/operations/mul_packer.cc delete mode 100644 mindspore/ccsrc/predict/converter/lite_model/operations/mulflod_packer.cc delete mode 100644 mindspore/ccsrc/predict/converter/lite_model/operations/pooling_packer.cc delete mode 100644 mindspore/ccsrc/predict/converter/lite_model/operations/reshape_packer.cc delete mode 100644 mindspore/ccsrc/predict/converter/lite_model/operations/scale_packer.cc delete mode 100644 mindspore/ccsrc/predict/converter/lite_model/operations/softmax_packer.cc delete mode 100644 mindspore/ccsrc/predict/converter/lite_model/operations/squeeze_packer.cc delete mode 100644 mindspore/ccsrc/predict/generator/ir/ir_model.cc delete mode 100644 mindspore/ccsrc/predict/generator/ir/ir_model.h delete mode 100644 mindspore/ccsrc/predict/generator/ir/ir_task_info.cc delete mode 100644 mindspore/ccsrc/predict/generator/ir/ir_task_info.h delete mode 100644 mindspore/ccsrc/predict/generator/utils/ir_model_util.cc delete mode 100644 mindspore/ccsrc/predict/generator/utils/ir_model_util.h delete mode 100644 mindspore/ccsrc/predict/predict.cc delete mode 100644 mindspore/ccsrc/predict/predict.h delete mode 100644 mindspore/ccsrc/predict/proto/DModel_ir.proto delete mode 100644 mindspore/ccsrc/predict/proto/Graph_ir.proto delete mode 100644 mindspore/ccsrc/predict/proto/ge_runtime_taskinfo.proto delete mode 100644 mindspore/ccsrc/predict/readme.txt delete mode 100644 mindspore/ccsrc/predict/schema/inner/readme.txt delete mode 100644 mindspore/ccsrc/predict/schema/ms.fbs delete mode 100644 mindspore/ccsrc/predict/schema/op.fbs delete mode 100644 predict/.gitignore delete mode 100755 predict/CMakeLists.txt delete mode 100755 predict/benchmark/CMakeLists.txt delete mode 100644 predict/benchmark/README.md delete mode 100644 predict/benchmark/benchmark.cc delete mode 100644 predict/benchmark/benchmark.h delete mode 100644 predict/benchmark/main.cc delete mode 100755 predict/common/CMakeLists.txt delete mode 100644 predict/common/common.h delete mode 100644 predict/common/file_utils.cc delete mode 100644 predict/common/file_utils.h delete mode 100644 predict/common/flag_parser.cc delete mode 100644 predict/common/flag_parser.h delete mode 100644 predict/common/func_utils.cc delete mode 100644 predict/common/func_utils.h delete mode 100644 predict/common/graph_util.cc delete mode 100644 predict/common/graph_util.h delete mode 100644 predict/common/module_registry.cc delete mode 100644 predict/common/module_registry.h delete mode 100644 predict/common/mslog.cc delete mode 100644 predict/common/mslog.h delete mode 100644 predict/common/op_utils.h delete mode 100644 predict/common/option.h delete mode 100644 predict/common/storage.cc delete mode 100644 predict/common/storage.h delete mode 100644 predict/common/utils.cc delete mode 100644 predict/common/utils.h delete mode 100644 predict/include/context.h delete mode 100755 predict/include/errorcode.h delete mode 100644 predict/include/session.h delete mode 100644 predict/include/tensor.h delete mode 100644 predict/module/CMakeLists.txt delete mode 100644 predict/module/tvm_kernel/.gitignore delete mode 100644 predict/module/tvm_kernel/.gitmodules delete mode 100755 predict/module/tvm_kernel/CMakeLists.txt delete mode 100755 predict/module/tvm_kernel/lite/CMakeLists.txt delete mode 100644 predict/module/tvm_kernel/lite/include/lite/api/km_api.h delete mode 100644 predict/module/tvm_kernel/lite/python/__init__.py delete mode 100644 predict/module/tvm_kernel/lite/python/arm_cpu/__init__.py delete mode 100644 predict/module/tvm_kernel/lite/python/arm_cpu/conv2d.py delete mode 100644 predict/module/tvm_kernel/lite/python/arm_cpu/deconv.py delete mode 100644 predict/module/tvm_kernel/lite/python/arm_cpu/depthwise_conv2d.py delete mode 100644 predict/module/tvm_kernel/lite/python/arm_cpu/matmul.py delete mode 100644 predict/module/tvm_kernel/lite/python/at_ops/__init__.py delete mode 100644 predict/module/tvm_kernel/lite/python/at_ops/at_gen_strip.py delete mode 100644 predict/module/tvm_kernel/lite/python/at_ops/at_lib.py delete mode 100644 predict/module/tvm_kernel/lite/python/at_ops/config_tool.py delete mode 100644 predict/module/tvm_kernel/lite/python/at_rt/at_runtime_reset.py delete mode 100644 predict/module/tvm_kernel/lite/src/api/kernel_manager.cc delete mode 100644 predict/module/tvm_kernel/lite/src/api/tvm_op_module.cc delete mode 100644 predict/module/tvm_kernel/lite/src/api/tvm_op_module.h delete mode 100644 predict/module/tvm_kernel/lite/src/codegen/llvm/lite_rtfunc_reset.cc delete mode 100644 predict/schema/inner/README delete mode 100644 predict/schema/ms.fbs delete mode 100755 predict/schema/op.fbs delete mode 100644 predict/src/CMakeLists.txt delete mode 100644 predict/src/context.cc delete mode 100644 predict/src/graph.cc delete mode 100755 predict/src/graph.h delete mode 100644 predict/src/graph_execution.cc delete mode 100644 predict/src/graph_execution.h delete mode 100644 predict/src/node.cc delete mode 100644 predict/src/node.h delete mode 100644 predict/src/op.cc delete mode 100644 predict/src/op.h delete mode 100644 predict/src/op_common.h delete mode 100644 predict/src/op_factory.cc delete mode 100644 predict/src/op_factory.h delete mode 100644 predict/src/op_registry.cc delete mode 100644 predict/src/op_registry.h delete mode 100755 predict/src/operator/cpu/common/op_func_comm.cc delete mode 100644 predict/src/operator/cpu/include/op_func_comm.h delete mode 100644 predict/src/runtime/allocator.cc delete mode 100644 predict/src/runtime/allocator.h delete mode 100644 predict/src/runtime/runtime_api.cc delete mode 100644 predict/src/runtime/runtime_api.h delete mode 100644 predict/src/runtime/thread_pool.cc delete mode 100644 predict/src/runtime/thread_pool.h delete mode 100644 predict/src/runtime/workspace_pool.cc delete mode 100644 predict/src/runtime/workspace_pool.h delete mode 100644 predict/src/session.cc delete mode 100644 predict/src/tensor.cc delete mode 100755 predict/test/CMakeLists.txt delete mode 100644 predict/test/benchmark/benchmark_tests.cc delete mode 100755 predict/test/data/lenet/lenet.bin delete mode 100755 predict/test/data/lenet/lenet.ms delete mode 100644 predict/test/data/lenet/lenet.out delete mode 100755 predict/test/data/ms/mindspore.bin delete mode 100755 predict/test/data/ms/mindspore.ms delete mode 100644 predict/test/data/ms/mindspore.out delete mode 100644 predict/test/main.cc delete mode 100755 predict/test/run_tests.sh delete mode 100644 predict/test/src/graph_tests.cc delete mode 100644 predict/test/test_context.cc delete mode 100644 predict/test/test_context.h delete mode 100644 tests/ut/python/predict/test_predict_save_model.py delete mode 100644 third_party/patch/predict/0001-RetBugFix-CustomRuntime_v06.patch diff --git a/mindspore/ccsrc/CMakeLists.txt b/mindspore/ccsrc/CMakeLists.txt index d2d19ec0af..1339b4b9ea 100644 --- a/mindspore/ccsrc/CMakeLists.txt +++ b/mindspore/ccsrc/CMakeLists.txt @@ -60,11 +60,6 @@ if(ENABLE_GPU) add_compile_definitions(ENABLE_GPU) endif () -## make flatuffer files -include_directories("${CMAKE_BINARY_DIR}/predict/schema/inner") -file(GLOB_RECURSE FLATBUFFER_IN RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "predict/schema/*.fbs") -set(FLATBUFFER_OU "${CMAKE_BINARY_DIR}/predict/schema/inner") -ms_build_flatbuffers("${FLATBUFFER_IN}" "${FLATBUFFER_IN}" flat_input "${FLATBUFFER_OU}") ## make protobuf files file(COPY "${ms_onnx_INC}/onnx/onnx.proto" DESTINATION ${CMAKE_BINARY_DIR}/proto) @@ -104,13 +99,9 @@ endif () if (ENABLE_D) include_directories("${CMAKE_BINARY_DIR}/backend/kernel_compiler/aicpu") - include_directories("${CMAKE_BINARY_DIR}/predict/generator/ir") file(GLOB_RECURSE PROTO_IN RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "backend/kernel_compiler/aicpu/proto/*.proto") ms_protobuf_generate(PROTOSRCS PROTOHDRS ${PROTO_IN}) - file(GLOB_RECURSE PROTO_INNER RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "predict/proto/*.proto") - ms_protobuf_generate(PREDICT_PROTOSRCS PREDICT_PROTOHDRS ${PROTO_INNER}) - file(GLOB_RECURSE PROTO_DUMP RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "runtime/device/ascend/dump/proto/*.proto") ms_protobuf_generate(DUMP_PROTOSRCS PROTOHDRS ${PROTO_DUMP}) @@ -139,7 +130,7 @@ set(SUB_COMP frontend/operator pipeline/jit pipeline/pynative - common debug gvar predict pybind_api utils vm + common debug gvar pybind_api utils vm ) foreach (_comp ${SUB_COMP}) @@ -147,7 +138,7 @@ foreach (_comp ${SUB_COMP}) string(REPLACE "/" "_" sub ${_comp}) if (TARGET _mindspore_${sub}_obj) list(APPEND SUB_OBJECTS_SRC $) - add_dependencies(_mindspore_${sub}_obj proto_input flat_input) + add_dependencies(_mindspore_${sub}_obj proto_input ) endif () endforeach () add_subdirectory(${CMAKE_SOURCE_DIR}/mindspore/core/base base) @@ -158,7 +149,7 @@ add_subdirectory(${CMAKE_SOURCE_DIR}/mindspore/core/utils util) list(APPEND SUB_OBJECTS_SRC $) add_subdirectory(${CMAKE_SOURCE_DIR}/mindspore/core/ir ir) list(APPEND SUB_OBJECTS_SRC $) -add_dependencies(_mindspore_core_utils_obj _mindspore_base_obj _mindspore_ir_obj _mindspore_abstract_obj proto_input flat_input) +add_dependencies(_mindspore_core_utils_obj _mindspore_base_obj _mindspore_ir_obj _mindspore_abstract_obj proto_input ) set_property(SOURCE ${SUB_OBJECTS_SRC} PROPERTY COMPILE_DEFINITIONS SUBMODULE_ID=mindspore::SubModuleId::SM_ME) add_library(mindspore STATIC ${SUB_OBJECTS_SRC}) diff --git a/mindspore/ccsrc/backend/session/ascend_session.cc b/mindspore/ccsrc/backend/session/ascend_session.cc index cf2f85ed9f..39bfd9f9ea 100644 --- a/mindspore/ccsrc/backend/session/ascend_session.cc +++ b/mindspore/ccsrc/backend/session/ascend_session.cc @@ -34,7 +34,6 @@ #include "runtime/device/kernel_adjust.h" #include "runtime/device/ascend/ascend_stream_assign.h" #include "runtime/device/ascend/ascend_label_assign.h" -#include "predict/predict.h" #include "backend/session/anf_runtime_algorithm.h" #include "ir/scalar.h" #include "debug/anf_ir_dump.h" @@ -303,8 +302,6 @@ void AscendSession::CompileChildGraph(const KernelGraphPtr &child_graph) { save_graphs_path + "/" + "select_kernel_after" + "_graph_" + std::to_string(child_graph->graph_id()) + ".ir"; DumpIR(file_path, child_graph); } - // convert kernel Graph to model - predictmodel::StepConvertGraph(child_graph); // optimize graph HardwareOptimize(child_graph); // assign static memory of parameters @@ -333,8 +330,6 @@ void AscendSession::RunGraph(const GraphId &graph_id, const std::vector graph, memo->insert(graph.get()); MS_LOG(INFO) << "Start to do HardwareOptimize in graph: " << graph->graph_id(); - // convert kernel Graph to model - predictmodel::StepConvertGraph(graph.get()); HardwareOptimize(graph.get()); for (auto &child_graph : graph->child_graph_order()) { diff --git a/mindspore/ccsrc/backend/session/cpu_session.cc b/mindspore/ccsrc/backend/session/cpu_session.cc index 94862a0a59..6230699a3d 100644 --- a/mindspore/ccsrc/backend/session/cpu_session.cc +++ b/mindspore/ccsrc/backend/session/cpu_session.cc @@ -23,7 +23,6 @@ #include "common/utils.h" #include "backend/session/anf_runtime_algorithm.h" #include "runtime/device/kernel_runtime.h" -#include "predict/predict.h" #include "backend/kernel_compiler/cpu/cpu_kernel_factory.h" #include "runtime/device/cpu/kernel_select_cpu.h" #include "backend/optimizer/common/optimizer.h" @@ -79,7 +78,6 @@ GraphId CPUSession::CompileGraph(const AnfNodePtrList &lst, const AnfNodePtrList Optimize(graph); } #endif - predictmodel::StepConvertGraph(graph); MS_LOG(INFO) << "Build kernel"; BuildKernel(graph.get()); MS_LOG(INFO) << "Assign kernel address"; @@ -100,7 +98,6 @@ void CPUSession::RunGraph(const GraphId &graph_id, const std::vector need_sync_outputs; runtime_.BindInputOutput(kernel_graph.get(), inputs, outputs, &need_sync_outputs); MS_LOG(INFO) << "Run graph start"; - predictmodel::StepConvertWeight(inputs); auto execution_order = kernel_graph->execution_order(); Reorder(&execution_order); diff --git a/mindspore/ccsrc/backend/session/gpu_session.cc b/mindspore/ccsrc/backend/session/gpu_session.cc index 9d0a140108..d608f69edb 100644 --- a/mindspore/ccsrc/backend/session/gpu_session.cc +++ b/mindspore/ccsrc/backend/session/gpu_session.cc @@ -31,7 +31,6 @@ #include "backend/optimizer/gpu/replace_momentum_cast_fusion.h" #include "backend/optimizer/gpu/replace_addn_fusion.h" #include "runtime/device/kernel_runtime_manager.h" -#include "predict/predict.h" #include "common/utils.h" #include "common/trans.h" #include "utils/context/ms_context.h" @@ -190,8 +189,6 @@ GraphId GPUSession::CompileGraph(const AnfNodePtrList &lst, const AnfNodePtrList // Assign parameter keys. AssignParamKey(graph); #endif - // Convert kernel Graph to model - predictmodel::StepConvertGraph(graph); // Start gpu kernel runtime StartKernelRT(); // Dump .pb graph before hardware optimization @@ -245,8 +242,6 @@ void GPUSession::RunGraph(const GraphId &graph_id, const std::vectorType(); - MS_EXCEPTION_IF_NULL(type_ptr); - if (type_ptr->isa()) { - auto tensor_ptr = type_ptr->cast(); - MS_EXCEPTION_IF_NULL(tensor_ptr); - TypePtr elem = tensor_ptr->element(); - return elem; - } else if (type_ptr->isa()) { - auto tuple_ptr = type_ptr->cast(); - MS_EXCEPTION_IF_NULL(tuple_ptr); - auto tuple_i = (*tuple_ptr)[0]; - MS_EXCEPTION_IF_NULL(tuple_i); - if (tuple_i->isa()) { - auto tensor_ptr = tuple_i->cast(); - MS_EXCEPTION_IF_NULL(tensor_ptr); - TypePtr elem = tensor_ptr->element(); - MS_EXCEPTION_IF_NULL(elem); - return elem; - } else if (tuple_i->isa()) { - return type_ptr; - } else { - MS_LOG(EXCEPTION) << "unsupported type: " << type_ptr->ToString(); - } - } else if (type_ptr->isa()) { - return type_ptr; - } - std::string type_name = type_ptr->ToString(); - MS_LOG(EXCEPTION) - << "The output type of node should be a tensor type a number or a tuple of tensor type, but this is: " - << type_name; -} - -MsDataType GetMSDataType(TypeId ori_data_type) { - MsDataType dst_data_type; - switch (ori_data_type) { - case kNumberTypeFloat16: - dst_data_type = mindspore::predict::DataType_DT_FLOAT16; - return dst_data_type; - case kNumberTypeFloat32: - dst_data_type = mindspore::predict::DataType_DT_FLOAT; - return dst_data_type; - case kNumberTypeInt8: - dst_data_type = mindspore::predict::DataType_DT_INT8; - return dst_data_type; - case kNumberTypeInt32: - dst_data_type = mindspore::predict::DataType_DT_INT32; - return dst_data_type; - case kNumberTypeUInt8: - dst_data_type = mindspore::predict::DataType_DT_UINT8; - return dst_data_type; - case kNumberTypeUInt32: - dst_data_type = mindspore::predict::DataType_DT_UINT32; - return dst_data_type; - case kTypeUnknown: - dst_data_type = mindspore::predict::DataType_DT_UNDEFINED; - return dst_data_type; - default: - MS_LOG(EXCEPTION) << "Ms don't support this DataType"; - } -} - -MsFormat GetMsFormat(const std::string &format_str) { - if (format_str == kOpFormat_DEFAULT) { - MsFormat ms_format = predict::Format_NCHW; - return ms_format; - } else { - // all middle format default to NCHW - return predict::Format_NCHW; - } -} - -TensorPtr GetParaAscendTensor(const AnfNodePtr &anf_node) { - MS_EXCEPTION_IF_NULL(anf_node); - if (!anf_node->isa()) { - return nullptr; - } - auto device_type_id = AnfAlgo::GetOutputDeviceDataType(anf_node, 0); - // device type_ptr - auto device_type_ptr = GetTypePtr(anf_node); - // device shape - auto shape = AnfAlgo::GetOutputDeviceShape(anf_node, 0); - std::vector tensor_shape; - (void)std::transform(shape.begin(), shape.end(), std::back_inserter(tensor_shape), SizeToInt); - // device format - auto format = AnfAlgo::GetOutputFormat(anf_node, 0); - // device tensor - TensorPtr device_tensor = std::make_shared(device_type_id, tensor_shape); - // device info - device_tensor->SetDeviceInfo(format, device_type_ptr); - return device_tensor; -} - -TensorPtr GetParaCpuTensor(const AnfNodePtr &anf_node) { - MS_EXCEPTION_IF_NULL(anf_node); - if (!(anf_node->isa())) { - return nullptr; - } else { - auto ori_type_id = AnfAlgo::GetOutputInferDataType(anf_node, 0); - auto ori_type_ptr = GetTypePtr(anf_node); - auto ori_shape = AnfAlgo::GetOutputInferShape(anf_node, 0); - std::vector tensor_shape; - (void)std::transform(ori_shape.begin(), ori_shape.end(), std::back_inserter(tensor_shape), SizeToInt); - auto ori_format = AnfAlgo::GetOutputFormat(anf_node, 0); - TensorPtr cpu_tensor = std::make_shared(ori_type_id, tensor_shape); - cpu_tensor->SetDeviceInfo(ori_format, ori_type_ptr); - return cpu_tensor; - } -} - -TensorPtr GetValueTensor(const ValueNodePtr &const_node) { - MS_EXCEPTION_IF_NULL(const_node); - auto value_ptr = const_node->value(); - MS_EXCEPTION_IF_NULL(value_ptr); - if (!value_ptr->isa()) { - return nullptr; - } - TensorPtr tensor = value_ptr->cast(); - MS_EXCEPTION_IF_NULL(tensor); - auto data_type = tensor->Dtype(); - MS_EXCEPTION_IF_NULL(data_type); - auto type_id = data_type->type_id(); - auto shape = tensor->shape(); - TensorPtr tensor_constant = std::make_shared(type_id, shape); - tensor_constant->SetDeviceInfo(tensor->device_info().format_, tensor->device_info().data_type_); - return tensor_constant; -} - -TensorPtr GetKernelCpuTensor(const CNodePtr &c_node_ptr, size_t inx) { - if (c_node_ptr == nullptr || inx >= AnfAlgo::GetOutputTensorNum(c_node_ptr)) { - MS_LOG(ERROR) << "GetKernelCpuTensor failed"; - return nullptr; - } - auto ori_shape = AnfAlgo::GetOutputInferShape(c_node_ptr, inx); - auto ori_type_id = AnfAlgo::GetOutputInferDataType(c_node_ptr, inx); - std::vector tensor_shape; - (void)std::transform(ori_shape.begin(), ori_shape.end(), std::back_inserter(tensor_shape), SizeToInt); - auto ori_output_type = GetTypePtr(c_node_ptr); - TensorPtr device_tensor = std::make_shared(ori_type_id, tensor_shape); - auto format = AnfAlgo::GetOutputFormat(c_node_ptr, inx); - device_tensor->SetDeviceInfo(format, ori_output_type); - return device_tensor; -} - -TensorPtr GetKernelAscendTensor(const CNodePtr &c_node_ptr, size_t inx) { - if (c_node_ptr == nullptr || inx >= AnfAlgo::GetOutputTensorNum(c_node_ptr)) { - MS_LOG(ERROR) << "GetKernelAscendTensor failed"; - return nullptr; - } - auto shape = AnfAlgo::GetOutputDeviceShape(c_node_ptr, inx); - std::vector tensor_shape; - (void)std::transform(shape.begin(), shape.end(), std::back_inserter(tensor_shape), SizeToInt); - auto format = AnfAlgo::GetOutputFormat(c_node_ptr, inx); - auto type_id = AnfAlgo::GetOutputDeviceDataType(c_node_ptr, inx); - auto output_type_ptr = GetTypePtr(c_node_ptr); - TensorPtr device_tensor = std::make_shared(type_id, tensor_shape); - device_tensor->SetDeviceInfo(format, output_type_ptr); - return device_tensor; -} - -TensorPtr GetOutputTensor(const AnfNodePtr &out_node, size_t inx) { - MS_EXCEPTION_IF_NULL(out_node); - auto shape = AnfAlgo::GetOutputInferShape(out_node, inx); - std::vector tensor_shape; - (void)std::transform(shape.begin(), shape.end(), std::back_inserter(tensor_shape), SizeToInt); - auto type_id = AnfAlgo::GetOutputInferDataType(out_node, inx); - auto output_type_ptr = GetTypePtr(out_node); - auto format = AnfAlgo::GetOutputFormat(out_node, inx); - TensorPtr output_tensor = std::make_shared(type_id, tensor_shape); - output_tensor->SetDeviceInfo(format, output_type_ptr); - return output_tensor; -} - -bool FindNodeInMap(const std::unordered_map &node_map, const AnfNodePtr &node) { - return std::any_of(node_map.begin(), node_map.end(), - [node](const std::pair &kernel_key) { return kernel_key.first == node.get(); }); -} - -bool SaveDeviceModelUtil(const std::shared_ptr &new_ms_graph_ptr, const std::string &save_path_name, - SubGraphDefT *sub_graph) { - MS_EXCEPTION_IF_NULL(new_ms_graph_ptr); - MS_EXCEPTION_IF_NULL(sub_graph); - // save mindspore schema to file - new_ms_graph_ptr->name = "default_graph"; - std::unique_ptr sub_graph_ptr(sub_graph); - new_ms_graph_ptr->subgraphs.emplace_back(std::move(sub_graph_ptr)); - // get flatbuffer builder - flatbuffers::FlatBufferBuilder builder(1024); - auto offset = mindspore::predict::GraphDef::Pack(builder, new_ms_graph_ptr.get()); - builder.Finish(offset); - auto size = builder.GetSize(); - if (size == 0) { - MS_LOG(ERROR) << "builder has no size"; - return false; - } - auto content = builder.GetBufferPointer(); - std::ofstream output(save_path_name); - if (!output.is_open()) { - MS_LOG(EXCEPTION) << "mindspore.mindspoire output failed"; - } - (void)output.write((const char *)content, size); - output.close(); - return true; -} -} // namespace utils -} // namespace predict -} // namespace mindspore diff --git a/mindspore/ccsrc/predict/converter/attr_utils/convert_util.h b/mindspore/ccsrc/predict/converter/attr_utils/convert_util.h deleted file mode 100644 index 92fb5f63c9..0000000000 --- a/mindspore/ccsrc/predict/converter/attr_utils/convert_util.h +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright 2019-2020 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef MINDSPORE_CCSRC_PREDICT_CONVERTER_ATTR_UTILS_CONVERT_UTIL_H_ -#define MINDSPORE_CCSRC_PREDICT_CONVERTER_ATTR_UTILS_CONVERT_UTIL_H_ - -#include -#include -#include -#include -#include -#include -#include -#include "ir/tensor.h" -#include "backend/session/anf_runtime_algorithm.h" -#include "predict/schema/inner/ms_generated.h" - -using TensorPtr = mindspore::tensor::TensorPtr; -using TensorPtrList = std::vector; -using AllOutputTensors = std::unordered_map; -using OpDefT = mindspore::predict::OpDefT; -using GraphDefT = mindspore::predict::GraphDefT; -using TensorDefT = mindspore::predict::TensorDefT; -using SubGraphDefT = mindspore::predict::SubGraphDefT; -using SubGraphPtr = std::unique_ptr; -using MsDataType = mindspore::predict::DataType; -using MsFormat = mindspore::predict::Format; -using MsKernelKey = void *; -namespace mindspore { -namespace predict { -namespace utils { -TypePtr GetTypePtr(const AnfNodePtr &anf_node); -MsDataType GetMSDataType(TypeId ori_data_type); -MsFormat GetMsFormat(const std::string &format_str); -TensorPtr GetParaAscendTensor(const AnfNodePtr &anf_node); -TensorPtr GetParaCpuTensor(const AnfNodePtr &anf_node); -TensorPtr GetValueTensor(const ValueNodePtr &const_node); -TensorPtr GetKernelCpuTensor(const CNodePtr &c_node_ptr, size_t inx); -TensorPtr GetKernelAscendTensor(const CNodePtr &c_node_ptr, size_t inx); -TensorPtr GetOutputTensor(const AnfNodePtr &out_node, size_t inx); -bool FindNodeInMap(const std::unordered_map &Nodemap, const AnfNodePtr &node); -bool SaveDeviceModelUtil(const std::shared_ptr &new_ms_graph_ptr, const std::string &save_path_name, - SubGraphDefT *sub_graph_def_t); -} // namespace utils -} // namespace predict -} // namespace mindspore -#endif // MINDSPORE_CCSRC_PREDICT_CONVERTER_ATTR_UTILS_CONVERT_UTIL_H_ diff --git a/mindspore/ccsrc/predict/converter/attr_utils/op_attr_type.h b/mindspore/ccsrc/predict/converter/attr_utils/op_attr_type.h deleted file mode 100644 index 381da7678c..0000000000 --- a/mindspore/ccsrc/predict/converter/attr_utils/op_attr_type.h +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef MINDSPORE_CCSRC_PREDICT_CONVERTER_CPU_ATTR_UTILS_OP_ATTR_TYPE_H_ -#define MINDSPORE_CCSRC_PREDICT_CONVERTER_CPU_ATTR_UTILS_OP_ATTR_TYPE_H_ -namespace mindspore { -namespace predict { -namespace convert { -typedef enum CpuOpType { - CPU_OP_PAD = 0, - CPU_OP_MAXIMUM, - CPU_OP_CONCAT, - CPU_OP_SOFTMAX, - CPU_OP_ACTIVATION, - CPU_OP_CONV2D, - CPU_OP_FUSEDBATCHNORM, - CPU_OP_CAFFEBATCHNORM, - CPU_OP_SQUEEZE, - CPU_OP_BIASADD, - CPU_OP_POOLING, - CPU_OP_DEPTHWISECONV2D, - CPU_OP_DEDEPTHWISECONV2D, - CPU_OP_RESIZE, - CPU_OP_DETECTIONPOSTPROCESS, - CPU_OP_FULLCONNECTION, - CPU_OP_MEAN, - CPU_OP_DECONV2D, - CPU_OP_SCALE, - CPU_OP_ELTWISE, - CPU_OP_ADD, - CPU_OP_SLICE, - CPU_OP_MUL, - CPU_OP_EXP, - CPU_OP_RESHAPE, - CPU_OP_POWER, - CPU_OP_ARGMAX, - CPU_OP_ARGMAX_NETOUTPUT, - CPU_OP_MATMUL, - CPU_OP_CAFFEPRELU, - CPU_OP_STRIDEDSLICE, - CPU_OP_STACK, - CPU_OP_RANGE, - CPU_OP_EXPANDDIMS, - CPU_OP_TILE, - CPU_OP_CAST, - CPU_OP_CAFFECROP, - CPU_OP_PRESERVEED = 37 -} CpuOpType_t; -} // namespace convert -} // namespace predict -} // namespace mindspore -#endif // MINDSPORE_CCSRC_PREDICT_CONVERTER_CPU_ATTR_UTILS_OP_ATTR_TYPE_H_ diff --git a/mindspore/ccsrc/predict/converter/executor_tensor.cc b/mindspore/ccsrc/predict/converter/executor_tensor.cc deleted file mode 100644 index b51496a9b4..0000000000 --- a/mindspore/ccsrc/predict/converter/executor_tensor.cc +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "predict/converter/executor_tensor.h" - -namespace mindspore { -namespace executor { -int TensorCache::addExTensor(int tensor_key, const TensorPtr &tensor, int refCount, const std::vector &host_shape, - ExTensorType stable, bool inc) { - MS_EXCEPTION_IF_NULL(tensor); - TensorPtr tmp_tensor = tensor; - ExTensorPtr ex_tensor_ptr = - std::make_shared(tensor_key, tmp_tensor, refCount, nodeIndex, host_shape, stable); - int pre_index = ex_tensor_ptr->index_; - if (inc) { - nodeIndex++; - } - // no need to judge,just add to map directly - tensors[tensor_key].push_back(ex_tensor_ptr); - return pre_index; -} - -std::vector TensorCache::findTensor(int key) { - std::vector ex_tensors; - auto iter = tensors.find(key); - if (iter != tensors.end()) { - return iter->second; - } else { - MS_LOG(INFO) << "can not find any tensorlist"; - return ex_tensors; - } -} - -void TensorCache::deleteTensor(int key) { (void)tensors.erase(key); } -} // namespace executor -} // namespace mindspore diff --git a/mindspore/ccsrc/predict/converter/executor_tensor.h b/mindspore/ccsrc/predict/converter/executor_tensor.h deleted file mode 100644 index b33cea030d..0000000000 --- a/mindspore/ccsrc/predict/converter/executor_tensor.h +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright 2019-2020 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef MINDSPORE_CCSRC_PREDICT_CONVERTER_EXECUTOR_TENSOR_H_ -#define MINDSPORE_CCSRC_PREDICT_CONVERTER_EXECUTOR_TENSOR_H_ - -#include -#include -#include -#include -#include "ir/tensor.h" - -namespace mindspore { -namespace executor { -using TensorPtr = tensor::TensorPtr; -static constexpr int MS_MAX_REFCOUNT = 999; -enum ExTensorType { INPUTDATA, WEIGHTS, CONSTANT, KERNEL, OUTPUT }; -class ExTensor { - public: - int key_; - TensorPtr device_tensor_ptr_; - int ref_count_; - int index_; - std::vector host_shape_; - ExTensorType stable_; - ExTensor(int key, TensorPtr tensor_ptr, int ref_count, int index, std::vector host_shape, - ExTensorType ex_tensor_type) - : key_(key), - device_tensor_ptr_(std::move(tensor_ptr)), - ref_count_(ref_count), - index_(index), - host_shape_(std::move(host_shape)), - stable_(ex_tensor_type) {} - ~ExTensor() { host_shape_.clear(); } -}; -using ExTensorPtr = std::shared_ptr; -class TensorCache { - public: - TensorCache() = default; - - ~TensorCache() { tensors.clear(); } - - int addExTensor(int tensor_key, const TensorPtr &tensor, int refCount, const std::vector &host_shape, - ExTensorType stable, bool inc = true); - // just adjust for dynamic tensor - std::vector findTensor(int key); - void deleteTensor(int key); - const std::unordered_map> &GetCachedTensor() const { return tensors; } - - private: - std::unordered_map> tensors; - int nodeIndex = 0; -}; -using TensorCachePtr = std::shared_ptr; -} // namespace executor -} // namespace mindspore -#endif // MINDSPORE_CCSRC_PREDICT_CONVERTER_EXECUTOR_TENSOR_H_ diff --git a/mindspore/ccsrc/predict/converter/kernel2ms.cc b/mindspore/ccsrc/predict/converter/kernel2ms.cc deleted file mode 100644 index 04aceb62eb..0000000000 --- a/mindspore/ccsrc/predict/converter/kernel2ms.cc +++ /dev/null @@ -1,561 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "predict/converter/kernel2ms.h" -#include -#include "ir/anf.h" -#include "predict/converter/lite_model/op_attr_packer.h" -#include "mindspore/ccsrc/frontend/operator/ops.h" - -namespace mindspore { -namespace executor { -Kernel2Ms &Kernel2Ms::GetInstance() { - static Kernel2Ms instance; - return instance; -} - -bool Kernel2Ms::SetMemResue() const { - MS_LOG(INFO) << "MemResue start"; - return true; -} - -bool Kernel2Ms::SetAllTensors(const TensorCachePtr &tensor_cache, SubGraphDefT *ms_graph) { - if (tensor_cache == nullptr || ms_graph == nullptr) { - return false; - } - const std::unordered_map> &cachedTensors = tensor_cache->GetCachedTensor(); - size_t total_size = 0; - if (cachedTensors.empty()) { - return false; - } - for (auto &iter : cachedTensors) { - auto ex_tensors = iter.second; - total_size += ex_tensors.size(); - } - ms_graph->allTensors.resize(total_size); - for (auto &iter : cachedTensors) { - for (auto &ex_tensor : iter.second) { - std::unique_ptr ms_tensor(new TensorDefT()); - auto device_tensor_tmp = ex_tensor->device_tensor_ptr_; - auto device_d_type = device_tensor_tmp->data_type(); - ms_tensor->dataType = predict::utils::GetMSDataType(device_d_type); - auto device_shape = device_tensor_tmp->shape(); - ms_tensor->dims.clear(); - if (device_shape.empty()) { - ms_tensor->dims.push_back(1); - } else { - ms_tensor->dims.assign(device_shape.begin(), device_shape.end()); - } - std::string format_str = device_tensor_tmp->device_info().format_; - ms_tensor->format = predict::utils::GetMsFormat(format_str); - ms_tensor->offset = 0; - auto stable = ex_tensor->stable_; - if (stable == INPUTDATA || stable == CONSTANT || stable == WEIGHTS) { - ms_tensor->refCount = MS_MAX_REFCOUNT; - } else { - ms_tensor->refCount = 0; - } - ms_graph->allTensors[IntToSize(ex_tensor->index_)] = std::move(ms_tensor); - } - } - return true; -} - -bool Kernel2Ms::SetGraphOutputIdx(const KernelGraphPtr &kernel_graph_ptr, const TensorCachePtr &tensor_cache, - SubGraphDefT *ms_graph, AllOutputTensors *all_output_tensors) { - MS_EXCEPTION_IF_NULL(tensor_cache); - MS_EXCEPTION_IF_NULL(ms_graph); - MS_EXCEPTION_IF_NULL(all_output_tensors); - auto out_nodes = kernel_graph_ptr->outputs(); - if (out_nodes.empty()) { - return false; - } - // maybe need to judge out_nodes is real && output must be CNode - for (size_t i = 0; i < out_nodes.size(); ++i) { - std::vector real_inputs_link; - std::vector real_output_idx_link; - GetRealInpoutsPtr(out_nodes[i], &real_inputs_link, &real_output_idx_link); - if (real_inputs_link.empty()) { - MS_LOG(INFO) << "this graph output node is vitural node, has no real input"; - continue; - } - for (size_t k = 0; k < real_inputs_link.size(); ++k) { - int key = node_indexs_[out_nodes[i].get()]; - auto ex_tensor_list = tensor_cache->findTensor(key); - if (ex_tensor_list.empty()) { - MS_LOG(INFO) << "SetGraphOutputIdx do not add Extensor "; - continue; - } - auto ex_tensor = ex_tensor_list[real_output_idx_link[k]]; - ex_tensor_list.clear(); - ms_graph->outputIndex.push_back(ex_tensor->index_); - } - } - return true; -} - -bool Kernel2Ms::SetOpOutputIdx(const CNodePtr &c_node_ptr, const TensorPtr &output_tensor, - const TensorCachePtr &tensor_cache, int ref_count, size_t order_index, OpDefT *ms_node) { - MS_EXCEPTION_IF_NULL(c_node_ptr); - MS_EXCEPTION_IF_NULL(output_tensor); - MS_EXCEPTION_IF_NULL(ms_node); - MS_EXCEPTION_IF_NULL(tensor_cache); - if (!predict::utils::FindNodeInMap(node_indexs_, c_node_ptr)) { - MS_LOG(ERROR) << "can not find any pk_key in inited node_indexs map"; - return false; - } - int tensor_key = node_indexs_[c_node_ptr.get()]; - auto host_shape = AnfAlgo::GetOutputInferShape(c_node_ptr, order_index); - std::vector tensor_shape; - (void)std::transform(host_shape.begin(), host_shape.end(), std::back_inserter(tensor_shape), SizeToInt); - int outputIndex = tensor_cache->addExTensor(tensor_key, output_tensor, ref_count, tensor_shape, KERNEL); - ms_node->outputIndex.push_back(outputIndex); - return true; -} - -void Kernel2Ms::GetRealInpoutsPtr(const AnfNodePtr &node, std::vector *real_inputs, - std::vector *real_output_idx) { - MS_EXCEPTION_IF_NULL(real_inputs); - MS_EXCEPTION_IF_NULL(real_output_idx); - size_t default_idx = 0; - if (node->isa()) { - auto c_node = node->cast(); - MS_EXCEPTION_IF_NULL(c_node); - std::string c_node_name = GetCNodeFuncName(c_node); - if (c_node_name == prim::kPrimTupleGetItem->name()) { - auto v_node = c_node->inputs()[kTupleGetItemIndex]->cast(); - MS_EXCEPTION_IF_NULL(v_node); - default_idx = IntToSize(GetValue(v_node->value())); - real_inputs->push_back(c_node->inputs()[1]); - real_output_idx->push_back(default_idx); - return; - } else if (c_node_name == prim::kPrimDepend->name()) { - GetRealInpoutsPtr(c_node->inputs()[1], real_inputs, real_output_idx); - return; - } else if (c_node_name == prim::kPrimMakeTuple->name()) { - for (auto &in : c_node->inputs()) { - GetRealInpoutsPtr(in, real_inputs, real_output_idx); - } - return; - } else { - real_inputs->push_back(node); - real_output_idx->push_back(default_idx); - } - } else if (node->isa()) { - real_inputs->push_back(node); - real_output_idx->push_back(default_idx); - } else if (node->isa()) { - real_inputs->push_back(node); - real_output_idx->push_back(default_idx); - } -} - -bool Kernel2Ms::SetOpInputIdx(const CNodePtr &c_node_ptr, const TensorCachePtr &tensor_cache, OpDefT *ms_node) { - MS_EXCEPTION_IF_NULL(c_node_ptr); - MS_EXCEPTION_IF_NULL(tensor_cache); - MS_EXCEPTION_IF_NULL(ms_node); - for (size_t i = 1; i < c_node_ptr->inputs().size(); ++i) { - std::vector real_inputs; - std::vector real_output_idx; - GetRealInpoutsPtr(c_node_ptr->inputs()[i], &real_inputs, &real_output_idx); - if (real_inputs.empty()) { - MS_LOG(INFO) << "kernel has no inputs: " << c_node_ptr.get() << " input size[%lu]" << c_node_ptr->inputs().size(); - continue; - } - for (size_t j = 0; j < real_inputs.size(); ++j) { - int key = node_indexs_[real_inputs[j].get()]; - std::vector ex_tensor_list = tensor_cache->findTensor(key); - if (ex_tensor_list.empty()) { - continue; - } - ExTensorPtr ex_tensor_ptr = ex_tensor_list[real_output_idx[j]]; - ex_tensor_list.clear(); - ms_node->inputIndex.push_back(ex_tensor_ptr->index_); - } - } - return true; -} - -void Kernel2Ms::TransformGraphIndx() { - // transform index && anfnodeptr - if (node_indexs_.empty()) { - MS_LOG(EXCEPTION) << "node_indexs_ not ininted"; - } - for (auto &item : node_indexs_) { - index_nodes_[item.second] = item.first; - } -} - -bool Kernel2Ms::InitGraphInputsIndx(const KernelGraphPtr &kernel_graph_ptr) { - MS_EXCEPTION_IF_NULL(kernel_graph_ptr); - auto input_nodes = kernel_graph_ptr->inputs(); - if (input_nodes.empty()) { - return false; - } - for (const auto &input_node : input_nodes) { - if (input_node->isa()) { - if (!predict::utils::FindNodeInMap(node_indexs_, input_node)) { - // init every parameter node - node_indexs_[input_node.get()] = graph_index_; - graph_index_++; - } - } else { - MS_LOG(INFO) << "This node is anfnode, no need to handle, continue. node info: " << input_node->ToString(); - continue; - } - } - MS_LOG(DEBUG) << "inputs GraphIndex: " << graph_index_; - return true; -} - -bool Kernel2Ms::InitGraphValueNodesIndx(const KernelGraphPtr &kernel_graph_ptr) { - MS_EXCEPTION_IF_NULL(kernel_graph_ptr); - if (kernel_graph_ptr->value_nodes().empty()) { - return false; - } - for (auto &item : kernel_graph_ptr->value_nodes()) { - if (item.first->isa()) { - auto value_node = item.first->cast(); - MS_EXCEPTION_IF_NULL(value_node); - if (value_node == nullptr) { - MS_LOG(WARNING) << "value_node is nullptr"; - return false; - } - if (value_node->value() == nullptr) { - MS_LOG(ERROR) << "Constant value is null."; - return false; - } - if (!value_node->value()->isa()) { - continue; - } - if (!predict::utils::FindNodeInMap(node_indexs_, item.first)) { - // init node - auto node_ptr = item.first; - node_indexs_[node_ptr.get()] = graph_index_; - graph_index_++; - } - } - } - return true; -} - -bool Kernel2Ms::InitGraphOpsIndx(const KernelGraphPtr &kernel_graph_ptr) { - MS_EXCEPTION_IF_NULL(kernel_graph_ptr); - auto kernels = kernel_graph_ptr->execution_order(); - if (kernels.empty()) { - MS_LOG(WARNING) << "this graph has no kernel"; - return false; - } - for (size_t i = 0; i < kernels.size(); ++i) { - // for each kernel's inputs foreach real_input - if (kernels[i]->isa()) { - if (!predict::utils::FindNodeInMap(node_indexs_, kernels[i])) { - // init node - node_indexs_[kernels[i].get()] = graph_index_; - graph_index_++; - } - } - } - return true; -} - -bool Kernel2Ms::InitGraphOutputsIndx(const KernelGraphPtr &kernel_graph_ptr) { - MS_EXCEPTION_IF_NULL(kernel_graph_ptr); - // graph output && their inputs should link together - auto out_nodes = kernel_graph_ptr->outputs(); - if (out_nodes.empty()) { - MS_LOG(ERROR) << "this graph has no outputs"; - return false; - } - for (auto &item : out_nodes) { - if (!predict::utils::FindNodeInMap(node_indexs_, item)) { - node_indexs_[item.get()] = graph_index_; - graph_index_++; - } - } - return true; -} - -bool Kernel2Ms::InitGraphIndx(const KernelGraphPtr &kernel_graph_ptr) { - MS_EXCEPTION_IF_NULL(kernel_graph_ptr); - // only parameter - if (!InitGraphInputsIndx(kernel_graph_ptr)) { - return false; - } - // init value node - if (!InitGraphValueNodesIndx(kernel_graph_ptr)) { - return false; - } - // init op - if (!InitGraphOpsIndx(kernel_graph_ptr)) { - return false; - } - // init Graphoutput attention: out_put nodes have inputs - return InitGraphOutputsIndx(kernel_graph_ptr); -} - -bool Kernel2Ms::SetGraphInputTensors(const KernelGraphPtr &kernel_graph_ptr, const TensorCachePtr &tensor_cache, - SubGraphDefT *ms_graph) { - MS_EXCEPTION_IF_NULL(tensor_cache); - MS_EXCEPTION_IF_NULL(ms_graph); - MS_EXCEPTION_IF_NULL(kernel_graph_ptr); - if (convert_mode_ == kConvertUnused) { - return false; - } - if (kernel_graph_ptr->inputs().empty()) { - return false; - } - for (const auto &input_node : kernel_graph_ptr->inputs()) { - if (input_node->isa()) { - ParameterPtr pk_node = std::dynamic_pointer_cast(input_node); - TensorPtr device_tensor; - if (convert_mode_ == kConvertCpuMode) { - device_tensor = predict::utils::GetParaCpuTensor(input_node); - } else { - device_tensor = predict::utils::GetParaAscendTensor(input_node); - } - if (device_tensor == nullptr) { - return false; - } - ExTensorType node_type; - if (AnfAlgo::IsParameterWeight(pk_node)) { - node_type = WEIGHTS; - } else { - node_type = INPUTDATA; - } - if (!predict::utils::FindNodeInMap(node_indexs_, input_node)) { - MS_LOG(WARNING) << "can not find any pk_key in inited node_indexs map"; - return false; - } - auto pk_key = node_indexs_[input_node.get()]; - all_output_tensors_[pk_key].push_back(device_tensor); - int nodeRefCount = SizeToInt(AnfAlgo::GetOutputTensorNum(input_node)); - int nodeInputIdx = - tensor_cache->addExTensor(pk_key, device_tensor, nodeRefCount, device_tensor->shape(), node_type); - if (!AnfAlgo::IsParameterWeight(pk_node)) { - ms_graph->inputIndex.push_back(nodeInputIdx); - all_input_idxs_.push_back(nodeInputIdx); - } else { - input_weight_idxs_.push_back(nodeInputIdx); - all_input_idxs_.push_back(nodeInputIdx); - } - } - } - return true; -} - -bool Kernel2Ms::SetGraphValueTensors(const KernelGraphPtr &kernel_graph_ptr, const TensorCachePtr &tensor_cache) { - MS_EXCEPTION_IF_NULL(kernel_graph_ptr); - MS_EXCEPTION_IF_NULL(tensor_cache); - for (auto &item : kernel_graph_ptr->value_nodes()) { - if (item.first->isa()) { - auto const_node = item.first->cast(); - auto tensor_constant = predict::utils::GetValueTensor(const_node); - if (tensor_constant == nullptr) { - continue; - } - if (!predict::utils::FindNodeInMap(node_indexs_, item.first)) { - MS_LOG(WARNING) << "can not find any pk_key in inited node_indexs map"; - return false; - } - int constant_key = node_indexs_[(item.first).get()]; - all_output_tensors_[constant_key].push_back(tensor_constant); - auto shape = tensor_constant->shape(); - (void)tensor_cache->addExTensor(constant_key, tensor_constant, 0, shape, CONSTANT); - } - } - return true; -} - -bool Kernel2Ms::SetGraphOpTensors(const KernelGraphPtr &kernel_graph_ptr, const TensorCachePtr &tensor_cache, - SubGraphDefT *ms_graph) { - MS_EXCEPTION_IF_NULL(kernel_graph_ptr); - MS_EXCEPTION_IF_NULL(tensor_cache); - MS_EXCEPTION_IF_NULL(ms_graph); - auto kernels = kernel_graph_ptr->execution_order(); - if (kernels.empty()) { - MS_LOG(ERROR) << "this graph has no kernels"; - return false; - } - for (auto &kernel : kernels) { - if (!predict::utils::FindNodeInMap(node_indexs_, kernel)) { - MS_LOG(ERROR) << "can not find any pk_key in inited node_indexs map"; - return false; - } - auto kernel_key = node_indexs_[kernel.get()]; - std::unique_ptr ms_node(new OpDefT); - ms_node->name = kernel->fullname_with_scope(); - ms_node->fmkType = mindspore::predict::FmkType_CAFFE; - auto c_name = AnfAlgo::GetCNodeName(kernel); - auto fun = predict::convert::OpAttrFactory::GetInstance()->GetPackFun(c_name); - if (fun == nullptr) { - MS_LOG(WARNING) << "get node [" << kernel->fullname_with_scope() << "] attr failed."; - } else if (!fun(kernel, ms_node.get())) { - MS_LOG(ERROR) << "set node [" << kernel->fullname_with_scope() << "] attr failed."; - return false; - } - auto output_size = AnfAlgo::GetOutputTensorNum(kernel); - int nodeRefCount = SizeToInt(output_size); - for (size_t j = 0; j < output_size; ++j) { - TensorPtr device_tensor; - if (convert_mode_ == kConvertCpuMode) { - device_tensor = predict::utils::GetKernelCpuTensor(kernel, j); - } else if (convert_mode_ == kConvertAscendMode) { - device_tensor = predict::utils::GetKernelAscendTensor(kernel, j); - } - if (device_tensor == nullptr) { - return false; - } - all_output_tensors_[kernel_key].push_back(device_tensor); - if (!SetOpOutputIdx(kernel, device_tensor, tensor_cache, nodeRefCount, j, ms_node.get())) { - return false; - } - } - tmp_op_nodes_.emplace_back(ms_node.release()); - } - return true; -} - -bool Kernel2Ms::KernelGraph2MsGraph(const KernelGraphPtr &kernel_graph_ptr) { - MS_EXCEPTION_IF_NULL(kernel_graph_ptr); - graph_index_ = 0; - all_output_tensors_.clear(); - node_indexs_.clear(); - index_nodes_.clear(); - std::unique_ptr sub_ms_graph(new SubGraphDefT()); - if (!InitGraphIndx(kernel_graph_ptr)) { - return false; - } - TransformGraphIndx(); - tensor_cache_ptr_ = std::make_shared(); - // foreach node to init it's real output tensor - if (!SetGraphInputTensors(kernel_graph_ptr, tensor_cache_ptr_, sub_ms_graph.get())) { - return false; - } - // Get KernelGraph value node - if (!SetGraphValueTensors(kernel_graph_ptr, tensor_cache_ptr_)) { - return false; - } - // Get KernelGraph apply_kernel && add opNode - if (!SetGraphOpTensors(kernel_graph_ptr, tensor_cache_ptr_, sub_ms_graph.get())) { - return false; - } - // Get KernelGraph outputs - if (!SetGraphOutputIdx(kernel_graph_ptr, tensor_cache_ptr_, sub_ms_graph.get(), &all_output_tensors_)) { - return false; - } - auto kernels = kernel_graph_ptr->execution_order(); - for (size_t i = 0; i < kernels.size(); ++i) { - auto ms_node = tmp_op_nodes_[i]; - if (!SetOpInputIdx(kernels[i], tensor_cache_ptr_, ms_node)) { - return false; - } - std::unique_ptr ms_node_tmp(ms_node); - sub_ms_graph->nodes.emplace_back(std::move(ms_node_tmp)); - } - if (!SetAllTensors(tensor_cache_ptr_, sub_ms_graph.get())) { - return false; - } - if (!SetMemResue()) { - return false; - } - sub_ms_graph_ = std::move(sub_ms_graph); - sub_ms_graph_->name = "default_sub_graph"; - return true; -} - -bool Kernel2Ms::CheckInputSizes(const std::vector &input_tensors, - const std::vector &all_input_idxs) { - if (input_tensors.size() != all_input_idxs.size()) { - MS_LOG(EXCEPTION) << "real input tensors size:" << input_tensors.size() - << "not equal converted tesnors size:" << all_input_idxs.size() << "the graph has changed"; - } - for (auto in : all_input_idxs) { - if (in < sub_ms_graph_->allTensors.size()) { - auto real_tensor = input_tensors[in]; - auto convert_dims = sub_ms_graph_->allTensors[in]->dims; - auto real_dims = real_tensor->shape(); - if (real_dims.size() != convert_dims.size()) { - return false; - } else { - for (size_t i = 0; i < convert_dims.size(); ++i) { - if (convert_dims[i] != real_dims[i]) { - return false; - } - } - } - } else { - MS_LOG(EXCEPTION) << "index: " << in << "in all_input_idxs is valid"; - } - } - return true; -} - -void Kernel2Ms::ReleaseContextRes() { - tmp_op_nodes_.clear(); - node_indexs_.clear(); - index_nodes_.clear(); - tensor_cache_ptr_ = nullptr; - all_output_tensors_.clear(); -} - -bool Kernel2Ms::KernelInput2MS(const std::vector &input_tensors) { - const std::unordered_map> &cache_tensors = tensor_cache_ptr_->GetCachedTensor(); - if (cache_tensors.empty()) { - return false; - } - auto all_weights_idxs = GetAllInputWeightIdxs(); - auto all_input_idxs = GetAllInputIdxs(); - auto real_input_size = input_tensors.size(); - // check tensor size - bool ret = CheckInputSizes(input_tensors, all_input_idxs); - std::vector match_to_rel_idxs; - // indx order not matched,macth to it - if (!ret) { - for (auto idx : all_weights_idxs) { - auto macth_idx = real_input_size - idx; - match_to_rel_idxs.push_back(macth_idx); - } - } else { - match_to_rel_idxs = all_weights_idxs; - } - if (match_to_rel_idxs.size() == all_weights_idxs.size()) { - for (size_t j = 0; j < all_weights_idxs.size(); ++j) { - auto cache_idx = all_weights_idxs[j]; - auto match_idx = match_to_rel_idxs[j]; - auto real_tensor = input_tensors[match_idx]; - auto real_size = LongToSize(real_tensor->data().nbytes()); - auto real_data = real_tensor->data_c(); - MS_EXCEPTION_IF_NULL(real_data); - if (sub_ms_graph_->allTensors[cache_idx] != nullptr) { - sub_ms_graph_->allTensors[cache_idx]->data.resize(real_size); - } - if (memcpy_s(sub_ms_graph_->allTensors[cache_idx]->data.data(), real_size, real_data, real_size) != 0) { - MS_LOG(ERROR) << "KernelInput2MS memcpy_s failed"; - return false; - } - } - } - ReleaseContextRes(); - return true; -} - -bool Kernel2Ms::SaveDeviceModel(const std::shared_ptr &new_ms_graph_ptr, const std::string &save_path_name) { - MS_EXCEPTION_IF_NULL(new_ms_graph_ptr); - return predict::utils::SaveDeviceModelUtil(new_ms_graph_ptr, save_path_name, sub_ms_graph_.release()); -} -} // namespace executor -} // namespace mindspore diff --git a/mindspore/ccsrc/predict/converter/kernel2ms.h b/mindspore/ccsrc/predict/converter/kernel2ms.h deleted file mode 100644 index 073d618bb2..0000000000 --- a/mindspore/ccsrc/predict/converter/kernel2ms.h +++ /dev/null @@ -1,118 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef MINDSPORE_CCSRC_PREDICT_CONVERTER_KERNEL_TO_MS_H_ -#define MINDSPORE_CCSRC_PREDICT_CONVERTER_KERNEL_TO_MS_H_ - -#include -#include -#include -#include -#include -#include "backend/session/kernel_graph.h" -#include "predict/converter/executor_tensor.h" -#include "predict/schema/inner/ms_generated.h" -#include "predict/converter/attr_utils/convert_util.h" - -static constexpr size_t kTupleGetItemIndex = 2; -namespace mindspore { -namespace executor { -using KernelGraphPtr = std::shared_ptr; -enum ConvertMode { kConvertCpuMode, kConvertAscendMode, kConvertUnused }; -enum TargetMode { kCPUTarget, kGPUTarget, kUnknowTarget }; -class Kernel2Ms { - public: - static Kernel2Ms &GetInstance(); - - Kernel2Ms(const Kernel2Ms &) = delete; - - Kernel2Ms &operator=(const Kernel2Ms &) = delete; - - bool KernelGraph2MsGraph(const KernelGraphPtr &kernel_graph_ptr); - - bool KernelInput2MS(const std::vector &input_tensors); - - ConvertMode convert_mode() const { return convert_mode_; } - - void set_convert_mode(ConvertMode convert_mode) { convert_mode_ = convert_mode; } - - TargetMode device_target() const { return device_target_; } - - void set_device_target(TargetMode device_target) { device_target_ = device_target; } - - bool SaveDeviceModel(const std::shared_ptr &new_ms_graph_ptr, const std::string &save_path_name); - - private: - Kernel2Ms() : graph_index_(0) {} - - void ReleaseContextRes(); - - ~Kernel2Ms() = default; - - bool SetAllTensors(const TensorCachePtr &tensor_cache, SubGraphDefT *sub_graph_def_t); - - bool SetOpInputIdx(const CNodePtr &c_node_ptr, const TensorCachePtr &tensor_cache, OpDefT *ms_node); - - bool SetOpOutputIdx(const CNodePtr &c_node_ptr, const TensorPtr &output_tensor, const TensorCachePtr &tensor_cache, - int ref_count, size_t order_index, OpDefT *ms_node); - - bool SetGraphOutputIdx(const KernelGraphPtr &kernel_graph_ptr, const TensorCachePtr &tensor_cache, - SubGraphDefT *sub_graph_def_t, AllOutputTensors *all_output_tensors); - - void TransformGraphIndx(); - - void GetRealInpoutsPtr(const AnfNodePtr &node, std::vector *real_inputs, - std::vector *real_output_idx); - - bool InitGraphIndx(const KernelGraphPtr &kernel_graph_ptr); - - bool InitGraphInputsIndx(const KernelGraphPtr &kernel_graph_ptr); - - bool InitGraphValueNodesIndx(const KernelGraphPtr &kernel_graph_ptr); - - bool InitGraphOpsIndx(const KernelGraphPtr &kernel_graph_ptr); - - bool InitGraphOutputsIndx(const KernelGraphPtr &kernel_graph_ptr); - - bool SetGraphInputTensors(const KernelGraphPtr &kernel_graph_ptr, const TensorCachePtr &tensor_cache, - SubGraphDefT *sub_graph_def_t); - - bool SetGraphValueTensors(const KernelGraphPtr &kernel_graph_ptr, const TensorCachePtr &tensor_cache); - - bool SetGraphOpTensors(const KernelGraphPtr &kernel_graph_ptr, const TensorCachePtr &tensor_cache, - SubGraphDefT *sub_graph_def_t); - std::vector GetAllInputWeightIdxs() const { return input_weight_idxs_; } - std::vector GetAllInputIdxs() const { return all_input_idxs_; } - - bool CheckInputSizes(const std::vector &input_tensors, const std::vector &all_input_idxs); - - bool SetMemResue() const; - SubGraphPtr sub_ms_graph_; - AllOutputTensors all_output_tensors_; - std::vector tmp_op_nodes_; - std::unordered_map node_indexs_; - std::unordered_map index_nodes_; - int graph_index_ = 0; - TensorCachePtr tensor_cache_ptr_ = nullptr; - ConvertMode convert_mode_ = kConvertCpuMode; - TargetMode device_target_ = kCPUTarget; - std::vector input_weight_idxs_; - std::vector all_input_idxs_; -}; -using Kernel2MsPtr = std::shared_ptr; -} // namespace executor -} // namespace mindspore -#endif // MINDSPORE_CCSRC_PREDICT_CONVERTER_KERNEL_TO_MS_H_ diff --git a/mindspore/ccsrc/predict/converter/lite_model/op_attr_packer.cc b/mindspore/ccsrc/predict/converter/lite_model/op_attr_packer.cc deleted file mode 100644 index 52648812be..0000000000 --- a/mindspore/ccsrc/predict/converter/lite_model/op_attr_packer.cc +++ /dev/null @@ -1,110 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "predict/converter/lite_model/op_attr_packer.h" -#include "./securec.h" - -namespace mindspore { -namespace predict { -namespace convert { -// forward declare -bool Conv2dPacker(const CNodePtr &c_node_ptr, OpDefT *ms_op); -bool MatMulPacker(const CNodePtr &c_node_ptr, OpDefT *ms_op); -bool BiasAddPacker(const CNodePtr &c_node_ptr, OpDefT *ms_op); -bool ReshapePacker(const CNodePtr &c_node_ptr, OpDefT *ms_op); -bool ActivationPacker(const CNodePtr &c_node_ptr, OpDefT *ms_op); -bool PoolingPacker(const CNodePtr &c_node_ptr, OpDefT *ms_op); -bool FusedBatchNormPacker(const CNodePtr &c_node_ptr, OpDefT *ms_op); -bool AddPacker(const CNodePtr &c_node_ptr, OpDefT *ms_op); -bool CastPacker(const CNodePtr &c_node_ptr, OpDefT *ms_op); -bool MeanPacker(const CNodePtr &c_node_ptr, OpDefT *ms_op); -bool SoftmaxPacker(const CNodePtr &c_node_ptr, OpDefT *ms_op); -bool ScalePacker(const CNodePtr &c_node_ptr, OpDefT *ms_op); -bool AddFoldPacker(const CNodePtr &c_node_ptr, OpDefT *ms_op); -bool ArgMaxPacker(const CNodePtr &c_node_ptr, OpDefT *ms_op); -bool BatchNormFoldPacker(const CNodePtr &c_node_ptr, OpDefT *ms_op); -bool FakeQuantWithMinMaxPacker(const CNodePtr &c_node_ptr, OpDefT *ms_op); -bool FakeQuantWithMinMaxPerChannelPacker(const CNodePtr &c_node_ptr, OpDefT *ms_op); -bool MulPacker(const CNodePtr &c_node_ptr, OpDefT *ms_op); -bool MulFoldPacker(const CNodePtr &c_node_ptr, OpDefT *ms_op); -bool SqueezePacker(const CNodePtr &c_node_ptr, OpDefT *ms_op); - -OpAttrFactory::OpAttrFactory() { - pack_funs_ = {{"Conv2D", Conv2dPacker}, - {"MatMul", MatMulPacker}, - {"BiasAdd", BiasAddPacker}, - {"Reshape", ReshapePacker}, - {"Activation", ActivationPacker}, - {"ReLU", ActivationPacker}, - {"ReLU6", ActivationPacker}, - {"EReLU", ActivationPacker}, - {"LeakyReLU", ActivationPacker}, - {"Sigmoid", ActivationPacker}, - {"Softsign", ActivationPacker}, - {"Softplus", ActivationPacker}, - {"Tanh", ActivationPacker}, - {"HSwish", ActivationPacker}, - {"HSigmoid", ActivationPacker}, - {"MaxPool", PoolingPacker}, - {"MaxPool2D", PoolingPacker}, - {"MeanPool", PoolingPacker}, - {"GlobalPool", PoolingPacker}, - {"FusedBatchNorm", FusedBatchNormPacker}, - {"FusedBatchNormGrad", FusedBatchNormPacker}, - {"Cast", CastPacker}, - {"TensorAdd", AddPacker}, - {"SoftMax", SoftmaxPacker}, - {"SimpleMean", MeanPacker}, - {"ReduceMean", MeanPacker}, - {"AddFold", AddFoldPacker}, - {"ArgMax", ArgMaxPacker}, - {"BatchNorm", BatchNormFoldPacker}, - {"FakeQuantPerLayer", FakeQuantWithMinMaxPacker}, - {"FakeQuantPerChannel", FakeQuantWithMinMaxPerChannelPacker}, - {"Mul", MulPacker}, - {"MulFold", MulFoldPacker}, - {"Squeeze", SqueezePacker}}; -} -OpAttrPackFun OpAttrFactory::GetPackFun(const std::string &opType) { - if (pack_funs_.find(opType) == pack_funs_.end()) { - MS_LOG(WARNING) << "Op Attr pack fun [" << opType << "] not found."; - return nullptr; - } - return pack_funs_[opType]; -} - -mindspore::predict::Format GetAttrFormat(const std::string &format) { - if (format == kOpFormat_NCHW) { - return predict::Format::Format_NCHW; - } else if (format == kOpFormat_NHWC) { - return predict::Format::Format_NHWC; - } else { - return predict::Format::Format_NUM_OF_FORMAT; - } -} - -mindspore::predict::PadMode GetAttrPadMode(const std::string &pad_mode) { - if (pad_mode == "same") { - return mindspore::predict::PadMode::PadMode_SAME; - } else if (pad_mode == "valid") { - return mindspore::predict::PadMode::PadMode_VALID; - } else { - return mindspore::predict::PadMode::PadMode_NOTSET; - } -} -} // namespace convert -} // namespace predict -} // namespace mindspore diff --git a/mindspore/ccsrc/predict/converter/lite_model/op_attr_packer.h b/mindspore/ccsrc/predict/converter/lite_model/op_attr_packer.h deleted file mode 100644 index 5cb7b1ba21..0000000000 --- a/mindspore/ccsrc/predict/converter/lite_model/op_attr_packer.h +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef MINDSPORE_CCSRC_PREDICT_CONVERTER_OP_ATTR_PACKER_H_ -#define MINDSPORE_CCSRC_PREDICT_CONVERTER_OP_ATTR_PACKER_H_ - -#include -#include -#include -#include "backend/session/anf_runtime_algorithm.h" -#include "predict/schema/inner/ms_generated.h" - -static constexpr size_t kNIndex = 0; -static constexpr size_t kCIndex = 1; -static constexpr size_t kHIndex = 2; -static constexpr size_t kWIndex = 3; -static constexpr size_t kNCHWSize = 4; -namespace mindspore { -namespace predict { -namespace convert { -using OpAttrPackFun = bool (*)(const CNodePtr &c_node_ptr, OpDefT *ms_op); -class OpAttrFactory { - public: - static OpAttrFactory *GetInstance() { - static OpAttrFactory instance; - return &instance; - } - OpAttrFactory(const OpAttrFactory &) = delete; - OpAttrFactory &operator=(const OpAttrFactory &) = delete; - OpAttrPackFun GetPackFun(const std::string &op_type); - ~OpAttrFactory() { pack_funs_.clear(); } - OpAttrFactory(); - - private: - std::unordered_map pack_funs_; -}; - -mindspore::predict::Format GetAttrFormat(const std::string &format); - -mindspore::predict::PadMode GetAttrPadMode(const std::string &pad_mode); -} // namespace convert -} // namespace predict -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_PREDICT_CONVERTER_CPU_OP_INFO_OP_ATTR_FACTORY_H_ diff --git a/mindspore/ccsrc/predict/converter/lite_model/operations/activation_packer.cc b/mindspore/ccsrc/predict/converter/lite_model/operations/activation_packer.cc deleted file mode 100644 index 3dc09f70b4..0000000000 --- a/mindspore/ccsrc/predict/converter/lite_model/operations/activation_packer.cc +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "predict/converter/lite_model/op_attr_packer.h" - -namespace mindspore { -namespace predict { -namespace convert { -bool ActivationPacker(const CNodePtr &c_node_ptr, OpDefT *ms_op) { - if (c_node_ptr == nullptr || ms_op == nullptr) { - return false; - } - std::unique_ptr attr(new ActivationT()); - MS_EXCEPTION_IF_NULL(attr); - if (AnfAlgo::GetCNodeName(c_node_ptr) == "ReLU") { - attr->type = predict::ActivationType::ActivationType_RELU; - } else if (AnfAlgo::GetCNodeName(c_node_ptr) == "Sigmoid") { - attr->type = predict::ActivationType::ActivationType_SIGMOID; - } else if (AnfAlgo::GetCNodeName(c_node_ptr) == "ReLU6") { - attr->type = predict::ActivationType::ActivationType_RELU6; - } else if (AnfAlgo::GetCNodeName(c_node_ptr) == "ELU") { - attr->type = predict::ActivationType::ActivationType_ELU; - } else if (AnfAlgo::GetCNodeName(c_node_ptr) == "Leaky_ReLU") { - attr->type = predict::ActivationType::ActivationType_LEAKY_RELU; - } else if (AnfAlgo::GetCNodeName(c_node_ptr) == "ABS") { - attr->type = predict::ActivationType::ActivationType_ABS; - } else if (AnfAlgo::GetCNodeName(c_node_ptr) == "ReLU1") { - attr->type = predict::ActivationType::ActivationType_RELU1; - } else if (AnfAlgo::GetCNodeName(c_node_ptr) == "Softsign") { - attr->type = predict::ActivationType::ActivationType_SOFTSIGN; - } else if (AnfAlgo::GetCNodeName(c_node_ptr) == "Softplus") { - attr->type = predict::ActivationType::ActivationType_SOFTPLUS; - } else if (AnfAlgo::GetCNodeName(c_node_ptr) == "Tanh") { - attr->type = predict::ActivationType::ActivationType_TANH; - } else { - attr->type = predict::ActivationType::ActivationType_UNKNOW; - MS_LOG(WARNING) << "unknow Activation"; - } - ms_op->name = c_node_ptr->fullname_with_scope(); - ms_op->attr.type = OpT_Activation; - ms_op->attr.value = attr.release(); - return true; -} -} // namespace convert -} // namespace predict -} // namespace mindspore diff --git a/mindspore/ccsrc/predict/converter/lite_model/operations/add_packer.cc b/mindspore/ccsrc/predict/converter/lite_model/operations/add_packer.cc deleted file mode 100644 index 02a9bda65e..0000000000 --- a/mindspore/ccsrc/predict/converter/lite_model/operations/add_packer.cc +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "predict/converter/lite_model/op_attr_packer.h" - -namespace mindspore { -namespace predict { -namespace convert { -bool AddPacker(const CNodePtr &c_node_ptr, OpDefT *ms_op) { - if (c_node_ptr == nullptr || ms_op == nullptr) { - return false; - } - std::unique_ptr attr(new AddT()); - MS_EXCEPTION_IF_NULL(attr); - ms_op->name = c_node_ptr->fullname_with_scope(); - ms_op->attr.type = OpT_Add; - ms_op->attr.value = attr.release(); - return true; -} -} // namespace convert -} // namespace predict -} // namespace mindspore diff --git a/mindspore/ccsrc/predict/converter/lite_model/operations/addfold_packer.cc b/mindspore/ccsrc/predict/converter/lite_model/operations/addfold_packer.cc deleted file mode 100644 index b6affd5001..0000000000 --- a/mindspore/ccsrc/predict/converter/lite_model/operations/addfold_packer.cc +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "predict/converter/lite_model/op_attr_packer.h" - -namespace mindspore { -namespace predict { -namespace convert { -bool AddFoldPacker(const CNodePtr &c_node_ptr, OpDefT *ms_op) { - if (c_node_ptr == nullptr || ms_op == nullptr) { - return false; - } - std::unique_ptr attr(new AddFoldT()); - MS_EXCEPTION_IF_NULL(attr); - ms_op->attr.type = OpT_AddFold; - ms_op->attr.value = attr.release(); - return true; -} -} // namespace convert -} // namespace predict -} // namespace mindspore diff --git a/mindspore/ccsrc/predict/converter/lite_model/operations/argmax_packer.cc b/mindspore/ccsrc/predict/converter/lite_model/operations/argmax_packer.cc deleted file mode 100644 index 4df643704c..0000000000 --- a/mindspore/ccsrc/predict/converter/lite_model/operations/argmax_packer.cc +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "predict/converter/lite_model/op_attr_packer.h" - -namespace mindspore { -namespace predict { -namespace convert { -bool ArgMaxPacker(const CNodePtr &c_node_ptr, OpDefT *ms_op) { - if (c_node_ptr == nullptr || ms_op == nullptr) { - return false; - } - std::unique_ptr attr(new ArgMaxT()); - MS_EXCEPTION_IF_NULL(attr); - ms_op->attr.type = OpT_ArgMax; - ms_op->attr.value = attr.release(); - return true; -} -} // namespace convert -} // namespace predict -} // namespace mindspore diff --git a/mindspore/ccsrc/predict/converter/lite_model/operations/batchnormfold_packer.cc b/mindspore/ccsrc/predict/converter/lite_model/operations/batchnormfold_packer.cc deleted file mode 100644 index f05f3894be..0000000000 --- a/mindspore/ccsrc/predict/converter/lite_model/operations/batchnormfold_packer.cc +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "predict/converter/lite_model/op_attr_packer.h" - -namespace mindspore { -namespace predict { -namespace convert { -bool BatchNormFoldPacker(const CNodePtr &c_node_ptr, OpDefT *ms_op) { - if (c_node_ptr == nullptr || ms_op == nullptr) { - return false; - } - std::unique_ptr attr(new BatchNormFoldT()); - MS_EXCEPTION_IF_NULL(attr); - ms_op->attr.type = OpT_BatchNormFold; - ms_op->attr.value = attr.release(); - return true; -} -} // namespace convert -} // namespace predict -} // namespace mindspore diff --git a/mindspore/ccsrc/predict/converter/lite_model/operations/biasadd_packer.cc b/mindspore/ccsrc/predict/converter/lite_model/operations/biasadd_packer.cc deleted file mode 100644 index 6fe32c1f6b..0000000000 --- a/mindspore/ccsrc/predict/converter/lite_model/operations/biasadd_packer.cc +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include "predict/converter/lite_model/op_attr_packer.h" - -namespace mindspore { -namespace predict { -namespace convert { -bool BiasAddPacker(const CNodePtr &c_node_ptr, OpDefT *ms_op) { - if (c_node_ptr == nullptr || ms_op == nullptr) { - return false; - } - std::unique_ptr attr(new BiasAddT()); - MS_EXCEPTION_IF_NULL(attr); - attr->axis = {1}; - ms_op->name = c_node_ptr->fullname_with_scope(); - ms_op->attr.type = OpT_BiasAdd; - ms_op->attr.value = attr.release(); - return true; -} -} // namespace convert -} // namespace predict -} // namespace mindspore diff --git a/mindspore/ccsrc/predict/converter/lite_model/operations/cast_packer.cc b/mindspore/ccsrc/predict/converter/lite_model/operations/cast_packer.cc deleted file mode 100644 index d0f3f80f6c..0000000000 --- a/mindspore/ccsrc/predict/converter/lite_model/operations/cast_packer.cc +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "predict/converter/lite_model/op_attr_packer.h" - -namespace mindspore { -namespace predict { -namespace convert { -bool CastPacker(const CNodePtr &c_node_ptr, OpDefT *ms_op) { - if (c_node_ptr == nullptr || ms_op == nullptr) { - return false; - } - std::unique_ptr attr(new CastT()); - MS_EXCEPTION_IF_NULL(attr); - attr->srcT = 0; - attr->dstT = 0; - ms_op->name = c_node_ptr->fullname_with_scope(); - ms_op->attr.type = OpT_Cast; - ms_op->attr.value = attr.release(); - return true; -} -} // namespace convert -} // namespace predict -} // namespace mindspore diff --git a/mindspore/ccsrc/predict/converter/lite_model/operations/conv2d_packer.cc b/mindspore/ccsrc/predict/converter/lite_model/operations/conv2d_packer.cc deleted file mode 100644 index 176b235f5f..0000000000 --- a/mindspore/ccsrc/predict/converter/lite_model/operations/conv2d_packer.cc +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "predict/converter/lite_model/op_attr_packer.h" - -namespace mindspore { -namespace predict { -namespace convert { -bool Conv2dPacker(const CNodePtr &c_node_ptr, OpDefT *ms_op) { - if (c_node_ptr == nullptr || ms_op == nullptr) { - return false; - } - int kernel_group_value = AnfAlgo::GetNodeAttr(c_node_ptr, "group"); - int kernel_channel_value = AnfAlgo::GetNodeAttr(c_node_ptr, "out_channel"); - std::vector kernel_size_value = AnfAlgo::GetNodeAttr>(c_node_ptr, "kernel_size"); - std::string kernel_pad_mode_value = AnfAlgo::GetNodeAttr(c_node_ptr, "pad_mode"); - int kernel_pad_value = AnfAlgo::GetNodeAttr(c_node_ptr, "pad"); - auto kernel_stride_value = AnfAlgo::GetNodeAttr>(c_node_ptr, "stride"); - auto kernel_dilation_value = AnfAlgo::GetNodeAttr>(c_node_ptr, "dilation"); - std::string kernel_data_format_value = AnfAlgo::GetNodeAttr(c_node_ptr, "data_format"); - std::unique_ptr attr(new Conv2DT()); - MS_EXCEPTION_IF_NULL(attr); - attr->format = GetAttrFormat(kernel_data_format_value); - attr->group = kernel_group_value; - auto in_shape = AnfAlgo::GetPrevNodeOutputInferShape(c_node_ptr, 1); - if (in_shape.size() != kNCHWSize) { - return false; - } - attr->channelIn = SizeToInt(in_shape[1]); - attr->channelOut = kernel_channel_value; - attr->kernelW = kernel_size_value[0]; - attr->kernelH = kernel_size_value[1]; - attr->strideW = kernel_stride_value[0]; - attr->strideH = kernel_stride_value[1]; - attr->padMode = GetAttrPadMode(kernel_pad_mode_value); - attr->padUp = kernel_pad_value; - attr->padDown = kernel_pad_value; - attr->padLeft = kernel_pad_value; - attr->padRight = kernel_pad_value; - attr->dilateW = kernel_dilation_value[0]; - attr->dilateH = kernel_dilation_value[1]; - attr->hasBias = false; - ms_op->name = c_node_ptr->fullname_with_scope(); - ms_op->attr.type = OpT_Conv2D; - ms_op->attr.value = attr.release(); - return true; -} -} // namespace convert -} // namespace predict -} // namespace mindspore diff --git a/mindspore/ccsrc/predict/converter/lite_model/operations/fakequantwithminmax_packer.cc b/mindspore/ccsrc/predict/converter/lite_model/operations/fakequantwithminmax_packer.cc deleted file mode 100644 index 195a4fde9f..0000000000 --- a/mindspore/ccsrc/predict/converter/lite_model/operations/fakequantwithminmax_packer.cc +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "predict/converter/lite_model/op_attr_packer.h" - -namespace mindspore { -namespace predict { -namespace convert { -bool FakeQuantWithMinMaxPacker(const CNodePtr &c_node_ptr, OpDefT *ms_op) { - if (c_node_ptr == nullptr || ms_op == nullptr) { - return false; - } - std::unique_ptr attr(new FakeQuantWithMinMaxT()); - MS_EXCEPTION_IF_NULL(attr); - ms_op->attr.type = OpT_FakeQuantWithMinMax; - ms_op->attr.value = attr.release(); - return true; -} -} // namespace convert -} // namespace predict -} // namespace mindspore diff --git a/mindspore/ccsrc/predict/converter/lite_model/operations/fakequantwithminmaxperchannel_packer.cc b/mindspore/ccsrc/predict/converter/lite_model/operations/fakequantwithminmaxperchannel_packer.cc deleted file mode 100644 index 0074c87646..0000000000 --- a/mindspore/ccsrc/predict/converter/lite_model/operations/fakequantwithminmaxperchannel_packer.cc +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "predict/converter/lite_model/op_attr_packer.h" - -namespace mindspore { -namespace predict { -namespace convert { -bool FakeQuantWithMinMaxPerChannelPacker(const CNodePtr &c_node_ptr, OpDefT *ms_op) { - if (c_node_ptr == nullptr || ms_op == nullptr) { - return false; - } - std::unique_ptr attr(new FakeQuantWithMinMaxPerChannelT()); - MS_EXCEPTION_IF_NULL(attr); - ms_op->attr.type = OpT_FakeQuantWithMinMaxPerChannel; - ms_op->attr.value = attr.release(); - return true; -} -} // namespace convert -} // namespace predict -} // namespace mindspore diff --git a/mindspore/ccsrc/predict/converter/lite_model/operations/fusedbatchnorm_packer.cc b/mindspore/ccsrc/predict/converter/lite_model/operations/fusedbatchnorm_packer.cc deleted file mode 100644 index e0092820c2..0000000000 --- a/mindspore/ccsrc/predict/converter/lite_model/operations/fusedbatchnorm_packer.cc +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "predict/converter/lite_model/op_attr_packer.h" - -namespace mindspore { -namespace predict { -namespace convert { -bool FusedBatchNormPacker(const CNodePtr &c_node_ptr, OpDefT *ms_op) { - if (c_node_ptr == nullptr || ms_op == nullptr) { - return false; - } - std::unique_ptr attr(new FusedBatchNormT()); - MS_EXCEPTION_IF_NULL(attr); - auto kernel_epsilon = AnfAlgo::GetNodeAttr(c_node_ptr, "epsilon"); - attr->epsilon = kernel_epsilon; - ms_op->name = c_node_ptr->fullname_with_scope(); - ms_op->attr.type = OpT_FusedBatchNorm; - ms_op->attr.value = attr.release(); - return true; -} -} // namespace convert -} // namespace predict -} // namespace mindspore diff --git a/mindspore/ccsrc/predict/converter/lite_model/operations/matmul_packer.cc b/mindspore/ccsrc/predict/converter/lite_model/operations/matmul_packer.cc deleted file mode 100644 index a0f82810a7..0000000000 --- a/mindspore/ccsrc/predict/converter/lite_model/operations/matmul_packer.cc +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "predict/converter/lite_model/op_attr_packer.h" - -namespace mindspore { -namespace predict { -namespace convert { -bool MatMulPacker(const CNodePtr &c_node_ptr, OpDefT *ms_op) { - if (c_node_ptr == nullptr || ms_op == nullptr) { - return false; - } - bool kernel_transpore_a = AnfAlgo::GetNodeAttr(c_node_ptr, "transpose_a"); - bool kernel_transpore_b = AnfAlgo::GetNodeAttr(c_node_ptr, "transpose_b"); - std::unique_ptr attr(new MatMulT()); - MS_EXCEPTION_IF_NULL(attr); - attr->transposeA = kernel_transpore_a; - attr->transposeB = kernel_transpore_b; - ms_op->name = c_node_ptr->fullname_with_scope(); - ms_op->attr.type = OpT_MatMul; - ms_op->attr.value = attr.release(); - return true; -} -} // namespace convert -} // namespace predict -} // namespace mindspore diff --git a/mindspore/ccsrc/predict/converter/lite_model/operations/mean_packer.cc b/mindspore/ccsrc/predict/converter/lite_model/operations/mean_packer.cc deleted file mode 100644 index eac3fa88f1..0000000000 --- a/mindspore/ccsrc/predict/converter/lite_model/operations/mean_packer.cc +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "predict/converter/lite_model/op_attr_packer.h" - -namespace mindspore { -namespace predict { -namespace convert { -bool MeanPacker(const CNodePtr &c_node_ptr, OpDefT *ms_op) { - if (c_node_ptr == nullptr || ms_op == nullptr) { - return false; - } - std::unique_ptr attr(new MeanT()); - MS_EXCEPTION_IF_NULL(attr); - attr->axis = {1}; - attr->keepDims = false; - ms_op->name = c_node_ptr->fullname_with_scope(); - ms_op->attr.type = OpT_Mean; - ms_op->attr.value = attr.release(); - return true; -} -} // namespace convert -} // namespace predict -} // namespace mindspore diff --git a/mindspore/ccsrc/predict/converter/lite_model/operations/mul_packer.cc b/mindspore/ccsrc/predict/converter/lite_model/operations/mul_packer.cc deleted file mode 100644 index 6c430e79e7..0000000000 --- a/mindspore/ccsrc/predict/converter/lite_model/operations/mul_packer.cc +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "predict/converter/lite_model/op_attr_packer.h" - -namespace mindspore { -namespace predict { -namespace convert { -bool MulPacker(const CNodePtr &c_node_ptr, OpDefT *ms_op) { - if (c_node_ptr == nullptr || ms_op == nullptr) { - return false; - } - std::unique_ptr attr(new MulT()); - MS_EXCEPTION_IF_NULL(attr); - ms_op->attr.type = OpT_Mul; - ms_op->attr.value = attr.release(); - return true; -} -} // namespace convert -} // namespace predict -} // namespace mindspore diff --git a/mindspore/ccsrc/predict/converter/lite_model/operations/mulflod_packer.cc b/mindspore/ccsrc/predict/converter/lite_model/operations/mulflod_packer.cc deleted file mode 100644 index 1df7204875..0000000000 --- a/mindspore/ccsrc/predict/converter/lite_model/operations/mulflod_packer.cc +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "predict/converter/lite_model/op_attr_packer.h" - -namespace mindspore { -namespace predict { -namespace convert { -bool MulFoldPacker(const CNodePtr &c_node_ptr, OpDefT *ms_op) { - if (c_node_ptr == nullptr || ms_op == nullptr) { - return false; - } - std::unique_ptr attr(new MulFoldT()); - MS_EXCEPTION_IF_NULL(attr); - ms_op->name = c_node_ptr->fullname_with_scope(); - ms_op->attr.type = OpT_MulFold; - ms_op->attr.value = attr.release(); - return true; -} -} // namespace convert -} // namespace predict -} // namespace mindspore diff --git a/mindspore/ccsrc/predict/converter/lite_model/operations/pooling_packer.cc b/mindspore/ccsrc/predict/converter/lite_model/operations/pooling_packer.cc deleted file mode 100644 index edfdcda040..0000000000 --- a/mindspore/ccsrc/predict/converter/lite_model/operations/pooling_packer.cc +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "predict/converter/lite_model/op_attr_packer.h" - -namespace mindspore { -namespace predict { -namespace convert { -bool PoolingPacker(const CNodePtr &c_node_ptr, OpDefT *ms_op) { - if (c_node_ptr == nullptr || ms_op == nullptr) { - return false; - } - std::unique_ptr attr(new PoolingT()); - MS_EXCEPTION_IF_NULL(attr); - std::string kernel_format_value = AnfAlgo::GetNodeAttr(c_node_ptr, "data_format"); - attr->format = GetAttrFormat(kernel_format_value); - auto c_name = AnfAlgo::GetCNodeName(c_node_ptr); - if (c_name == "MaxPool") { - ms_op->name = c_node_ptr->fullname_with_scope(); - attr->poolingMode = mindspore::predict::PoolMode::PoolMode_MAX_POOLING; - } else if (c_name == "MeanPool") { - ms_op->name = c_node_ptr->fullname_with_scope(); - attr->poolingMode = mindspore::predict::PoolMode::PoolMode_MEAN_POOLING; - } else if (c_name == "GlobalPool") { - ms_op->name = c_node_ptr->fullname_with_scope(); - } else { - MS_LOG(ERROR) << "unknowed pooling type."; - return false; - } - std::vector kernel_ksize = AnfAlgo::GetNodeAttr>(c_node_ptr, "ksize"); - attr->windowW = kernel_ksize[kHIndex]; - attr->windowH = kernel_ksize[kWIndex]; - std::vector kernel_strides = AnfAlgo::GetNodeAttr>(c_node_ptr, "strides"); - attr->strideW = kernel_strides[kHIndex]; - attr->strideH = kernel_strides[kWIndex]; - std::string kernel_pad_mode_value = AnfAlgo::GetNodeAttr(c_node_ptr, "padding"); - attr->padMode = GetAttrPadMode(kernel_pad_mode_value); - attr->padUp = 0; - attr->padDown = 0; - attr->padLeft = 0; - attr->padRight = 0; - ms_op->attr.type = OpT_Pooling; - ms_op->attr.value = attr.release(); - return true; -} -} // namespace convert -} // namespace predict -} // namespace mindspore diff --git a/mindspore/ccsrc/predict/converter/lite_model/operations/reshape_packer.cc b/mindspore/ccsrc/predict/converter/lite_model/operations/reshape_packer.cc deleted file mode 100644 index a0a263631d..0000000000 --- a/mindspore/ccsrc/predict/converter/lite_model/operations/reshape_packer.cc +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "predict/converter/lite_model/op_attr_packer.h" - -namespace mindspore { -namespace predict { -namespace convert { -bool ReshapePacker(const CNodePtr &c_node_ptr, OpDefT *ms_op) { - if (c_node_ptr == nullptr || ms_op == nullptr) { - return false; - } - std::unique_ptr attr(new ReshapeT()); - MS_EXCEPTION_IF_NULL(attr); - attr->format = predict::Format::Format_NCHW; - ms_op->name = c_node_ptr->fullname_with_scope(); - ms_op->attr.type = OpT_Reshape; - ms_op->attr.value = attr.release(); - return true; -} -} // namespace convert -} // namespace predict -} // namespace mindspore diff --git a/mindspore/ccsrc/predict/converter/lite_model/operations/scale_packer.cc b/mindspore/ccsrc/predict/converter/lite_model/operations/scale_packer.cc deleted file mode 100644 index 356775247d..0000000000 --- a/mindspore/ccsrc/predict/converter/lite_model/operations/scale_packer.cc +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "predict/converter/lite_model/op_attr_packer.h" - -namespace mindspore { -namespace predict { -namespace convert { -bool ScalePacker(const CNodePtr &c_node_ptr, OpDefT *ms_op) { - if (c_node_ptr == nullptr || ms_op == nullptr) { - return false; - } - std::unique_ptr attr(new ScaleT()); - MS_EXCEPTION_IF_NULL(attr); - attr->format = predict::Format::Format_NCHW; - ms_op->name = c_node_ptr->fullname_with_scope(); - ms_op->attr.type = OpT_Scale; - ms_op->attr.value = attr.release(); - return true; -} -} // namespace convert -} // namespace predict -} // namespace mindspore diff --git a/mindspore/ccsrc/predict/converter/lite_model/operations/softmax_packer.cc b/mindspore/ccsrc/predict/converter/lite_model/operations/softmax_packer.cc deleted file mode 100644 index fe96bae451..0000000000 --- a/mindspore/ccsrc/predict/converter/lite_model/operations/softmax_packer.cc +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "predict/converter/lite_model/op_attr_packer.h" - -namespace mindspore { -namespace predict { -namespace convert { -bool SoftmaxPacker(const CNodePtr &c_node_ptr, OpDefT *ms_op) { - if (c_node_ptr == nullptr || ms_op == nullptr) { - return false; - } - std::unique_ptr attr(new SoftMaxT()); - MS_EXCEPTION_IF_NULL(attr); - attr->axis = {1}; - ms_op->name = c_node_ptr->fullname_with_scope(); - ms_op->attr.type = OpT_SoftMax; - ms_op->attr.value = attr.release(); - return true; -} -} // namespace convert -} // namespace predict -} // namespace mindspore diff --git a/mindspore/ccsrc/predict/converter/lite_model/operations/squeeze_packer.cc b/mindspore/ccsrc/predict/converter/lite_model/operations/squeeze_packer.cc deleted file mode 100644 index 7e836fe021..0000000000 --- a/mindspore/ccsrc/predict/converter/lite_model/operations/squeeze_packer.cc +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "predict/converter/lite_model/op_attr_packer.h" - -namespace mindspore { -namespace predict { -namespace convert { -bool SqueezePacker(const CNodePtr &c_node_ptr, OpDefT *ms_op) { - if (c_node_ptr == nullptr || ms_op == nullptr) { - return false; - } - std::unique_ptr attr(new SqueezeT()); - MS_EXCEPTION_IF_NULL(attr); - - std::vector kernel_axis_value = AnfAlgo::GetNodeAttr>(c_node_ptr, "axis"); - attr->axis = kernel_axis_value; - - ms_op->attr.type = OpT_Squeeze; - ms_op->attr.value = attr.release(); - return true; -} -} // namespace convert -} // namespace predict -} // namespace mindspore diff --git a/mindspore/ccsrc/predict/generator/ir/ir_model.cc b/mindspore/ccsrc/predict/generator/ir/ir_model.cc deleted file mode 100644 index ff46524577..0000000000 --- a/mindspore/ccsrc/predict/generator/ir/ir_model.cc +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "predict/generator/ir/ir_model.h" - -#include -#include - -#include "utils/log_adapter.h" - -namespace mindspore { -namespace generator { -IRModel::~IRModel() { ir_tasks_.clear(); } -void IRModel::SetIrTaskInfos(const std::vector &ir_tasks) { - (void)std::copy(ir_tasks.begin(), ir_tasks.end(), std::back_inserter(ir_tasks_)); -} -} // namespace generator -} // namespace mindspore diff --git a/mindspore/ccsrc/predict/generator/ir/ir_model.h b/mindspore/ccsrc/predict/generator/ir/ir_model.h deleted file mode 100644 index 09e22ff78d..0000000000 --- a/mindspore/ccsrc/predict/generator/ir/ir_model.h +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef MINDSPORE_CCSRC_EXECUTOR_GENERATOR_IR_IR_MODEL_H_ -#define MINDSPORE_CCSRC_EXECUTOR_GENERATOR_IR_IR_MODEL_H_ -#include -#include -#include -#include "predict/generator/ir/ir_task_info.h" -namespace mindspore { -namespace generator { -class IRModel { - public: - void SetIrTaskInfos(const std::vector &ir_tasks); - IRModel() = default; - ~IRModel(); - - private: - std::vector ir_tasks_; -}; -using IrModelPtr = std::shared_ptr; -} // namespace generator -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_EXECUTOR_GENERATOR_IR_IR_MODEL_H_ diff --git a/mindspore/ccsrc/predict/generator/ir/ir_task_info.cc b/mindspore/ccsrc/predict/generator/ir/ir_task_info.cc deleted file mode 100644 index 1c275ea8ed..0000000000 --- a/mindspore/ccsrc/predict/generator/ir/ir_task_info.cc +++ /dev/null @@ -1,244 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "predict/generator/ir/ir_task_info.h" -#include "utils/log_adapter.h" - -namespace mindspore { -namespace generator { -bool CceIRTaskInfo::SerializeIRToProto() { - auto cce_task_def_ptr = std::unique_ptr(); - auto kernel_context_ptr = std::unique_ptr(); - MS_EXCEPTION_IF_NULL(cce_task_def_ptr); - MS_EXCEPTION_IF_NULL(kernel_context_ptr); - kernel_context_ptr->set_kernel_type(k_ctx_.kernel_type); - kernel_context_ptr->set_op_id(k_ctx_.op_id); - kernel_context_ptr->set_kernel_func_id(k_ctx_.kernel_func_id); - kernel_context_ptr->set_op_index(k_ctx_.op_index); - kernel_context_ptr->set_is_flowtable(k_ctx_.is_flowtable); - kernel_context_ptr->set_args_count(k_ctx_.args_count); - for (unsigned int i : k_ctx_.origin_op_index) { - kernel_context_ptr->add_origin_op_index(i); - } - void *tmp_args_offset = static_cast((k_ctx_.args_offset).data()); - if (tmp_args_offset == nullptr) { - MS_LOG(WARNING) << "tmp_args_offset have no data"; - return false; - } - kernel_context_ptr->set_args_offset(tmp_args_offset, k_ctx_.args_offset.size()); - cce_task_def_ptr->set_allocated_kernel_context(std::move(kernel_context_ptr).get()); - cce_task_def_ptr->set_stub_func(stub_func_); - cce_task_def_ptr->set_block_dim(block_dim_); - cce_task_def_ptr->set_args_size(args_size_); - void *tmp_sm_desc = static_cast(sm_desc_.data()); - if (tmp_sm_desc == nullptr) { - MS_LOG(WARNING) << "tmp_sm_desc have no data"; - return false; - } - cce_task_def_ptr->set_sm_desc(tmp_sm_desc, sm_desc_.size()); - - void *tmp_flow_table = static_cast(flow_table_.data()); - if (tmp_flow_table == nullptr) { - MS_LOG(WARNING) << "tmp_flow_table have no data"; - return false; - } - cce_task_def_ptr->set_flow_table(tmp_flow_table, flow_table_.size()); - return true; -} - -CceIRTaskInfo::~CceIRTaskInfo() { - args_.clear(); - sm_desc_.clear(); - flow_table_.clear(); -} - -bool TbeIRTaskInfo::SerializeIRToProto() { - auto tbe_task_def_ptr = std::unique_ptr(); - MS_EXCEPTION_IF_NULL(tbe_task_def_ptr); - tbe_task_def_ptr->set_stub_func(stub_func_); - tbe_task_def_ptr->set_block_dim(block_dim_); - tbe_task_def_ptr->set_args_size(args_size_); - void *tmp_args = static_cast(args_.data()); - if (tmp_args == nullptr) { - MS_LOG(WARNING) << "tmp_args have no data"; - return false; - } - tbe_task_def_ptr->set_args(tmp_args, args_.size()); - void *tmp_sm_desc = static_cast(sm_desc_.data()); - if (tmp_sm_desc == nullptr) { - MS_LOG(WARNING) << "tmp_sm_desc have no data"; - return false; - } - tbe_task_def_ptr->set_sm_desc(tmp_sm_desc, sm_desc_.size()); - void *tmp_meta_data = static_cast(meta_data_.data()); - if (tmp_meta_data == nullptr) { - MS_LOG(WARNING) << "tmp_meta_data have no data"; - return false; - } - tbe_task_def_ptr->set_meta_data(tmp_meta_data, meta_data_.size()); - for (auto &in : input_data_addrs_) { - tbe_task_def_ptr->add_input_addrs(in); - } - for (auto &ou : output_data_addrs_) { - tbe_task_def_ptr->add_output_addrs(ou); - } - for (auto &wk : workspace_addrs_) { - tbe_task_def_ptr->add_workspace_addrs(wk); - } - return true; -} - -TbeIRTaskInfo::~TbeIRTaskInfo() { - args_.clear(); - sm_desc_.clear(); - meta_data_.clear(); - input_data_addrs_.clear(); - output_data_addrs_.clear(); - workspace_addrs_.clear(); -} - -bool AicpuIRTaskInfo::SerializeIRToProto() { - auto aicpu_task_def_ptr = std::unique_ptr(); - MS_EXCEPTION_IF_NULL(aicpu_task_def_ptr); - aicpu_task_def_ptr->set_op_type(op_type_); - aicpu_task_def_ptr->set_flag(flag_); - for (auto &shape : input_data_shapes_) { - auto in_shape_ptr = aicpu_task_def_ptr->add_input_shapes(); - for (auto &in_sh : shape) { - in_shape_ptr->add_shape(static_cast(in_sh)); - } - } - for (auto &shape : output_data_shapes_) { - auto ou_shape_ptr = aicpu_task_def_ptr->add_output_shapes(); - for (auto &ou_sh : shape) { - ou_shape_ptr->add_shape(static_cast(ou_sh)); - } - } - for (auto &in_type : input_data_types_) { - aicpu_task_def_ptr->add_input_types(in_type); - } - for (auto &ou_type : output_data_types_) { - aicpu_task_def_ptr->add_output_types(ou_type); - } - for (auto &in_addr : input_data_addrs_) { - aicpu_task_def_ptr->add_input_addrs(in_addr); - } - for (auto &ou_addr : output_data_addrs_) { - aicpu_task_def_ptr->add_output_addrs(ou_addr); - } - void *tmp_node_def = static_cast(node_def_.data()); - if (tmp_node_def == nullptr) { - MS_LOG(WARNING) << "tmp_node_def have no data"; - return false; - } - aicpu_task_def_ptr->set_node_def(tmp_node_def, node_def_.size()); - void *tmp_func_def = static_cast(func_def_.data()); - if (tmp_func_def == nullptr) { - MS_LOG(WARNING) << "tmp_func_def have no data"; - return false; - } - aicpu_task_def_ptr->set_func_def(tmp_func_def, func_def_.size()); - return true; -} - -AicpuIRTaskInfo::~AicpuIRTaskInfo() { - input_data_types_.clear(); - input_data_shapes_.clear(); - input_data_addrs_.clear(); - output_data_types_.clear(); - output_data_shapes_.clear(); - output_data_addrs_.clear(); - node_def_.clear(); - func_def_.clear(); -} - -bool LabelIRTaskInfo::SerializeIRToProto() { - auto label_task_def_ptr = std::unique_ptr(); - MS_EXCEPTION_IF_NULL(label_task_def_ptr); - label_task_def_ptr->set_label_id(label_id_); - return true; -} - -bool EventIRTaskInfo::SerializeIRToProto() { - auto event_task_def_ptr = std::unique_ptr(); - MS_EXCEPTION_IF_NULL(event_task_def_ptr); - event_task_def_ptr->set_event_id(event_id_); - return true; -} - -bool HcclIRTaskInfo::SerializeIRToProto() { - auto hccl_task_def_ptr = std::unique_ptr(); - MS_EXCEPTION_IF_NULL(hccl_task_def_ptr); - hccl_task_def_ptr->set_hccl_type(hccl_type_); - hccl_task_def_ptr->set_input_addr(input_data_addr_); - hccl_task_def_ptr->set_output_addr(output_data_addr_); - auto tmp_wk = static_cast(workspace_.data()); - hccl_task_def_ptr->set_workspace(tmp_wk, workspace_.size()); - hccl_task_def_ptr->set_workspace_num(workspace_num_); - auto tmp_pri_def = static_cast(private_def_.data()); - hccl_task_def_ptr->set_private_def(tmp_pri_def, private_def_.size()); - hccl_task_def_ptr->set_ops_kernel_store(ops_kernel_store_); - hccl_task_def_ptr->set_count(count_); - hccl_task_def_ptr->set_root_id(root_id_); - hccl_task_def_ptr->set_op_type(op_type_); - hccl_task_def_ptr->set_data_type(data_type_); - return true; -} - -HcclIRTaskInfo::~HcclIRTaskInfo() { - workspace_.clear(); - private_def_.clear(); -} - -bool ProfilerIRTaskInfo::SerializeIRToProto() { - auto profiler_task_def_ptr = std::unique_ptr(); - MS_EXCEPTION_IF_NULL(profiler_task_def_ptr); - profiler_task_def_ptr->set_log_id(log_id_); - profiler_task_def_ptr->set_flat(flat_); - profiler_task_def_ptr->set_notify(notify_); - return true; -} - -bool MemcpyAsyncIRTaskInfo::SerializeIRToProto() { - auto mem_task_def_ptr = std::unique_ptr(); - MS_EXCEPTION_IF_NULL(mem_task_def_ptr); - mem_task_def_ptr->set_dst(dst_); - mem_task_def_ptr->set_dst_max(dst_max_); - mem_task_def_ptr->set_src(src_); - mem_task_def_ptr->set_count(count_); - mem_task_def_ptr->set_kind(kind_); - return true; -} - -bool StreamSwitchIRTaskInfo::SerializeIRToProto() { - auto stream_switch_task_def_ptr = std::unique_ptr(); - MS_EXCEPTION_IF_NULL(stream_switch_task_def_ptr); - stream_switch_task_def_ptr->set_true_stream_id(true_stream_id_); - stream_switch_task_def_ptr->set_input_addr(input_addr_); - stream_switch_task_def_ptr->set_value_addr(value_addr_); - stream_switch_task_def_ptr->set_cond(cond_); - stream_switch_task_def_ptr->set_data_type(data_type_); - return true; -} - -bool StreamActiveIRTaskInfo::SerializeIRToProto() { - auto stream_active_task_def_ptr = std::unique_ptr(); - MS_EXCEPTION_IF_NULL(stream_active_task_def_ptr); - stream_active_task_def_ptr->set_active_stream_id(active_stream_id_); - return true; -} -} // namespace generator -} // namespace mindspore diff --git a/mindspore/ccsrc/predict/generator/ir/ir_task_info.h b/mindspore/ccsrc/predict/generator/ir/ir_task_info.h deleted file mode 100644 index ad0df0419a..0000000000 --- a/mindspore/ccsrc/predict/generator/ir/ir_task_info.h +++ /dev/null @@ -1,295 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef MINDSPORE_CCSRC_EXECUTOR_GENERATOR_IR_IR_TASK_H_ -#define MINDSPORE_CCSRC_EXECUTOR_GENERATOR_IR_IR_TASK_H_ -#include -#include -#include -#include -#include -#include "proto/ge_runtime_taskinfo.pb.h" - -namespace mindspore { -namespace generator { -using TaskType = ::ge::model_runner::TaskDef_TaskType; -enum TaskTmpType { - CCE_TMP_DEF = 0, - TBE_TMP_DEF = 1, - AICPU_TMP_DEF = 2, - LABEL_TMP_DEF = 3, - EVENT_TMP_DEF = 4, - HCCL_TMP_DEF = 5, - PROFILER_TRACE_TMP_DEF = 6, - MEMCPY_ASYNC_TMP_DEF = 7, - STREAM_SWITCH_TMP_DEF = 8, - STREAM_ACTIVE_TMP_DEF = 9 -}; - -struct KernelContext { - uint32_t kernel_type = 0; - uint32_t op_id = 0; - uint32_t kernel_func_id = 0; - uint32_t op_index = 0; - bool is_flowtable = false; - std::vector args_offset; - uint32_t args_count = 0; - std::vector origin_op_index; -}; - -class IRtaskInfo { - public: - virtual ~IRtaskInfo() = default; - virtual bool SerializeIRToProto() = 0; - - protected: - IRtaskInfo(TaskType task_type, TaskTmpType task_tmp_type, uint64_t stream_id) - : task_type_(task_type), task_tmp_type_(task_tmp_type), stream_id_(stream_id) {} - - public: - uint64_t GetStreamId() const { return stream_id_; } - TaskType GetTaskType() const { return task_type_; } - TaskTmpType GetTaskTmpType() const { return task_tmp_type_; } - - private: - TaskType task_type_; - TaskTmpType task_tmp_type_; - uint64_t stream_id_ = 0; -}; - -using IRtaskInfoPtr = std::shared_ptr; - -class CceIRTaskInfo : public IRtaskInfo { - public: - CceIRTaskInfo(TaskType task_type, uint64_t stream_id, KernelContext k_ctx, std::string stub_func, uint32_t block_dim, - std::vector args, uint32_t args_size, std::vector sm_desc, - std::vector flow_table) - : IRtaskInfo(task_type, CCE_TMP_DEF, stream_id), - k_ctx_(std::move(k_ctx)), - stub_func_(std::move(stub_func)), - block_dim_(block_dim), - args_(std::move(args)), - args_size_(args_size), - sm_desc_(std::move(sm_desc)), - flow_table_(std::move(flow_table)) {} - ~CceIRTaskInfo() override; - bool SerializeIRToProto() override; - - private: - KernelContext k_ctx_; - std::string stub_func_; - uint32_t block_dim_ = 0; - std::vector args_; - // uintptr_t args_addr_; - uint32_t args_size_ = 0; - std::vector sm_desc_; - std::vector flow_table_; -}; - -class TbeIRTaskInfo : public IRtaskInfo { - public: - TbeIRTaskInfo(TaskType task_type, uint64_t stream_id, std::string stub_func, uint32_t block_dim, - std::vector args, uint32_t args_size, std::vector sm_desc, - std::vector meta_data, std::vector input_data_addrs, - std::vector output_data_addrs, std::vector workspace_addrs) - : IRtaskInfo(task_type, TBE_TMP_DEF, stream_id), - stub_func_(std::move(stub_func)), - block_dim_(block_dim), - args_(std::move(args)), - args_size_(args_size), - sm_desc_(std::move(sm_desc)), - meta_data_(std::move(meta_data)), - input_data_addrs_(std::move(input_data_addrs)), - output_data_addrs_(std::move(output_data_addrs)), - workspace_addrs_(std::move(workspace_addrs)) {} - ~TbeIRTaskInfo() override; - bool SerializeIRToProto() override; - - private: - std::string stub_func_; - uint32_t block_dim_ = 0; - std::vector args_; - uint32_t args_size_ = 0; - std::vector sm_desc_; - // uintptr_t binary_; - // uint32_t binary_size_; - std::vector meta_data_; - std::vector input_data_addrs_; - std::vector output_data_addrs_; - std::vector workspace_addrs_; - // std::vector flow_table_; -}; - -class AicpuIRTaskInfo : public IRtaskInfo { - public: - AicpuIRTaskInfo(TaskType task_type, uint64_t stream_id, std::string op_type, uint32_t flag, - std::vector input_data_types, std::vector> input_data_shapes, - std::vector input_data_addrs, std::vector output_data_types, - std::vector> output_data_shapes, std::vector output_data_addrs, - std::vector node_def, std::vector func_def) - : IRtaskInfo(task_type, AICPU_TMP_DEF, stream_id), - op_type_(std::move(op_type)), - flag_(flag), - input_data_types_(std::move(input_data_types)), - input_data_shapes_(std::move(input_data_shapes)), - input_data_addrs_(std::move(input_data_addrs)), - output_data_types_(std::move(output_data_types)), - output_data_shapes_(std::move(output_data_shapes)), - output_data_addrs_(std::move(output_data_addrs)), - node_def_(std::move(node_def)), - func_def_(std::move(func_def)) {} - ~AicpuIRTaskInfo() override; - bool SerializeIRToProto() override; - - private: - std::string op_type_; - uint32_t flag_ = 0; - std::vector input_data_types_; - std::vector> input_data_shapes_; - std::vector input_data_addrs_; - std::vector output_data_types_; - std::vector> output_data_shapes_; - std::vector output_data_addrs_; - std::vector node_def_; - std::vector func_def_; -}; - -class LabelIRTaskInfo : public IRtaskInfo { - public: - LabelIRTaskInfo(TaskType task_type, uint64_t stream_id, uint32_t label_id) - : IRtaskInfo(task_type, LABEL_TMP_DEF, stream_id), label_id_(label_id) {} - ~LabelIRTaskInfo() override {} - bool SerializeIRToProto() override; - - private: - uint32_t label_id_ = 0; -}; - -class EventIRTaskInfo : public IRtaskInfo { - public: - EventIRTaskInfo(TaskType task_type, uint64_t stream_id, uint32_t event_id) - : IRtaskInfo(task_type, EVENT_TMP_DEF, stream_id), event_id_(event_id) {} - ~EventIRTaskInfo() override {} - bool SerializeIRToProto() override; - - private: - uint32_t event_id_ = 0; -}; - -class HcclIRTaskInfo : public IRtaskInfo { - public: - HcclIRTaskInfo(TaskType task_type, uint64_t stream_id, std::string hccl_type, uintptr_t input_data_addr, - uintptr_t output_data_addr, std::vector workspace, int64_t workspace_num, - std::vector private_def, uintptr_t ops_kernel_store, int32_t count, int64_t root_id, - int64_t op_type, int64_t data_type) - : IRtaskInfo(task_type, HCCL_TMP_DEF, stream_id), - hccl_type_(std::move(hccl_type)), - input_data_addr_(input_data_addr), - output_data_addr_(output_data_addr), - workspace_(std::move(workspace)), - workspace_num_(workspace_num), - private_def_(std::move(private_def)), - ops_kernel_store_(ops_kernel_store), - count_(count), - root_id_(root_id), - op_type_(op_type), - data_type_(data_type) {} - ~HcclIRTaskInfo() override; - bool SerializeIRToProto() override; - - private: - std::string hccl_type_; - uintptr_t input_data_addr_ = 0; - uintptr_t output_data_addr_ = 0; - std::vector workspace_; - int64_t workspace_num_ = 0; - std::vector private_def_; - uintptr_t ops_kernel_store_ = 0; - int32_t count_ = 0; - int64_t root_id_ = 0; - int64_t op_type_ = 0; - int64_t data_type_ = 0; -}; - -class ProfilerIRTaskInfo : public IRtaskInfo { - public: - ProfilerIRTaskInfo(TaskType task_type, uint64_t stream_id, uint64_t log_id, bool notify, uint32_t flat) - : IRtaskInfo(task_type, PROFILER_TRACE_TMP_DEF, stream_id), log_id_(log_id), notify_(notify), flat_(flat) {} - ~ProfilerIRTaskInfo() override {} - bool SerializeIRToProto() override; - - private: - uint64_t log_id_ = 0; - bool notify_ = false; - uint32_t flat_ = 0; -}; - -class MemcpyAsyncIRTaskInfo : public IRtaskInfo { - public: - MemcpyAsyncIRTaskInfo(TaskType task_type, uint32_t stream_id, uint64_t dst, uint64_t dst_max, uint64_t src, - uint64_t count, int64_t kind) - : IRtaskInfo(task_type, MEMCPY_ASYNC_TMP_DEF, stream_id), - dst_(dst), - dst_max_(dst_max), - src_(src), - count_(count), - kind_(kind) {} - ~MemcpyAsyncIRTaskInfo() override {} - bool SerializeIRToProto() override; - - private: - uint64_t dst_ = 0; - uint64_t dst_max_ = 0; - uint64_t src_ = 0; - uint64_t count_ = 0; - uint32_t kind_ = 0; -}; - -class StreamSwitchIRTaskInfo : public IRtaskInfo { - public: - StreamSwitchIRTaskInfo(TaskType task_type, uint64_t stream_id, uint32_t true_stream_id, uintptr_t input_addr, - uintptr_t value_addr, uint32_t cond, int64_t data_type) - : IRtaskInfo(task_type, STREAM_SWITCH_TMP_DEF, stream_id), - true_stream_id_(true_stream_id), - input_addr_(input_addr), - value_addr_(value_addr), - cond_(cond), - data_type_(data_type) {} - ~StreamSwitchIRTaskInfo() override {} - bool SerializeIRToProto() override; - - private: - uint32_t true_stream_id_ = 0; - uintptr_t input_addr_ = 0; - uintptr_t value_addr_ = 0; - uint32_t cond_ = 0; - int64_t data_type_ = 0; -}; - -class StreamActiveIRTaskInfo : public IRtaskInfo { - public: - StreamActiveIRTaskInfo(TaskType task_type, uint64_t stream_id, uint32_t active_stream_id) - : IRtaskInfo(task_type, STREAM_ACTIVE_TMP_DEF, stream_id), active_stream_id_(active_stream_id) {} - ~StreamActiveIRTaskInfo() override {} - bool SerializeIRToProto() override; - - private: - uint32_t active_stream_id_ = 0; -}; -}; // namespace generator -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_EXECUTOR_GENERATOR_IR_IR_TASK_H_ diff --git a/mindspore/ccsrc/predict/generator/utils/ir_model_util.cc b/mindspore/ccsrc/predict/generator/utils/ir_model_util.cc deleted file mode 100644 index 8128009472..0000000000 --- a/mindspore/ccsrc/predict/generator/utils/ir_model_util.cc +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "predict/generator/utils/ir_model_util.h" -namespace mindspore { -namespace generator { -IRModelUtil &IRModelUtil::GetInstance() { - static IRModelUtil instance; - return instance; -} - -void IRModelUtil::Init() { - MS_LOG(INFO) << "IRModel init success"; - version_ = "defaultVersion"; - stream_num_ = 0; - event_num_ = 0; - batch_num_ = 0; - memory_size_ = 0; - weight_size_ = 0; - var_size_ = 0; - logic_mem_base_ = 0; - logic_var_base_ = 0; - logic_var_base_ = 0; - priority_ = 0; - is_enable_save_model_ = false; - min_static_offset_ = 0; - max_dynamic_offset_ = 0; -} -} // namespace generator -} // namespace mindspore diff --git a/mindspore/ccsrc/predict/generator/utils/ir_model_util.h b/mindspore/ccsrc/predict/generator/utils/ir_model_util.h deleted file mode 100644 index 1de50725f2..0000000000 --- a/mindspore/ccsrc/predict/generator/utils/ir_model_util.h +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef MINDSPORE_CCSRC_PREDICT_GENERATOR_IR_IR_MODEL_UTIL_H_ -#define MINDSPORE_CCSRC_PREDICT_GENERATOR_IR_IR_MODEL_UTIL_H_ -#include -#include -#include -#include -#include -#include "utils/log_adapter.h" - -namespace mindspore { -namespace generator { -class IRModelUtil { - public: - static IRModelUtil &GetInstance(); - IRModelUtil(const IRModelUtil &) = delete; - IRModelUtil &operator=(const IRModelUtil &) = delete; - void Init(); - - void set_version(const std::string &version) { version_ = version; } - void set_stream_num(uint32_t stream_num) { stream_num_ = stream_num; } - void set_event_num(uint32_t event_num) { event_num_ = event_num; } - void set_batch_num(uint32_t batch_num) { batch_num_ = batch_num; } - void set_memory_size(uint32_t memory_size) { memory_size_ = memory_size; } - void set_weight_size(uint32_t weight_size) { weight_size_ = weight_size; } - void set_var_size(uint32_t var_size) { var_size_ = var_size; } - void set_logic_mem_base(uint32_t logic_mem_base) { logic_mem_base_ = logic_mem_base; } - void set_logic_weight_base(uint32_t logic_weight_base) { logic_weight_base_ = logic_weight_base; } - void set_logic_var_base(uint32_t logic_var_base) { logic_var_base_ = logic_var_base; } - void set_priority(uint32_t priority) { priority_ = priority; } - void set_is_enable_save_model(bool is_enable_save_model) { is_enable_save_model_ = is_enable_save_model; } - void set_min_static_offset(uint64_t min_static_offset) { min_static_offset_ = min_static_offset; } - void set_max_dynamic_offset(uint64_t max_dynamic_offset) { max_dynamic_offset_ = max_dynamic_offset; } - void set_max_mem_size(uint64_t max_mem_size) { max_mem_size_ = max_mem_size; } - void set_irmodel_mem_base(uint8_t irmodel_mem_base) { irmodel_mem_base_ = irmodel_mem_base; } - - std::string version() const { return version_; } - uint32_t stream_num() const { return stream_num_; } - uint32_t event_num() const { return event_num_; } - uint32_t batch_num() const { return batch_num_; } - uint64_t memory_size() const { return memory_size_; } - uint64_t weight_size() const { return weight_size_; } - uint64_t var_size() const { return var_size_; } - uint64_t logic_mem_base() const { return logic_mem_base_; } - uint64_t logic_weight_base() const { return logic_weight_base_; } - uint64_t logic_var_base() const { return logic_var_base_; } - uint32_t priority() const { return priority_; } - bool is_enable_save_model() const { return is_enable_save_model_; } - uint64_t min_static_offset() const { return min_static_offset_; } - uint64_t max_dynamic_offset() const { return max_dynamic_offset_; } - uint64_t max_mem_size() const { return max_mem_size_; } - uint8_t irmodel_mem_base() const { return irmodel_mem_base_; } - - private: - IRModelUtil() = default; - ~IRModelUtil() = default; - std::string version_; - uint32_t stream_num_ = 0; - uint32_t event_num_ = 0; - uint32_t batch_num_ = 0; - uint64_t memory_size_ = 0; - uint64_t weight_size_ = 0; - uint64_t var_size_ = 0; - uint64_t logic_mem_base_ = 0; - uint64_t logic_weight_base_ = 0; - uint64_t logic_var_base_ = 0; - uint32_t priority_ = 0; - bool is_enable_save_model_ = false; - uint64_t min_static_offset_ = 0; - uint64_t max_dynamic_offset_ = 0; - uint64_t max_mem_size_ = 0; - uint8_t irmodel_mem_base_ = 0; -}; -} // namespace generator -} // namespace mindspore - -#endif // MINDSPORE_CCSRC_PREDICT_GENERATOR_IR_IR_MODEL_UTIL_H_ diff --git a/mindspore/ccsrc/predict/predict.cc b/mindspore/ccsrc/predict/predict.cc deleted file mode 100644 index bbb12c3787..0000000000 --- a/mindspore/ccsrc/predict/predict.cc +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright 2020 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "predict/predict.h" - -#include -#include -#include - -namespace mindspore { -namespace predictmodel { -void StepConvertGraph(const KernelGraphPtr &kernel_graph_ptr) { - MS_LOG(INFO) << "start convert_graph step"; - // get kernel_graph. this graph can be origin or device, depends on which steps to persistence - MS_EXCEPTION_IF_NULL(kernel_graph_ptr); - bool save_ms_model = MsContext::GetInstance()->save_ms_model_flag(); - if (save_ms_model) { - if (kernel_graph_ptr->inputs().empty()) { - return; - } - // set convert_mode: convert cpu info or convert Davnici - executor::Kernel2Ms::GetInstance().set_convert_mode(executor::kConvertCpuMode); - // convert kernel_graph to sub_ms_graph - bool ret = executor::Kernel2Ms::GetInstance().KernelGraph2MsGraph(kernel_graph_ptr); - if (!ret) { - MS_LOG(WARNING) << "convert to mindsporeGraph failed"; - } else { - MS_LOG(INFO) << "convert to Graph success"; - } - } -} - -void StepConvertWeight(const std::vector &inputs) { - MS_LOG(INFO) << "start convert_input step"; - // get all inputs tensor - bool save_ms_model = MsContext::GetInstance()->save_ms_model_flag(); - std::string save_path = MsContext::GetInstance()->save_ms_model_path(); - if (save_ms_model) { - if (inputs.empty()) { - return; - } - MS_LOG(INFO) << "save ms model is true to path " << save_path; - if (!executor::Kernel2Ms::GetInstance().KernelInput2MS(inputs)) { - MS_LOG(WARNING) << "convert mindspore kernel input failed"; - } - auto new_ms_graph_ptr = std::make_shared(); - bool ret = executor::Kernel2Ms::GetInstance().SaveDeviceModel(new_ms_graph_ptr, save_path); - if (!ret) { - MS_LOG(WARNING) << "convert to mindsporeGraph failed"; - } else { - MS_LOG(INFO) << "save ms model success"; - } - } -} -} // namespace predictmodel -} // namespace mindspore diff --git a/mindspore/ccsrc/predict/predict.h b/mindspore/ccsrc/predict/predict.h deleted file mode 100644 index 46429fae1b..0000000000 --- a/mindspore/ccsrc/predict/predict.h +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef MINDSPORE_CCSRC_PREDICT_H_ -#define MINDSPORE_CCSRC_PREDICT_H_ - -#include -#include -#include "backend/session/session_basic.h" -#include "predict/converter/kernel2ms.h" - -namespace mindspore { -namespace predictmodel { -using KernelGraphPtr = std::shared_ptr; -void StepConvertGraph(const KernelGraphPtr &kernel_graph_ptr); -void StepConvertWeight(const std::vector &inputs); -} // namespace predictmodel -} // namespace mindspore -#endif // MINDSPORE_CCSRC_PREDICT_H_ diff --git a/mindspore/ccsrc/predict/proto/DModel_ir.proto b/mindspore/ccsrc/predict/proto/DModel_ir.proto deleted file mode 100644 index 02bfa94df3..0000000000 --- a/mindspore/ccsrc/predict/proto/DModel_ir.proto +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto3"; -import public "Graph_ir.proto"; -import public "ge_runtime_taskinfo.proto"; -package ge.model_runner; -option cc_enable_arenas = true; - -message ModelTaskDef { - - string version = 1; - - repeated TaskDef task = 10; - - uint32 stream_num = 11; - uint32 event_num = 12; - uint32 batch_num_ = 13; - - uint64 memory_size = 14; - uint64 weight_size = 15; - uint64 var_size_ = 16; - - uint64 logic_mem_base_ = 17; - uint64 logic_weight_base_ = 18; - uint64 logic_var_base_ = 19; - - uint32 priority_ = 20; -} diff --git a/mindspore/ccsrc/predict/proto/Graph_ir.proto b/mindspore/ccsrc/predict/proto/Graph_ir.proto deleted file mode 100644 index af91ec0917..0000000000 --- a/mindspore/ccsrc/predict/proto/Graph_ir.proto +++ /dev/null @@ -1,125 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto3"; - -package mindspore; - -// Data type definition -enum DataType { - DT_UNDEFINED = 0; - // Basic types. - DT_BOOL = 1; // bool - - DT_INT8 = 2; // int8_t - DT_INT16 = 3; // int16_t - DT_INT32 = 4; // int32_t - DT_INT64 = 5; // int64_t - - DT_UINT8 = 6; // uint8_t - DT_UINT16 = 7; // uint16_t - DT_UINT32 = 8; // uint32_t - DT_UINT64 = 9; // uint64_t - - DT_FLOAT16 = 10; // float 16 - DT_FLOAT32 = 11; // float 32 - DT_FLOAT64 = 12; // float 64 - - DT_STRING = 13; // string - DT_TENSOR = 14; // tensor - DT_GRAPH = 15; // graph - - // list type - DT_BOOLS = 16; // list of bool - - DT_INTS8 = 17; // list of int8_t - DT_INTS16 = 18; // list of int16_t - DT_INTS32 = 19; // list of int32_t - DT_INTS64 = 20; // list of int64_t - - DT_UINTS8 = 21; // list of uint8_t - DT_UINTS16 = 22; // list of uint16_t - DT_UINTS32 = 23; // list of uint32_t - DT_UINTS64 = 24; // list of uint64_t - - DT_FLOATS16 = 25; // list of float16 - DT_FLOATS32 = 26; // list of float32 - DT_FLOATS64 = 27; // list of float64 - - DT_STRINGS = 28; // list of string - DT_TENSORS = 29; // list of tensor - DT_GRAPHS = 30; // list of graph - - DT_TUPLE = 31; // tuple - DT_LIST = 32; // list - DT_DICT = 33; // dictionary - - // other types - DT_NONE = 34; // None - DT_SYM_INST = 35; // Symbolic Key Instance - - // type related type - DT_BASE_INT = 36; // type generic int - DT_BASE_UINT = 37; // type generate unsigned int - DT_BASE_FLOAT = 38; // type generate float - DT_TYPE = 39; // type type - DT_ANYTHING = 40; // type anything -}; - -enum MSConst { - DEFAULT_REFCOUNT = 0; - WEIGHT_REFCOUNT = 999; -}; - -message TensorDef { - DataType data_type = 1; - - repeated int64 dims = 2; - - string format = 3; - string layout = 4; - uint32 refCount = 5; - uint64 offset = 6; - uint64 size = 7; - uint64 weight_size = 8; - bytes data = 9; -} - -message OpDef { - string name = 1; - string type = 2; - - string fwk_type = 3; - string opAttr = 4; - repeated int64 input_index = 5; - repeated int64 output_index = 6; -} - -message GraphDef { - string name = 1; - - repeated int64 input_index = 2; - - repeated int64 output_index = 3; - uint64 mempool_size = 4; - - repeated OpDef opdefs = 5; - - repeated TensorDef alltensors = 6; -} - - - diff --git a/mindspore/ccsrc/predict/proto/ge_runtime_taskinfo.proto b/mindspore/ccsrc/predict/proto/ge_runtime_taskinfo.proto deleted file mode 100644 index 3429d06544..0000000000 --- a/mindspore/ccsrc/predict/proto/ge_runtime_taskinfo.proto +++ /dev/null @@ -1,155 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto3"; - -package ge.model_runner; -option cc_enable_arenas = true; - -message TaskDef { - enum TaskType { - CCE = 0; - TBE = 1; - AICPU = 2; - LABEL_SET = 3; - LABEL_SWITCH = 4; - LABEL_GOTO = 5; - EVENT_RECORD = 6; - EVENT_WAIT = 7; - FUSION_START = 8; - FUSION_END = 9; - HCCL = 10; - PROFILER_TRACE = 11; - MEMCPY_ASYNC = 12; - STREAM_SWITCH = 13; - STREAM_ACTIVE = 14; - // insert new task type here - REVSERVED = 23; - }; - - TaskType task_type = 1; - uint64 stream_id = 2; - oneof subclass { - CceTaskDef cce_task_def = 3; - TbeTaskDef tbe_task_def = 4; - AicpuTaskDef aicpu_task_def = 5; - LabelTaskDef label_task_def = 6; - EventTaskDef event_task_def = 7; - HcclTaskDef hccl_task_def = 8; - ProfilerTaskDef profiler_task_def = 9; - MemcpyAsyncTaskDef memcpy_async_task_def = 10; - StreamSwitchTaskDef stream_switch_task_def = 11; - StreamActiveTaskDef stream_active_task_def = 12; - } -} - -message CceTaskDef { - KernelContext kernel_context = 1; - string stub_func = 2; - uint32 block_dim = 3; - bytes args = 4; - uint32 args_size = 5; - bytes sm_desc = 6; - bytes flow_table = 7; -} - -message TbeTaskDef { - string stub_func = 1; - uint32 block_dim = 2; - bytes args = 3; - uint32 args_size = 4; - bytes sm_desc = 5; - bytes meta_data = 8; - repeated uint64 input_addrs = 9; - repeated uint64 output_addrs = 10; - repeated uint64 workspace_addrs = 11; -} - -message AicpuTaskDef { - string op_type = 1; - uint32 flag = 2; - repeated uint32 input_types = 3; - repeated Shape input_shapes = 4; - repeated uint64 input_addrs = 5; - repeated uint32 output_types = 6; - repeated Shape output_shapes = 7; - repeated uint64 output_addrs = 8; - bytes node_def = 9; - bytes func_def = 10; -} - -message Shape { - repeated uint32 shape = 1; -} - -message LabelTaskDef { - uint32 label_id = 1; -} - -message EventTaskDef { - uint32 event_id = 1; -} - -message HcclTaskDef { - string hccl_type = 1; - uint64 input_addr = 2; - uint64 output_addr = 3; - bytes workspace = 4; - int64 workspace_num = 5; - bytes private_def = 6; - uint64 ops_kernel_store = 7; - int32 count = 8; - int64 root_id = 9; - int64 op_type = 10; - int64 data_type = 11; -} - -message ProfilerTaskDef { - uint64 log_id = 1; - bool notify = 2; - uint32 flat = 3; -} - -message MemcpyAsyncTaskDef { - uint64 dst = 1; - uint64 dst_max = 2; - uint64 src = 3; - uint64 count = 4; - uint32 kind = 5; -} - -message StreamSwitchTaskDef { - uint32 true_stream_id = 1; - uint64 input_addr = 2; - uint64 value_addr = 3; - int64 cond = 4; - int64 data_type = 5; -} - -message StreamActiveTaskDef { - uint32 active_stream_id = 1; -} - -message KernelContext { - uint32 kernel_type = 1; - uint32 op_id = 2; - uint32 kernel_func_id = 3; - uint32 op_index = 4; - bool is_flowtable = 5; - bytes args_offset = 6; - uint32 args_count = 7; - repeated uint32 origin_op_index = 8; -} diff --git a/mindspore/ccsrc/predict/readme.txt b/mindspore/ccsrc/predict/readme.txt deleted file mode 100644 index d75abf257b..0000000000 --- a/mindspore/ccsrc/predict/readme.txt +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -this is a dictory for predict including saving model &&& saving taskinfos. diff --git a/mindspore/ccsrc/predict/schema/inner/readme.txt b/mindspore/ccsrc/predict/schema/inner/readme.txt deleted file mode 100644 index 774f71f602..0000000000 --- a/mindspore/ccsrc/predict/schema/inner/readme.txt +++ /dev/null @@ -1 +0,0 @@ -this is a dictory for predict to gen fbs headers \ No newline at end of file diff --git a/mindspore/ccsrc/predict/schema/ms.fbs b/mindspore/ccsrc/predict/schema/ms.fbs deleted file mode 100644 index 7c3dcfb498..0000000000 --- a/mindspore/ccsrc/predict/schema/ms.fbs +++ /dev/null @@ -1,212 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -include "op.fbs"; - -namespace mindspore.predict; - -enum MSCONST: int { - WEIGHT_REFCOUNT = 999 -} - -table QuantParam { - scale: double; - zeroPoint: int; - min: double = 0; - max: double = 0; - narrowRange: bool = true; - numBits: int = 8; -} - -table QuantParamArray { - param: [QuantParam]; //pre-channel -} - -table TensorDef { - // data type - dataType: DataType; - // shape - dims: [int]; - format: Format; - refCount: int; - offset: int; - data: [ubyte]; -} - -union OpT { - Concat, - SoftMax, - Activation, - Conv2D, - FusedBatchNorm, - CaffeBatchNorm, - BiasAdd, - Pooling, - DepthwiseConv2D, - DeDepthwiseConv2D, - Resize, - DetectionPostProcess, - FullConnection, - Mean, - DeConv2D, - Scale, - Reshape, - Eltwise, - NetOutput, - Add, - Sub, - MatMul, - StridedSlice, - Power, - Slice, - Stack, - Mul, - RealDiv, - Pad, - Maximum, - Minimum, - CaffePReLU, - LeakyReLU, - ArgMax, - ArgMin, - Exp, - CaffeCrop, - Range, - Rsqrt, - ExpandDims, - Tile, - Cast, - Shape, - Nchw2Nhwc, - Nhwc2Nchw, - QuantDTypeCast, - Split, - Permute, - FakeQuantWithMinMaxVars, - Equal, - Less, - Greater, - Min, - Floor, - Abs, - Neg, - Cos, - Sin, - Sqrt, - Square, - Constant, - Log, - Tan, - Atan, - Asin, - Clip, - Transpose, - Squeeze, - Unsqueeze, - Upsample, - Dropout, - Broadcast, - Lrn, - Prelu, - ZerosLike, - TopK, - SpaceToDepth, - SpaceToBatch, - SparseToDense, - ReverseSequence, - Rank, - Gather, - GatherNd, - Fill, - Elu, - DepthToSpace, - BatchToSpace, - AddN, - Ceil, - EmbeddingLookup, - EmbeddingLookupSparse, - FloorDiv, - FloorMod, - L2Norm, - LocalResponseNormalization, - MatrixDiag, - Reduce, - Reverse, - Round, - Select, - Scatter, - Unique, - Unstack, - LogicalAnd, - LogicalOr, - LogicalXor, - LogicalNot, - OnnxInt8Quantize, - OnnxInt8Dequantize, - FakeQuantWithMinMax, - FakeQuantWithMinMaxPerChannel, - BatchNormFold, - MulFold, - AddFold, - SquaredDifference -} - -enum QuantType: int { - QUANT_NONE, - AwareTrainning, - WeightQuant, - PostTraining -} - -enum FmkType: int { - TF, - CAFFE, - ONNX, - MS, - TFLITE -} - -table OpDef { - name: string; - fmkType: FmkType; - attr: OpT; - inputIndex: [uint]; - outputIndex: [uint]; - quantType: QuantType = QUANT_NONE; - quantParam: [QuantParamArray]; -} - -table SubGraphDef { - name: string; - inputIndex: [uint]; - outputIndex: [uint]; - mempoolSize: uint; - nodes: [OpDef]; - allTensors: [TensorDef]; // weight + input + output -} - -table MempoolCfg { - size: uint; - shiftFactor: uint; -} - -table GraphDef { - name: string; - mempoolCfg: MempoolCfg; - subgraphs: [SubGraphDef]; -} - -root_type GraphDef; diff --git a/mindspore/ccsrc/predict/schema/op.fbs b/mindspore/ccsrc/predict/schema/op.fbs deleted file mode 100644 index 9286c2b2d3..0000000000 --- a/mindspore/ccsrc/predict/schema/op.fbs +++ /dev/null @@ -1,699 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -namespace mindspore.predict; - -enum ResizeMethod: byte { - UNKNOW = -1, - BILINEAR = 0, - NEAREST_NEIGHBOR = 1 -} - -enum DataType : int { - DT_FLOAT = 0, - DT_FLOAT16 = 1, - DT_INT8 = 2, - DT_INT32 = 3, - DT_UINT8 = 4, - DT_INT16 = 5, - DT_UINT32 = 8, - DT_INT64 = 9, - DT_UINT16 = 10, - DT_UNDEFINED = 16 -} - -enum Format : int { - NCHW = 0, - NHWC, - HWKC, - HWCK, - KCHW, - CKHW, - KHWC, - CHWK, - NC4HW4 = 100, - NUM_OF_FORMAT -} - -enum ActivationType : byte { - NO_ACTIVATION = 0, - RELU = 1, - SIGMOID = 2, - RELU6 = 3, - ELU = 4, - LEAKY_RELU = 5, - ABS = 6, - RELU1 = 7, - SOFTSIGN = 8, - SOFTPLUS = 9, - TANH = 10, - SELU = 11, - HSWISH = 12, - HSIGMOID = 13, - THRESHOLDRELU = 14, - LINEAR = 15, - UNKNOW = 16 -} - -enum ReduceType : byte { - REDUCE_MAX = 0, - REDUCE_MEAN = 1, - REDUCE_ALL = 2, - REDUCE_ANY = 3, - REDUCE_LOG_SUM_EXP = 4, - REDUCE_PROD = 5, - REDUCE_SUM = 6, - UNKNOW = 7 -} - -enum PoolMode : byte { - MAX_POOLING = 0, - MEAN_POOLING = 1, -} - -enum EltwiseMode : byte { - PROD = 0, - SUM = 1, - MAXIMUM = 2, - UNKNOW = 3 -} - -enum PadMode : byte { - NOTSET = 0, - SAME = 1, - VALID = 2, - CAFFE = 4 -} - -enum RoundMode : byte { - FLOOR = 0, - CEIL = 1 -} - -enum PaddingMode : byte { - CONSTANT = 0, - REFLECT = 1, - SYMMETRIC = 2, - MODE_RESERVED = 3 -} - -table Pad { - paddingmode: PaddingMode; - paddings: [int]; -} - -table Maximum { -} - -table Minimum { -} - -table Concat { - axis: int; - n: int; -} - -table SoftMax { - axis: [int]; -} - -table Activation { - type: ActivationType = 0; -} - -table Conv2D { - format: Format = 0; - group: int; - channelIn: int; - channelOut: int; - kernelW: int; - kernelH: int; - strideW: int; - strideH: int; - padMode: PadMode; - padUp: int; - padDown: int; - padLeft: int; - padRight: int; - dilateW: int; - dilateH: int; - hasBias: bool = false; - activationType: ActivationType = 0; -} - -table FusedBatchNorm { - epsilon: float = 0.00001; // eg. epsilon=0.001 - momentum: float = 0.9; - spatial: int = 1; -} - -table CaffeBatchNorm { - epsilon: float; // eg. epsilon=0.001 -} - -table Shape { -} - -table Nchw2Nhwc { - -} - -table Nhwc2Nchw { - -} - -table FakeQuantWithMinMaxVars { - narrowRange: bool; - numBits: int; -} - -table BiasAdd { - axis: [int]; -} - -table Pooling { - format: Format = 0; - poolingMode: PoolMode; - global: bool = false; - windowW: int; - windowH: int; - strideW: int; - strideH: int; - padMode: PadMode; - padUp: int; - padDown: int; - padLeft: int; - padRight: int; - roundMode: RoundMode; -} - -table DepthwiseConv2D { - format: Format = 0; - channelIn: int; - channelMultiplier: int; - kernelW: int; - kernelH: int; - strideW: int; - strideH: int; - padMode: PadMode; - padUp: int; - padDown: int; - padLeft: int; - padRight: int; - dilateW: int; - dilateH: int; - hasBias: bool = false; - activationType: ActivationType = 0; -} - -table DeDepthwiseConv2D { - format: Format = 0; - channelIn: int; - channelMultiplier: int; - kernelW: int; - kernelH: int; - strideW: int; - strideH: int; - padMode: PadMode; - padUp: int; - padDown: int; - padLeft: int; - padRight: int; - dilateW: int; - dilateH: int; - hasBias: bool = false; - activationType: ActivationType = 0; -} - - -table Resize { - format: Format = 0; - method: ResizeMethod; - newHeight: long; - newWidth: long; - alignCorners: bool = false; - preserveAspectRatio: bool = false; -} - -table DetectionPostProcess { - format: Format = 0; - inputSize: int; - hScale: float; - wScale: float; - xScale: float; - yScale: float; - NmsIouThreshold: float; - NmsScoreThreshold: float; - MaxDetections: long; - DetectionsPreClass: long; - MaxClassesPreDetection: long; - NumClasses: long; - UseRegularNms: bool; -} - -table FullConnection { - hasBias: bool; - axis: int; -} - -// Mean(input_tensor, axis, keep_dims) -table Mean { - axis: [int]; - keepDims: bool = false; -} - -table DeConv2D { - format: Format = 0; - group: int; - channelIn: int; - channelOut: int; - kernelW: int; - kernelH: int; - strideW: int; - strideH: int; - padMode: PadMode; - padUp: int; - padDown: int; - padLeft: int; - padRight: int; - dilateW: int; - dilateH: int; - hasBias: bool = false; - activationType: ActivationType = 0; -} - -table Scale { - format: Format = 0; -} - -table Eltwise { - mode: EltwiseMode; -} - -table Add { -} - -table Sub { -} - -table Mul { -} - -table RealDiv { -} - -table Rsqrt { -} - -table Equal { -} - -table Less { -} - -table Greater { -} - -table Min { -} - -table Slice { - format: Format = 0; - begin: [int]; - size: [int]; -} - -table Floor { -} - -table Abs { -} - -table Neg { -} - -table Exp { -} - -table Cos { -} - -table Sin { -} - -table Sqrt { -} - -table Square { -} - -table Ceil { -} - -table Log { -} - -table Tan { -} - -table Atan { -} - -table Asin { -} - -table Reshape { - format: Format = 0; - shape: [long]; -} - -table Power { - power: float; - scale: float; - shift: float; -} - -table ArgMax { - axis: int; - outMaxValue: bool; - topK: int = 1; - keepDims: bool; - axisType: int; -} - -table ArgMin { - axis: int; - outMaxValue: bool; - topK: int = 1; - keepDims: bool; - axisType: int; -} - -table NetOutput { -} - -table MatMul { - transposeA : bool = false; - transposeB : bool = false; -} - -table CaffePReLU { - channelShared : bool = false; -} - -table LeakyReLU { - negativeSlope: float; -} - -table StridedSlice { - beginMask: int; - endMask: int; - ellipsisMask: int; - newAxisMask: int; - shrinkAxisMask: int; - begin: [int]; - end: [int]; - stride: [int]; - isScale: [int]; -} - -table Stack { - axis: int; - n: int; - isScale: [int]; -} - -table Range { - dType: DataType; - start: int; - limit: int; - delta: int; -} - -table ExpandDims { - dim: int; -} - -table Tile { - multiples: [int]; -} - -table Cast { - srcT: int; - dstT: int; -} - -table QuantDTypeCast { - srcT: DataType; - dstT: DataType; -} - -table Split { - numberSplit: int; - sizeSplits: [int]; - splitDim: int; -} - -table CaffeCrop { - axis : long; - offsets : [long]; -} - -table Permute { - order: [long]; -} - -table Clip { - max: float; - min: float; -} - -table Constant { -} - - -table Elu { - alpha: float = 1.0; -} - -table Broadcast { -} - -table Lrn { - alpha: float = 0.0001; - beta: float = 0.75; - bias: float = 1.0; - size: int; -} - -enum ReduceMode : byte { - ReduceMean = 0, - ReduceMax = 1, - ReduceMin = 2, - ReduceProd = 3, - ReduceSum = 4, - ReduceSumSquare = 5 -} - -table Reduce { - axes: [int]; - keepDims: int; - mode: ReduceMode; -} - -table Prelu { - slope: [float]; -} - -table Transpose { - perm: [int]; - conjugate: bool = false; -} - -table Squeeze { - axis: [int]; -} - -table Unsqueeze { - axis: [int]; -} - -table Upsample { - mode: string; - scales: [float]; -} - -table Dropout { - ratio : float = 0.5; -} - -table LocalResponseNormalization { - depth_radius: int; - bias: float; - alpha: float; - beta: float; -} - -table ZerosLike { -} - -table TopK { - k : int; - sorted : bool = true; -} - -table SpaceToDepth { - blockSize : int; - format: Format = 0; -} - -table SpaceToBatch { - blockShape : [int]; - paddings : [int]; -} - -table SparseToDense { - validateIndices: bool; -} - -table ReverseSequence { - seqAxis: int; - batchAxis: int; -} - -table Rank { -} - - -table Gather { - axis: int; - batchDims: int; -} - -table GatherNd { - batchDims: int; -} - -table Fill { - dims: [int]; -} - -table DepthToSpace { - blockSize: int; - format: Format = 0; -} - - -table BatchToSpace { - blockShape: [int]; - crops: [int]; -} - -table AddN { - N: int; -} - - -table EmbeddingLookup { - ids: [int]; - maxNorm: float; -} - -table EmbeddingLookupSparse { - spIds: [int]; - spWeights: [float]; - //combiner: Combiner=0; - maxNortm: float; -} - -table FloorDiv { -} - -table FloorMod { -} - -table L2Norm { - axis: [int]; - epsilon: float; -} - -table LogicalAnd { -} - -table LogicalOr { -} - -table LogicalXor { -} - -table LogicalNot { -} - -table MatrixDiag { - k: int; - numRows: int; - numCols: int; - paddingValue: float; -} - -table Select { -} - -table TfReduce { - type: ReduceType = 7; -} - -table Reverse { - axis: [int]; -} - -table Round { -} - -table Scatter { -} - -table Unique { -} - -table Unstack { - num: int; - axis: int; -} - -table OnnxInt8Quantize { -} - -table OnnxInt8Dequantize { -} - -table FakeQuantWithMinMax { -} - -table FakeQuantWithMinMaxPerChannel { -} - -table BatchNormFold { -} - -table MulFold { -} - -table AddFold { -} - -table SquaredDifference { -} diff --git a/mindspore/ccsrc/runtime/device/ascend/ascend_stream_assign.cc b/mindspore/ccsrc/runtime/device/ascend/ascend_stream_assign.cc index 3835dda002..9a3327a5f0 100644 --- a/mindspore/ccsrc/runtime/device/ascend/ascend_stream_assign.cc +++ b/mindspore/ccsrc/runtime/device/ascend/ascend_stream_assign.cc @@ -24,7 +24,6 @@ #include "common/utils.h" #include "backend/session/anf_runtime_algorithm.h" #include "runtime/device/kernel_adjust.h" -#include "predict/generator/utils/ir_model_util.h" #include "backend/optimizer/common/helper.h" #include "utils/utils.h" @@ -53,13 +52,6 @@ void AscendStreamAssign::AssignStream(const NotNull &graph_ptr) GetStreamRelations(); PrintStreamGroups(); FindEventRelations(graph_ptr); - - // Get info for D Model - AscendResourceMng &resource_manager = AscendResourceMng::GetInstance(); - generator::IRModelUtil::GetInstance().set_event_num(resource_manager.get_cur_event_num()); - generator::IRModelUtil::GetInstance().set_stream_num(resource_manager.get_cur_stream_num()); - // Init to 1,temporarily - generator::IRModelUtil::GetInstance().set_batch_num(1); } } diff --git a/mindspore/ccsrc/runtime/device/kernel_runtime.h b/mindspore/ccsrc/runtime/device/kernel_runtime.h index b613ee277a..419c77e3f8 100644 --- a/mindspore/ccsrc/runtime/device/kernel_runtime.h +++ b/mindspore/ccsrc/runtime/device/kernel_runtime.h @@ -24,7 +24,6 @@ #include "runtime/device/device_address.h" #include "ir/tensor.h" #include "utils/convert_utils.h" -#include "predict/generator/utils/ir_model_util.h" #ifdef ENABLE_DUMP_E2E #include "debug/e2e_dump.h" #endif diff --git a/mindspore/ccsrc/utils/context/ms_context.cc b/mindspore/ccsrc/utils/context/ms_context.cc index 404c50f796..b3320b0360 100644 --- a/mindspore/ccsrc/utils/context/ms_context.cc +++ b/mindspore/ccsrc/utils/context/ms_context.cc @@ -50,8 +50,6 @@ std::map MsContext::policy_map_ = {{"ge", kMsBacke MsContext::MsContext(const std::string &policy, const std::string &target) { save_graphs_flag_ = false; save_graphs_path_ = "."; - save_ms_model_flag_ = false; - save_ms_model_path_ = "./model.ms"; enable_dump_ = false; save_dump_path_ = "."; tsd_ref_ = 0; diff --git a/mindspore/ccsrc/utils/context/ms_context.h b/mindspore/ccsrc/utils/context/ms_context.h index 19205cccb8..489277f06e 100644 --- a/mindspore/ccsrc/utils/context/ms_context.h +++ b/mindspore/ccsrc/utils/context/ms_context.h @@ -102,12 +102,6 @@ class MsContext { void set_enable_mem_reuse(bool enable_mem_reuse) { enable_mem_reuse_ = enable_mem_reuse; } bool enable_mem_reuse() const { return enable_mem_reuse_; } - bool save_ms_model_flag() const { return save_ms_model_flag_; } - void set_save_ms_model_flag(bool save_ms_model_flag) { save_ms_model_flag_ = save_ms_model_flag; } - - std::string save_ms_model_path() const { return save_ms_model_path_; } - void set_save_ms_model_path(const std::string &save_ms_model_path) { save_ms_model_path_ = save_ms_model_path; } - void set_enable_gpu_summary(bool enable_gpu_summary) { enable_gpu_summary_ = enable_gpu_summary; } bool enable_gpu_summary() const { return enable_gpu_summary_; } @@ -190,8 +184,6 @@ class MsContext { bool enable_reduce_precision_; bool enable_loop_sink_; bool enable_mem_reuse_; - std::string save_ms_model_path_; - bool save_ms_model_flag_; bool enable_gpu_summary_; bool enable_dump_; std::string save_dump_path_; diff --git a/mindspore/context.py b/mindspore/context.py index 551ec7b79a..eecdc291bf 100644 --- a/mindspore/context.py +++ b/mindspore/context.py @@ -234,22 +234,6 @@ class _Context: if not success: raise RuntimeError("Device id set failed!!!") - @property - def save_ms_model(self): - return self._context_handle.get_save_ms_model_flag() - - @save_ms_model.setter - def save_ms_model(self, save_ms_model_flag): - self._context_handle.set_save_ms_model_flag(save_ms_model_flag) - - @property - def save_ms_model_path(self): - return self._context_handle.get_save_ms_model_path() - - @save_ms_model_path.setter - def save_ms_model_path(self, save_ms_model_path): - self._context_handle.set_save_ms_model_path(save_ms_model_path) - @property def enable_auto_mixed_precision(self): return self._context_handle.get_auto_mixed_precision_flag() @@ -541,7 +525,7 @@ def reset_auto_parallel_context(): @args_type_check(mode=int, precompile_only=bool, device_target=str, device_id=int, save_graphs=bool, - save_graphs_path=str, save_ms_model=bool, save_ms_model_path=str, enable_dump=bool, + save_graphs_path=str, enable_dump=bool, save_dump_path=str, enable_reduce_precision=bool, variable_memory_max_size=str, enable_profiling=bool, profiling_options=str, enable_auto_mixed_precision=bool, enable_graph_kernel=bool, check_bprop=bool, max_device_memory=str, print_file_path=str, @@ -569,8 +553,6 @@ def set_context(**kwargs): device_id (int): Id of target device, the value must be in [0, device_num_per_host-1], while device_num_per_host should no more than 4096. Default: 0. save_graphs (bool): Whether to save graphs. Default: False. - save_ms_model (bool): Whether to save lite model converted by graph. Default: False. - save_ms_model_path (str): Path to save converted lite model. Default: "." save_graphs_path (str): Path to save graphs. Default: "." enable_auto_mixed_precision (bool): Whether to enable auto mixed precision. Default: True. enable_graph_kernel (bool): Whether to enable composition of basic primitives. These primitives would be @@ -615,7 +597,6 @@ def set_context(**kwargs): >>> context.set_context(device_id=0) >>> context.set_context(save_graphs=True, save_graphs_path="./model.ms") >>> context.set_context(enable_reduce_precision=True) - >>> context.set_context(save_ms_model=True, save_ms_model_path=".") >>> context.set_context(enable_dump=True, save_dump_path=".") >>> context.set_context(reserve_class_name_in_scope=True) >>> context.set_context(variable_memory_max_size="6GB") diff --git a/mindspore/train/serialization.py b/mindspore/train/serialization.py index fdb6fecb77..8b54002669 100644 --- a/mindspore/train/serialization.py +++ b/mindspore/train/serialization.py @@ -20,7 +20,6 @@ from threading import Thread, Lock import numpy as np import mindspore.nn as nn -import mindspore.context as context from mindspore import log as logger from mindspore.train.checkpoint_pb2 import Checkpoint from mindspore.train.print_pb2 import Print @@ -457,18 +456,17 @@ def export(net, *inputs, file_name, file_format='GEIR'): net (Cell): MindSpore network. inputs (Tensor): Inputs of the `net`. file_name (str): File name of model to export. - file_format (str): MindSpore currently supports 'GEIR', 'ONNX' 'LITE' and 'BINARY' format for exported model. + file_format (str): MindSpore currently supports 'GEIR', 'ONNX' and 'BINARY' format for exported model. - GEIR: Graph Engine Intermidiate Representation. An intermidiate representation format of Ascend model. - ONNX: Open Neural Network eXchange. An open format built to represent machine learning models. - - LITE: Huawei model format for mobile. A lite model only for the MindSpore Lite - BINARY: Binary format for model. An intermidiate representation format for models. """ logger.info("exporting model file:%s format:%s.", file_name, file_format) check_input_data(*inputs, data_class=Tensor) - supported_formats = ['GEIR', 'ONNX', 'LITE', 'BINARY'] + supported_formats = ['GEIR', 'ONNX', 'BINARY'] if file_format not in supported_formats: raise ValueError(f'Illegal file format {file_format}, it must be one of {supported_formats}') # switch network mode to infer when it is training @@ -497,9 +495,6 @@ def export(net, *inputs, file_name, file_format='GEIR'): with open(file_name, 'wb') as f: os.chmod(file_name, stat.S_IWUSR | stat.S_IRUSR) f.write(onnx_stream) - elif file_format == 'LITE': # file_format is 'LITE' - context.set_context(save_ms_model=True, save_ms_model_path=file_name) - net(*inputs) # restore network training mode if is_training: net.set_train(mode=True) diff --git a/predict/.gitignore b/predict/.gitignore deleted file mode 100644 index caf7aec495..0000000000 --- a/predict/.gitignore +++ /dev/null @@ -1,14 +0,0 @@ -# git ignore file for predict - -#flatbuf generated file -schema/*_generated.h -schema/inner/*_generated.h -module/tvm_module/lite/include/*_generated.h - -#tvm fbs files -module/tvm_module/lite/tune/convert/*.fbs - -#doTest dir -test/doTest/ - - diff --git a/predict/CMakeLists.txt b/predict/CMakeLists.txt deleted file mode 100755 index 39ca6b27e8..0000000000 --- a/predict/CMakeLists.txt +++ /dev/null @@ -1,79 +0,0 @@ -cmake_minimum_required(VERSION 3.12.1) -project (mindspore-predict) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g") -set(CMAKE_BUILD_TYPE "Release") - -set(CMAKE_CXX_STANDARD 11) -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden") -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -s") - -option(ENABLE_ASAN "Enable Google Sanitizer to find memory bugs" OFF) -option(ENABLE_PREDICT_ARM64 "predict arm64" OFF) -option(ENABLE_PREDICT_ARM32 "predict arm32" OFF) - -set(PREDICT_DIR ${CMAKE_CURRENT_SOURCE_DIR}) -set(PREDICT_BUILD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/build) -set(3RD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../third_party) -set(DOTEST_DIR ${PREDICT_BUILD_DIR}/test/doTest) - -include_directories(${3RD_DIR}) -include_directories(${3RD_DIR}/flatbuffers/include/) -include_directories(${3RD_DIR}/protobuf/build/include/) -include_directories(${3RD_DIR}/googletest/googletest/include/) -include_directories(${3RD_DIR}/googletest/googlemock/include/) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/module/tvm_kernel/lite/include/) -include_directories(${PREDICT_DIR}/module/tvm_kernel/incubator-tvm/3rdparty/dlpack/include) -include_directories(common) - -if(ENABLE_PREDICT_ARM64 OR ENABLE_PREDICT_ARM32) - message("*********************predict compile arm*********************") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DMS_USE_ARM=1") - set(ANDROID_NDK $ENV{ANDROID_NDK}) - if(ANDROID_NDK) - add_subdirectory(${3RD_DIR}/googletest ${CMAKE_BINARY_DIR}/googletest) - link_directories(${PREDICT_BUILD_DIR}/googletest/googlemock/gtest) - - add_subdirectory(${3RD_DIR}/securec ${CMAKE_BINARY_DIR}/securec) - link_directories(${PREDICT_BUILD_DIR}/securec/src) - else() - message(FATAL_ERROR "please set ANDROID_NDK in environment variable for example: export ANDROID_NDK=/root/usr/android-ndk-r16b/") - endif() - - include_directories(${ANDROID_SYSROOT}/usr/include/) - if(${ANDROID_ABI} STREQUAL "armeabi-v7a") - include_directories(${ANDROID_SYSROOT}/usr/include/arm-linux-androideabi) - elseif(${ANDROID_ABI} STREQUAL "arm64-v8a") - include_directories(${ANDROID_SYSROOT}/usr/include/aarch64-linux-android) - else() - include_directories(${ANDROID_SYSROOT}/usr/include/arm-linux-androideabi) - endif() - -else() - # include libsecurec.a x86 - message("*********************predict compile x86*********************") - if(EXISTS "${PREDICT_DIR}/../build/mindspore/securec/src/libsecurec.a") - link_directories(${PREDICT_DIR}/../build/mindspore/securec/src) - else() - include(${PREDICT_DIR}/../cmake/dependency_securec.cmake) - link_directories(${PREDICT_BUILD_DIR}/securec/src) - endif() - - # include libgtest.so x86 - if(EXISTS "${PREDICT_DIR}/../build/googletest/googlemock/gtest/libgtest.so") - link_directories(${PREDICT_DIR}/../build/googletest/googlemock/gtest) - else() - include(${PREDICT_DIR}/../cmake/dependency_gtest.cmake) - link_directories(${PREDICT_BUILD_DIR}/googletest/googlemock/gtest) - endif() -endif() - -if (CODE_COVERAGE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage -O0") -endif() - -add_subdirectory(common) -add_subdirectory(src) -add_subdirectory(benchmark) -add_subdirectory(test) -add_subdirectory(module) diff --git a/predict/benchmark/CMakeLists.txt b/predict/benchmark/CMakeLists.txt deleted file mode 100755 index 22f87d8a97..0000000000 --- a/predict/benchmark/CMakeLists.txt +++ /dev/null @@ -1,38 +0,0 @@ - -cmake_minimum_required(VERSION 3.12) -project(benchmark) - -set(CMAKE_CXX_STANDARD 14) -set(CMAKE_BUILD_TYPE "Debug") - -#include 3rd -include_directories(${3RD_DIR}/protobuf/build/include) -include_directories(${3RD_DIR}/securec/include) -include_directories(${3RD_DIR}/flatbuffers/include) -include_directories(${3RD_DIR}/googletest/googletest/include) -include_directories(${3RD_DIR}/googletest/googlemock/include) -include_directories(${PREDICT_DIR}/module/tvm_kernel/incubator-tvm/3rdparty/dlpack/include) -include_directories(${3RD_DIR}/flatbuffers/include) -include_directories(${3RD_DIR}/securec/include) - -#include ms -include_directories(.) -include_directories(${PREDICT_DIR}) - -set(COMMON_SRC ${PREDICT_DIR}/common/flag_parser.cc - ${PREDICT_DIR}/common/file_utils.cc - ${PREDICT_DIR}/common/func_utils.cc - ${PREDICT_DIR}/common/mslog.cc - ${PREDICT_DIR}/common/utils.cc) - -link_directories(${CMAKE_CURRENT_SOURCE_DIR}/../output/lib/) - -add_executable(benchmark main.cc benchmark.cc ${COMMON_SRC}) - -target_link_libraries(benchmark mspredict libsecurec.a) -add_dependencies(benchmark tvm_kernel) -add_dependencies(benchmark securec) - -add_custom_command(TARGET benchmark POST_BUILD - COMMAND mkdir -pv ${DOTEST_DIR} - COMMAND cp ${PREDICT_BUILD_DIR}/benchmark/benchmark ${DOTEST_DIR}) diff --git a/predict/benchmark/README.md b/predict/benchmark/README.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/predict/benchmark/benchmark.cc b/predict/benchmark/benchmark.cc deleted file mode 100644 index c55d03e450..0000000000 --- a/predict/benchmark/benchmark.cc +++ /dev/null @@ -1,451 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "benchmark/benchmark.h" -#include -#include -#include -#include -#include -#include "include/session.h" - -namespace mindspore { -namespace predict { -STATUS Benchmark::GenerateRandomData(size_t size, void *data) { - MS_ASSERT(data != nullptr); - char *castedData = static_cast(data); - for (size_t i = 0; i < size; i++) { - castedData[i] = static_cast(i); - } - return RET_OK; -} - -STATUS Benchmark::GenerateInputData() { - for (Tensor *tensor : msInputs) { - MS_ASSERT(tensor != nullptr); - auto ret = tensor->MallocData(); - if (ret != RET_OK) { - MS_LOGE("MallocData for inTensor failed %d", ret); - return ret; - } - MS_ASSERT(tensor->GetData() != nullptr); - auto tensorByteSize = tensor->GetDataSize(); - auto status = GenerateRandomData(tensorByteSize, tensor->GetData()); - if (status != RET_OK) { - MS_LOGE("GenerateRandomData for inTensor failed %d", status); - return status; - } - } - return RET_OK; -} - -STATUS Benchmark::LoadInput() { - size_t size = 0; - char *graphBuf = ReadFile(_flags->modelPath.c_str(), &size); - if (graphBuf == nullptr) { - MS_LOGE("Load graph failed, path %s", _flags->modelPath.c_str()); - return RET_ERROR; - } - - this->msInputs = session->GetInput(); - - if (_flags->inDataPath.empty()) { - auto status = GenerateInputData(); - if (status != RET_OK) { - delete graphBuf; - MS_LOGE("Generate input data error %d", status); - return status; - } - } else { - auto status = ReadInputFile(); - if (status != RET_OK) { - delete graphBuf; - MS_LOGE("ReadInputFile error, %d", status); - return status; - } - } - delete graphBuf; - return RET_OK; -} - -STATUS Benchmark::ReadInputFile() { - MS_ASSERT(msInputs.size() <= 1); - if (msInputs.empty()) { - return RET_OK; - } - Tensor *inTensor = msInputs.at(0); - MS_ASSERT(inTensor != nullptr); - - size_t size; - char *binBuf = ReadFile(_flags->inDataPath.c_str(), &size); - if (binBuf == nullptr) { - return RET_ERROR; - } - auto tensorDataSize = inTensor->GetDataSize(); - if (size != tensorDataSize) { - MS_LOGE("Input binary file size error, required: %zu, in fact: %zu", tensorDataSize, size); - delete binBuf; - return RET_ERROR; - } - inTensor->SetData(binBuf); - binBuf = nullptr; - - return RET_OK; -} - -// calibData is FP32 -STATUS Benchmark::ReadCalibData() { - const char *calibDataPath = _flags->calibDataPath.c_str(); - // read calib data - std::ifstream inFile(calibDataPath); - if (!inFile.good()) { - MS_LOGE("file: %s is not exist", calibDataPath); - return RET_PARAM_INVALID; - } - - if (!inFile.is_open()) { - MS_LOGE("file: %s open failed", calibDataPath); - inFile.close(); - return RET_PARAM_INVALID; - } - - std::string line; - MS_LOGI("Start reading calibData file"); - std::string tensorName; - while (!inFile.eof()) { - getline(inFile, line); - std::stringstream stringLine1(line); - size_t dim = 0; - stringLine1 >> tensorName >> dim; - std::vector dims; - size_t shapeSize = 1; - for (size_t i = 0; i < dim; i++) { - size_t tmpDim; - stringLine1 >> tmpDim; - dims.push_back(tmpDim); - shapeSize *= tmpDim; - } - - getline(inFile, line); - std::stringstream stringLine2(line); - std::vector tensorData; - for (size_t i = 0; i < shapeSize; i++) { - float tmpData; - stringLine2 >> tmpData; - tensorData.push_back(tmpData); - } - - std::unique_ptr checkTensor(new CheckTensor(dims, tensorData)); - this->calibData.insert(std::make_pair(tensorName, checkTensor.release())); - } - inFile.close(); - MS_LOGI("Finish reading calibData file"); - return RET_OK; -} - -// tensorData need to be converter first -float Benchmark::CompareData(const std::string &nodeName, std::vector msShape, float *msTensorData) { - auto iter = this->calibData.find(nodeName); - if (iter != this->calibData.end()) { - std::vector castedMSShape; - size_t shapeSize = 1; - for (int64_t dim : msShape) { - castedMSShape.push_back(size_t(dim)); - shapeSize *= dim; - } - - CheckTensor *calibTensor = iter->second; - if (calibTensor->shape != castedMSShape) { - std::ostringstream oss; - oss << "Shape of mslite output("; - for (auto dim : castedMSShape) { - oss << dim << ","; - } - oss << ") and shape source model output("; - for (auto dim : calibTensor->shape) { - oss << dim << ","; - } - oss << ") are different"; - MS_LOGE("%s", oss.str().c_str()); - return -1; - } - - float meanBias = 0; - std::ostringstream outputData; - outputData << "Data of node " << nodeName << " : "; - for (size_t j = 0; j < shapeSize; j++) { - if (j < printNum) { - outputData << msTensorData[j] << " "; - } - if (fabs(calibTensor->data.at(j)) > minFloatThr) { - double bias = fabs(msTensorData[j] - calibTensor->data.at(j)) / fabs(calibTensor->data.at(j)); - meanBias += bias; - } - } - meanBias /= shapeSize; - MS_LOGI("%s", outputData.str().c_str()); - - if (meanBias <= minFloatThr) { - MS_LOGI("Mean bias of node %s : 0%%", nodeName.c_str()); - } else { - MS_LOGI("Mean bias of node %s : %f%%", nodeName.c_str(), meanBias * percentage); - } - return meanBias; - } else { - MS_LOGI("%s is not in Source Model output", nodeName.c_str()); - return -1; - } -} - -STATUS Benchmark::CompareOutput(const std::map> &msOutputs) { - float totalBias = 0; - int totalSize = 0; - bool hasError = false; - for (const auto &msOutput : msOutputs) { - std::string nodeName = msOutput.first; - auto tensors = msOutput.second; - for (auto tensor : tensors) { - MS_ASSERT(tensor->GetData() != nullptr); - float bias = CompareData(nodeName, tensor->GetDims(), static_cast(tensor->GetData())); - if (bias >= 0) { - totalBias += bias; - totalSize++; - } else { - hasError = true; - break; - } - } - } - - if (!hasError) { - float meanBias; - if (totalSize != 0) { - meanBias = totalBias / totalSize * percentage; - } else { - meanBias = 0; - } - - MS_LOGI("Mean bias all node : %f%%", meanBias); - - if (meanBias > 1) { - MS_LOGE("Mean bias of all nodes is too big: %f%%", meanBias); - return RET_ERROR; - } else { - return RET_OK; - } - } else { - MS_LOGE("Error in CompareData"); - return RET_ERROR; - } -} - -STATUS Benchmark::MarkPerformance() { - MS_LOGI("Running warm up loops..."); - for (int i = 0; i < _flags->warmUpLoopCount; i++) { - auto status = session->Run(msInputs); - if (status != RET_OK) { - MS_LOGE("Inference error %d", status); - return status; - } - } - - MS_LOGI("Running benchmark loops..."); - uint64_t timeMin = maxTimeThr; - uint64_t timeMax = 0; - uint64_t timeAvg = 0; - for (int i = 0; i < _flags->loopCount; i++) { - uint64_t start = GetTimeUs(); - auto status = session->Run(msInputs); - if (status != RET_OK) { - MS_LOGE("Inference error %d", status); - return status; - } - - uint64_t end = GetTimeUs(); - uint64_t time = end - start; - timeMin = std::min(timeMin, time); - timeMax = std::max(timeMax, time); - timeAvg += time; - - msOutputs = session->GetAllOutput(); - if (cleanData) { - for (auto &msOutput : msOutputs) { - for (auto &outputTensor : msOutput.second) { - delete outputTensor; - } - } - msOutputs.clear(); - } - } - if (_flags->loopCount > 0) { - timeAvg /= _flags->loopCount; - MS_LOGI("MinRunTime = %f ms, MaxRuntime = %f ms, AvgRunTime = %f ms", timeMin / US2MS, timeMax / US2MS, - timeAvg / US2MS); - } - return RET_OK; -} - -STATUS Benchmark::MarkAccuracy() { - MS_LOGI("MarkAccuracy"); - - auto status = session->Run(msInputs); - if (status != RET_OK) { - MS_LOGE("Inference error %d", status); - return status; - } - msOutputs = session->GetAllOutput(); - - ReadCalibData(); - status = CompareOutput(msOutputs); - if (cleanData) { - for (auto &msOutput : msOutputs) { - for (auto &outputTensor : msOutput.second) { - delete outputTensor; - } - } - msOutputs.clear(); - } - return status; -} - -STATUS Benchmark::CleanData() { - if (cleanData) { - for (auto &msInput : msInputs) { - delete msInput; - } - msInputs.clear(); - for (auto &data : calibData) { - data.second->shape.clear(); - data.second->data.clear(); - delete data.second; - } - calibData.clear(); - } - return RET_OK; -} - -STATUS Benchmark::RunBenchmark() { - // Load graph - std::string comment = modelName; - - MS_LOGI("start reading model file"); - size_t size = 0; - char *graphBuf = ReadFile(_flags->modelPath.c_str(), &size); - if (graphBuf == nullptr) { - MS_LOGE("Load graph failed while running %s", comment.c_str()); - return RET_ERROR; - } - - uint64_t startPrepareTime = GetTimeUs(); - session = CreateSession(graphBuf, size, ctx); - if (session == nullptr) { - delete graphBuf; - MS_LOGE("new session failed while running %s", comment.c_str()); - return RET_ERROR; - } - uint64_t endPrepareTime = GetTimeUs(); - MS_LOGI("PrepareTime = %f ms, ", (endPrepareTime - startPrepareTime) / US2MS); - - // Load input - MS_LOGI("start generate input data"); - auto status = LoadInput(); - if (status != RET_OK) { - delete graphBuf; - MS_LOGE("Generate input data error"); - return status; - } - - if (!_flags->calibDataPath.empty()) { - status = MarkAccuracy(); - if (status != RET_OK) { - delete graphBuf; - MS_LOGE("Run MarkAccuracy error: %d", status); - return status; - } - } else { - status = MarkPerformance(); - if (status != RET_OK) { - delete graphBuf; - MS_LOGE("Run MarkPerformance error: %d", status); - return status; - } - } - - CleanData(); - delete graphBuf; - return RET_OK; -} - -STATUS Benchmark::Init() { - if (this->_flags == nullptr) { - return RET_ERROR; - } - MS_LOGI("ModelPath = %s", this->_flags->modelPath.c_str()); - MS_LOGI("InDataPath = %s", this->_flags->inDataPath.c_str()); - MS_LOGI("TensorDataType = %s", this->_flags->tensorDataTypeIn.c_str()); - MS_LOGI("LoopCount = %d", this->_flags->loopCount); - MS_LOGI("WarmUpLoopCount = %d", this->_flags->warmUpLoopCount); - MS_LOGI("NumThreads = %d", this->_flags->numThreads); - MS_LOGI("calibDataPath = %s", this->_flags->calibDataPath.c_str()); - - this->_flags->inDataType = this->_flags->inDataTypeIn == "img" ? kImage : kBinary; - if (this->_flags->tensorDataTypeIn == "float") { - this->_flags->tensorDataType = DataType_DT_FLOAT; - } - - if (_flags->modelPath.empty()) { - MS_LOGE("modelPath is required"); - return RET_ERROR; - } - - modelName = _flags->modelPath.substr(_flags->modelPath.find_last_of("/") + 1); - - return RET_OK; -} - -int RunBenchmark(int argc, const char **argv) { - BenchmarkFlags flags; - Option err = flags.ParseFlags(argc, argv); - - if (err.IsSome()) { - std::cerr << err.Get() << std::endl; - std::cerr << flags.Usage() << std::endl; - return -1; - } - - if (flags.help) { - std::cerr << flags.Usage() << std::endl; - return 0; - } - - Benchmark mBenchmark(&flags); - auto status = mBenchmark.Init(); - if (status != RET_OK) { - MS_LOGE("Benchmark init Error : %d", status); - return 1; - } - - status = mBenchmark.RunBenchmark(); - if (status != RET_OK) { - MS_LOGE("Run Benchmark Error : %d", status); - return 1; - } - - MS_LOGI("end of benchmark"); - return 0; -} -} // namespace predict -} // namespace mindspore diff --git a/predict/benchmark/benchmark.h b/predict/benchmark/benchmark.h deleted file mode 100644 index 03cd117df0..0000000000 --- a/predict/benchmark/benchmark.h +++ /dev/null @@ -1,142 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PREDICT_BENCHMARK_BENCHMARK_H_ -#define PREDICT_BENCHMARK_BENCHMARK_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common/flag_parser.h" -#include "common/file_utils.h" -#include "common/func_utils.h" -#include "common/mslog.h" -#include "common/utils.h" -#include "include/errorcode.h" -#include "include/session.h" -#include "include/tensor.h" -#include "schema/inner/ms_generated.h" -#include "src/graph.h" -#include "src/graph_execution.h" -#include "src/op.h" - -namespace mindspore { -namespace predict { -enum InDataType { kImage = 0, kBinary = 1 }; - -struct CheckTensor { - CheckTensor(const std::vector &shape, const std::vector &data) { - this->shape = shape; - this->data = data; - } - std::vector shape; - std::vector data; -}; - -class BenchmarkFlags : public virtual FlagParser { - public: - BenchmarkFlags() { - // common - AddFlag(&BenchmarkFlags::modelPath, "modelPath", "Input model path", ""); - AddFlag(&BenchmarkFlags::tensorDataTypeIn, "tensorDataType", "Data type of input Tensor. float", "float"); - AddFlag(&BenchmarkFlags::inDataPath, "inDataPath", "Input data path, if not set, use random input", ""); - // MarkPerformance - AddFlag(&BenchmarkFlags::loopCount, "loopCount", "Run loop count", 10); - AddFlag(&BenchmarkFlags::numThreads, "numThreads", "Run threads number", 2); - AddFlag(&BenchmarkFlags::warmUpLoopCount, "warmUpLoopCount", "Run warm up loop", 3); - // MarkAccuracy - AddFlag(&BenchmarkFlags::calibDataPath, "calibDataPath", "Calibration data file path", ""); - } - - ~BenchmarkFlags() override = default; - - public: - // common - std::string modelPath; - std::string inDataPath; - InDataType inDataType; - std::string inDataTypeIn; - DataType tensorDataType; - std::string tensorDataTypeIn; - // MarkPerformance - int loopCount; - int numThreads; - int warmUpLoopCount; - // MarkAccuracy - std::string calibDataPath; -}; - -class Benchmark { - public: - explicit Benchmark(BenchmarkFlags *flags) : _flags(flags) {} - - virtual ~Benchmark() = default; - - STATUS Init(); - STATUS RunBenchmark(); - - private: - // call GenerateInputData or ReadInputFile to init inputTensors - STATUS LoadInput(); - - // call GenerateRandomData to fill inputTensors - STATUS GenerateInputData(); - - STATUS GenerateRandomData(size_t size, void *data); - - STATUS ReadInputFile(); - - STATUS ReadCalibData(); - - STATUS CleanData(); - - STATUS CompareOutput(const std::map> &msOutputs); - - float CompareData(const std::string &nodeName, std::vector msShape, float *msTensorData); - - STATUS MarkPerformance(); - - STATUS MarkAccuracy(); - - private: - BenchmarkFlags *_flags; - std::shared_ptr session; - Context ctx; - std::vector msInputs; - std::map> msOutputs; - std::unordered_map calibData; - std::string modelName = ""; - bool cleanData = true; - - const float US2MS = 1000.0f; - const float percentage = 100.0f; - const int printNum = 50; - const float minFloatThr = 0.0000001f; - - const uint64_t maxTimeThr = 1000000; -}; - -int RunBenchmark(int argc, const char **argv); -} // namespace predict -} // namespace mindspore -#endif // PREDICT_BENCHMARK_BENCHMARK_H_ diff --git a/predict/benchmark/main.cc b/predict/benchmark/main.cc deleted file mode 100644 index 66e473a42a..0000000000 --- a/predict/benchmark/main.cc +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include "benchmark/benchmark.h" - -int main(int argc, const char **argv) { - signal(SIGSEGV, mindspore::predict::CoreDumpTraceFunc); - return mindspore::predict::RunBenchmark(argc, argv); -} diff --git a/predict/common/CMakeLists.txt b/predict/common/CMakeLists.txt deleted file mode 100755 index 3734c26bc0..0000000000 --- a/predict/common/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -include_directories(${CMAKE_CURRENT_SOURCE_DIR}) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../include) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../third_party) - -add_compile_options(-fPIC) - -add_library(common_mid OBJECT - ${CMAKE_CURRENT_SOURCE_DIR}/common.h - ${CMAKE_CURRENT_SOURCE_DIR}/graph_util.cc - ${CMAKE_CURRENT_SOURCE_DIR}/file_utils.cc - ${CMAKE_CURRENT_SOURCE_DIR}/flag_parser.cc - ${CMAKE_CURRENT_SOURCE_DIR}/func_utils.cc - ${CMAKE_CURRENT_SOURCE_DIR}/module_registry.cc - ${CMAKE_CURRENT_SOURCE_DIR}/mslog.cc - ${CMAKE_CURRENT_SOURCE_DIR}/storage.cc - ${CMAKE_CURRENT_SOURCE_DIR}/utils.cc) diff --git a/predict/common/common.h b/predict/common/common.h deleted file mode 100644 index d93139abae..0000000000 --- a/predict/common/common.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PREDICT_COMMON_COMMON_H_ -#define PREDICT_COMMON_COMMON_H_ - -#include -#include "schema/inner/ms_generated.h" - -namespace mindspore { -namespace predict { -enum NCHW_SHAPE { NCHW_N = 0, NCHW_C = 1, NCHW_H = 2, NCHW_W = 3 }; -enum NHWC_SHAPE { NHWC_N = 0, NHWC_H = 1, NHWC_W = 2, NHWC_C = 3 }; -enum HWCK_SHAPE { HWCK_H = 0, HWCK_W = 1, HWCK_C = 2, HWCK_K = 3 }; -enum KCHW_SHAPE { KCHW_K = 0, KCHW_C = 1, KCHW_H = 2, KCHW_W = 3 }; -enum CHW_SHAPE { CHW_C = 0, CHW_H = 1, CHW_W = 2 }; -enum HWC_SHAPE { HWC_H = 0, HWC_W = 1, HWC_C = 2 }; - -static constexpr int TENSOR_MAX_REFCOUNT = 999; - -static const char *DELIM_COLON = ":"; -static const char *DELIM_COMMA = ","; -static const char *DELIM_SLASH = "/"; -static const char *DELIM_DOUBLE_BACKSLASH = "\\"; - -// quantization relative -static const char QUANTIZED_UINT8[] = "QUANTIZED_UINT8"; -static const char QUANTIZED_INT8[] = "QUANTIZED_INT8"; -static const char QUANTIZED_INT16[] = "QUANTIZED_INT16"; -static const char QUANTIZED_UINT16[] = "QUANTIZED_UINT16"; -static const char QUANTIZED_FLOAT16[] = "FLOAT16"; -static const char QUANTIZED_FLOAT32[] = "FLOAT32"; -static const char QUANTIZATION_TYPE_DYNAMIC[] = "DYNAMIC"; -static const char QUANTIZATION_TYPE_STATIC[] = "STATIC"; -static const char CALIB_NORM[] = "NORM"; - -// dims -static const int32_t DIM_DEFAULT_SIZE = 4; - -static const Format DEFAULT_FORMAT = Format_NCHW; -} // namespace predict -} // namespace mindspore - -#endif // PREDICT_COMMON_COMMON_H_ diff --git a/predict/common/file_utils.cc b/predict/common/file_utils.cc deleted file mode 100644 index 94adf0f7ac..0000000000 --- a/predict/common/file_utils.cc +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "common/file_utils.h" -#include - -namespace mindspore { -namespace predict { -char *ReadFile(const char *file, size_t *size) { - if (file == nullptr) { - MS_LOGE("file is nullptr"); - return nullptr; - } - MS_ASSERT(size != nullptr); - std::ifstream ifs(RealPath(file)); - if (!ifs.good()) { - MS_LOGE("file: %s is not exist", file); - return nullptr; - } - - if (!ifs.is_open()) { - MS_LOGE("file: %s open failed", file); - return nullptr; - } - - ifs.seekg(0, std::ios::end); - *size = ifs.tellg(); - std::unique_ptr buf(new (std::nothrow) char[*size]); - if (buf == nullptr) { - MS_LOGE("malloc buf failed, file:%s", file); - ifs.close(); - return nullptr; - } - - ifs.seekg(0, std::ios::beg); - ifs.read(buf.get(), *size); - ifs.close(); - - return buf.release(); -} - -std::string RealPath(const char *path) { - if (path == nullptr) { - MS_LOGE("path is nullptr"); - return ""; - } - if ((strlen(path)) >= PATH_MAX) { - MS_LOGE("path is too long"); - return ""; - } - - std::shared_ptr resolvedPath(new (std::nothrow) char[PATH_MAX]{0}); - if (resolvedPath == nullptr) { - MS_LOGE("new resolvedPath failed"); - return ""; - } - - auto ret = realpath(path, resolvedPath.get()); - if (ret == nullptr) { - MS_LOGE("realpath failed"); - return ""; - } - return resolvedPath.get(); -} -} // namespace predict -} // namespace mindspore diff --git a/predict/common/file_utils.h b/predict/common/file_utils.h deleted file mode 100644 index e67c1cf9f1..0000000000 --- a/predict/common/file_utils.h +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PREDICT_COMMON_FILE_UTILS_H_ -#define PREDICT_COMMON_FILE_UTILS_H_ - -#include -#include -#include -#include -#include -#include -#include -#include "common/utils.h" -#include "common/mslog.h" -#include "include/tensor.h" - -namespace mindspore { -namespace predict { -char *ReadFile(const char *file, size_t *size); - -std::string RealPath(const char *path); -} // namespace predict -} // namespace mindspore - -#endif // PREDICT_COMMON_FILE_UTILS_H_ diff --git a/predict/common/flag_parser.cc b/predict/common/flag_parser.cc deleted file mode 100644 index 37482dc409..0000000000 --- a/predict/common/flag_parser.cc +++ /dev/null @@ -1,179 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "common/flag_parser.h" - -namespace mindspore { -namespace predict { -// parse flags read from command line -Option FlagParser::ParseFlags(int argc, const char *const *argv, bool supportUnknown, - bool supportDuplicate) { - MS_ASSERT(argv != nullptr); - const int FLAG_PREFIX_LEN = 2; - // Get binary name - binName = GetFileName(argv[0]); - - std::multimap> keyValues; - for (int i = 1; i < argc; i++) { - std::string tmp = argv[i]; - Trim(&tmp); - const std::string flagItem(tmp); - - if (flagItem == "--") { - break; - } - - if (flagItem.find("--") == std::string::npos) { - continue; - } - - std::string key; - Option value = Option(None()); - - size_t pos = flagItem.find_first_of("="); - if (pos == std::string::npos && flagItem.find("--no-") != std::string::npos) { - key = flagItem.substr(FLAG_PREFIX_LEN); - } else if (pos == std::string::npos) { - key = flagItem.substr(FLAG_PREFIX_LEN); - } else { - key = flagItem.substr(FLAG_PREFIX_LEN, pos - FLAG_PREFIX_LEN); - value = Option(flagItem.substr(pos + 1)); - } - - keyValues.insert(std::pair>(key, value)); - } - - Option ret = Option(InnerParseFlags(&keyValues)); - if (ret.IsSome()) { - return Option(ret.Get()); - } - - return Option(None()); -} - -bool FlagParser::GetRealFlagName(const std::string &oriFlagName, std::string *flagName) { - MS_ASSERT(flagName != nullptr); - const int BOOL_TYPE_FLAG_PREFIX_LEN = 3; - bool opaque = false; - if (StartsWithPrefix(oriFlagName, "no-")) { - *flagName = oriFlagName.substr(BOOL_TYPE_FLAG_PREFIX_LEN); - opaque = true; - } else { - *flagName = oriFlagName; - } - return opaque; -} - -// Inner parse function -Option FlagParser::InnerParseFlags(std::multimap> *keyValues) { - MS_ASSERT(keyValues != nullptr); - for (auto it = keyValues->begin(); it != keyValues->end(); ++it) { - std::string flagName; - bool opaque = GetRealFlagName((*it).first, &flagName); - Option flagValue = (*it).second; - - auto item = flags.find(flagName); - if (item == flags.end()) { - return Option(std::string(flagName + " is not a valid flag")); - } - FlagInfo *flag = &(item->second); - if (flag == nullptr) { - return Option("Failed: flag is nullptr"); - } - if (flag->isParsed) { - return Option("Failed: already parsed flag: " + flagName); - } - std::string tmpValue; - if (!flag->isBoolean) { - if (opaque) { - return Option(flagName + " is not a boolean type"); - } - if (flagValue.IsNone()) { - return Option("No value provided for non-boolean type: " + flagName); - } - tmpValue = flagValue.Get(); - } else { - if (flagValue.IsNone() || flagValue.Get().empty()) { - tmpValue = !opaque ? "true" : "false"; - } else if (!opaque) { - tmpValue = flagValue.Get(); - } else { - return Option(std::string("Boolean flag can not have non-empty value")); - } - } - // begin to parse value - Option ret = flag->parse(this, tmpValue); - if (ret.IsNone()) { - return Option("Failed to parse value for: " + flag->flagName); - } - flag->isParsed = true; - } - - // to check flags not given in command line but added as in constructor - for (auto &flag : flags) { - if (flag.second.isRequired && !flag.second.isParsed) { - return Option("Error, value of '" + flag.first + "' not provided"); - } - } - - return Option(None()); -} - -void Replaceall(std::string *str, const std::string &oldValue, const std::string &newValue) { - if (str == nullptr) { - MS_LOGE("Input str is nullptr"); - return; - } - while (true) { - std::string::size_type pos(0); - if ((pos = str->find(oldValue)) != std::string::npos) { - str->replace(pos, oldValue.length(), newValue); - } else { - break; - } - } -} - -std::string FlagParser::Usage(const Option &usgMsg) const { - // first line, brief of the usage - std::string usageString = usgMsg.IsSome() ? usgMsg.Get() + "\n" : ""; - // usage of bin name - usageString += usageMsg.IsNone() ? "usage: " + binName + " [options]\n" : usageMsg.Get() + "\n"; - // help line of help message, usageLine:message of parametors - std::string helpLine = ""; - std::string usageLine = ""; - uint32_t i = 0; - for (auto flag = flags.begin(); flag != flags.end(); flag++) { - std::string flagName = flag->second.flagName; - std::string helpInfo = flag->second.helpInfo; - // parameter line - std::string thisLine = flag->second.isBoolean ? " --[no-]" + flagName : " --" + flagName + "=VALUE"; - if (++i < flags.size()) { - // add paramter help message of each line - thisLine += " " + helpInfo; - Replaceall(&helpInfo, "\n\r", "\n"); - usageLine += thisLine + "\n"; - } else { - // brief help message - helpLine = thisLine + " " + helpInfo + "\n"; - } - } - // total usage is brief of usage+ brief of bin + help message + brief of - // paramters - return usageString + helpLine + usageLine; -} -} // namespace predict -} // namespace mindspore diff --git a/predict/common/flag_parser.h b/predict/common/flag_parser.h deleted file mode 100644 index f01b8df71e..0000000000 --- a/predict/common/flag_parser.h +++ /dev/null @@ -1,291 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PREDICT_COMMON_FLAG_PARSER_H_ -#define PREDICT_COMMON_FLAG_PARSER_H_ - -#include -#include -#include -#include - -#include "common/utils.h" -#include "common/option.h" - -namespace mindspore { -namespace predict { -struct FlagInfo; - -struct Nothing {}; - -class FlagParser { - public: - FlagParser() { AddFlag(&FlagParser::help, "help", "print usage message", false); } - - virtual ~FlagParser() = default; - - // only support read flags from command line - virtual Option ParseFlags(int argc, const char *const *argv, bool supportUnknown = false, - bool supportDuplicate = false); - std::string Usage(const Option &usgMsg = Option(None())) const; - - template - void AddFlag(T1 *t1, const std::string &flagName, const std::string &helpInfo, const T2 &t2); - - template - void AddFlag(T1 Flags::*t1, const std::string &flagName, const std::string &helpInfo, const T2 &t2); - - template - void AddFlag(T Flags::*t, const std::string &flagName, const std::string &helpInfo); - - // Option-type fields - template - void AddFlag(Option Flags::*t, const std::string &flagName, const std::string &helpInfo); - bool help; - - protected: - std::string binName; - Option usageMsg; - - private: - struct FlagInfo { - std::string flagName; - bool isRequired; - bool isBoolean; - std::string helpInfo; - bool isParsed; - std::function(FlagParser *, const std::string &)> parse; - }; - - inline void AddFlag(const FlagInfo &flag); - - // construct a temporary flag - template - void ConstructFlag(Option Flags::*t, const std::string &flagName, const std::string &helpInfo, FlagInfo *flag); - - // construct a temporary flag - template - void ConstructFlag(T1 Flags::*t1, const std::string &flagName, const std::string &helpInfo, FlagInfo *flag); - - Option InnerParseFlags(std::multimap> *values); - - bool GetRealFlagName(const std::string &oriFlagName, std::string *flagName); - - std::map flags; -}; - -// convert to std::string -template -Option ConvertToString(T Flags::*t, const FlagParser &baseFlag) { - const Flags *flag = dynamic_cast(&baseFlag); - if (flag != nullptr) { - return std::to_string(flag->*t); - } - - return Option(None()); -} - -// construct for a Option-type flag -template -void FlagParser::ConstructFlag(Option Flags::*t1, const std::string &flagName, const std::string &helpInfo, - FlagInfo *flag) { - if (flag == nullptr) { - MS_LOGE("FlagInfo is nullptr"); - return; - } - flag->flagName = flagName; - flag->helpInfo = helpInfo; - flag->isBoolean = typeid(T) == typeid(bool); - flag->isParsed = false; -} - -// construct a temporary flag -template -void FlagParser::ConstructFlag(T Flags::*t1, const std::string &flagName, const std::string &helpInfo, FlagInfo *flag) { - if (flag == nullptr) { - MS_LOGE("FlagInfo is nullptr"); - return; - } - if (t1 == nullptr) { - MS_LOGE("t1 is nullptr"); - return; - } - flag->flagName = flagName; - flag->helpInfo = helpInfo; - flag->isBoolean = typeid(T) == typeid(bool); - flag->isParsed = false; -} - -inline void FlagParser::AddFlag(const FlagInfo &flagItem) { flags[flagItem.flagName] = flagItem; } - -template -void FlagParser::AddFlag(T Flags::*t, const std::string &flagName, const std::string &helpInfo) { - if (t == nullptr) { - MS_LOGE("t1 is nullptr"); - return; - } - - Flags *flag = dynamic_cast(this); - if (flag == nullptr) { - MS_LOGI("dynamic_cast failed"); - return; - } - - FlagInfo flagItem; - - // flagItem is as a output parameter - ConstructFlag(t, flagName, helpInfo, &flagItem); - flagItem.parse = [t](FlagParser *base, const std::string &value) -> Option { - Flags *flag = dynamic_cast(base); - if (base != nullptr) { - Option ret = Option(GenericParseValue(value)); - if (ret.IsNone()) { - return Option(None()); - } else { - flag->*t = ret.Get(); - } - } - - return Option(Nothing()); - }; - - flagItem.isRequired = true; - flagItem.helpInfo += - !helpInfo.empty() && helpInfo.find_last_of("\n\r") != helpInfo.size() - 1 ? " (default: " : "(default: "; - flagItem.helpInfo += ")"; - - // add this flag to a std::map - AddFlag(flagItem); -} - -template -void FlagParser::AddFlag(T1 *t1, const std::string &flagName, const std::string &helpInfo, const T2 &t2) { - if (t1 == nullptr) { - MS_LOGE("t1 is nullptr"); - return; - } - - FlagInfo flagItem; - - // flagItem is as a output parameter - ConstructFlag(t1, flagName, helpInfo, flagItem); - flagItem.parse = [t1](FlagParser *base, const std::string &value) -> Option { - if (base != nullptr) { - Option ret = Option(GenericParseValue(value)); - if (ret.IsNone()) { - return Option(None()); - } else { - *t1 = ret.Get(); - } - } - - return Option(Nothing()); - }; - - flagItem.isRequired = false; - *t1 = t2; - - flagItem.helpInfo += - !helpInfo.empty() && helpInfo.find_last_of("\n\r") != helpInfo.size() - 1 ? " (default: " : "(default: "; - flagItem.helpInfo += ToString(t2).Get(); - flagItem.helpInfo += ")"; - - // add this flag to a std::map - AddFlag(flagItem); -} - -template -void FlagParser::AddFlag(T1 Flags::*t1, const std::string &flagName, const std::string &helpInfo, const T2 &t2) { - if (t1 == nullptr) { - MS_LOGE("t1 is nullptr"); - return; - } - - Flags *flag = dynamic_cast(this); - if (flag == nullptr) { - MS_LOGI("dynamic_cast failed"); - return; - } - - FlagInfo flagItem; - - // flagItem is as a output parameter - ConstructFlag(t1, flagName, helpInfo, &flagItem); - flagItem.parse = [t1](FlagParser *base, const std::string &value) -> Option { - Flags *flag = dynamic_cast(base); - if (base != nullptr) { - Option ret = Option(GenericParseValue(value)); - if (ret.IsNone()) { - return Option(None()); - } else { - flag->*t1 = ret.Get(); - } - } - - return Option(Nothing()); - }; - - flagItem.isRequired = false; - flag->*t1 = t2; - - flagItem.helpInfo += - !helpInfo.empty() && helpInfo.find_last_of("\n\r") != helpInfo.size() - 1 ? " (default: " : "(default: "; - flagItem.helpInfo += ToString(t2).Get(); - flagItem.helpInfo += ")"; - - // add this flag to a std::map - AddFlag(flagItem); -} - -// option-type add flag -template -void FlagParser::AddFlag(Option Flags::*t, const std::string &flagName, const std::string &helpInfo) { - if (t == nullptr) { - MS_LOGE("t is nullptr"); - return; - } - - Flags *flag = dynamic_cast(this); - if (flag == nullptr) { - MS_LOGE("dynamic_cast failed"); - return; - } - - FlagInfo flagItem; - // flagItem is as a output parameter - ConstructFlag(t, flagName, helpInfo, &flagItem); - flagItem.isRequired = false; - flagItem.parse = [t](FlagParser *base, const std::string &value) -> Option { - Flags *flag = dynamic_cast(base); - if (base != nullptr) { - Option ret = Option(GenericParseValue(value)); - if (ret.IsNone()) { - return Option(None()); - } else { - flag->*t = Option(Some(ret.Get())); - } - } - - return Option(Nothing()); - }; - - // add this flag to a std::map - AddFlag(flagItem); -} -} // namespace predict -} // namespace mindspore - -#endif // PREDICT_COMMON_FLAG_PARSER_H_ diff --git a/predict/common/func_utils.cc b/predict/common/func_utils.cc deleted file mode 100644 index d2aeb8d941..0000000000 --- a/predict/common/func_utils.cc +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "common/func_utils.h" - -namespace mindspore { -namespace predict { -#if MS_USE_ARM -_Unwind_Reason_Code PrintTraceArm(_Unwind_Context *ctx, void *d) { - MS_ASSERT(ctx != nullptr); - MS_ASSERT(d != nullptr); - Dl_info info; - int *depth = static_cast(d); - auto ipAddr = static_cast(_Unwind_GetIP(ctx)); - if (dladdr(reinterpret_cast(ipAddr), &info)) { - const char *symbol = ""; - const char *dlfile = ""; - if (info.dli_sname) { - symbol = info.dli_sname; - } - if (info.dli_fname) { - dlfile = info.dli_fname; - } - MS_PRINT_ERROR("#%d: (%08lx) %s %s ", *depth, ipAddr, dlfile, symbol); - } - - (*depth)++; - return _URC_NO_REASON; -} -#endif - -void CoreDumpTraceFunc(int iSignum) { - MS_PRINT_ERROR("----- start get backtrace info -----"); -#if MS_USE_ARM - int depth = 0; - _Unwind_Backtrace(&PrintTraceArm, &depth); -#else - const auto maxDeep = 32; - const auto maxStringLen = 100; - void *apBuffer[maxStringLen]; - char **ppStrings; - - auto iStackDepth = backtrace(apBuffer, maxDeep); - if (0 > iStackDepth) { - KillProcess("Get backtrace depth failed"); - return; - } - MS_PRINT_ERROR("Current stack depth is %d", iStackDepth); - ppStrings = backtrace_symbols(apBuffer, iStackDepth); - if (nullptr == ppStrings) { - KillProcess("Get backtrace_symbols failed"); - return; - } - - for (int iLoop = 0; iLoop < iStackDepth; iLoop++) { - MS_PRINT_ERROR("%s \n", ppStrings[iLoop]); - } -#endif - MS_PRINT_ERROR("----- finish get backtrace info -----"); - KillProcess("Exit after core dump"); - return; // try exit 1 -} -} // namespace predict -} // namespace mindspore diff --git a/predict/common/func_utils.h b/predict/common/func_utils.h deleted file mode 100644 index da0389a584..0000000000 --- a/predict/common/func_utils.h +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PREDICT_COMMON_FUNC_UTILS_H_ -#define PREDICT_COMMON_FUNC_UTILS_H_ - -#if MS_USE_ARM -#include -#include -#else -#include -#endif -#include "include/errorcode.h" -#include "common/mslog.h" - -namespace mindspore { -namespace predict { -void CoreDumpTraceFunc(int iSignum); -} // namespace predict -} // namespace mindspore - -#endif // PREDICT_COMMON_FUNC_UTILS_H_ diff --git a/predict/common/graph_util.cc b/predict/common/graph_util.cc deleted file mode 100644 index 6394731bc6..0000000000 --- a/predict/common/graph_util.cc +++ /dev/null @@ -1,167 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "common/graph_util.h" -#include -#include -#include "common/mslog.h" -#include "include/errorcode.h" - -namespace mindspore { -namespace predict { -OpGraph *OpGraph::Build(const SubGraphDef &subGraphDef) { - auto graph = std::unique_ptr(new OpGraph()); - if (graph == nullptr) { - MS_LOGE("malloc opgraph failed"); - return nullptr; - } - - auto nodeDefs = subGraphDef.nodes(); - if (nodeDefs == nullptr) { - MS_LOGE("nodeDefs from subGraphDef is nullptr"); - return nullptr; - } - - uint32_t opCount = nodeDefs->size(); - for (uint32_t i = 0; i < opCount; i++) { - auto nodeDef = nodeDefs->GetAs(i); - MS_ASSERT(nodeDef != nullptr); - auto ret = graph->AddEdge(*nodeDef, *nodeDefs); - if (ret != RET_OK) { - MS_LOGE("%s add edge failed. ret:%d", nodeDef->opDef()->name()->c_str(), ret); - return nullptr; - } - } - - return graph.release(); -} - -int OpGraph::AddEdge(const NodeDef &srcNodeDef, const flatbuffers::Vector> &nodeDefs) { - MS_ASSERT(srcNodeDef.opDef() != nullptr); - MS_ASSERT(srcNodeDef.opDef()->name() != nullptr); - NODE_ID srcId = std::string(srcNodeDef.opDef()->name()->c_str()); - uint32_t opCount = nodeDefs.size(); - - MS_ASSERT(srcNodeDef.opDef()->outputIndex() != nullptr); - for (auto index : *(srcNodeDef.opDef()->outputIndex())) { - for (uint32_t i = 0; i < opCount; i++) { - auto dstNodeDef = nodeDefs.GetAs(i); - bool find = false; - MS_ASSERT(dstNodeDef != nullptr); - MS_ASSERT(dstNodeDef->opDef() != nullptr); - auto inputIndex = dstNodeDef->opDef()->inputIndex(); - MS_ASSERT(inputIndex != nullptr); - if (std::any_of(inputIndex->begin(), inputIndex->end(), [&index](int i) { return i == index; })) { - find = true; - } - - if (!find) { - continue; - } - MS_ASSERT(dstNodeDef->opDef()->name() != nullptr); - NODE_ID dstId = std::string(dstNodeDef->opDef()->name()->c_str()); - auto ret = AddEdge(srcId, dstId); - if (ret != RET_OK) { - return ret; - } - } - } - - return RET_OK; -} - -int OpGraph::AddEdge(const NODE_ID &srcId, const NODE_ID &dstId) { - auto srcNode = AddNode(srcId); - if (srcNode == nullptr) { - MS_LOGE("add srcNode failed"); - return RET_ERROR; - } - srcNode->AddOutEdge(dstId); - auto dstNode = AddNode(dstId); - if (dstNode == nullptr) { - MS_LOGE("add dstNode failed"); - return RET_ERROR; - } - dstNode->AddInEdge(srcId); - return RET_OK; -} - -OpNode *OpGraph::GetNode(const NODE_ID &nodeId) { - auto node = nodes.find(nodeId); - if (node == nodes.end()) { - return nullptr; - } - return node->second; -} - -OpNode *OpGraph::AddNode(const NODE_ID &nodeId) { - auto node = GetNode(nodeId); - if (node != nullptr) { - return node; - } - node = new (std::nothrow) OpNode(nodeId); - if (node == nullptr) { - MS_LOGE("new node failed"); - return nullptr; - } - nodes[nodeId] = node; - return node; -} - -std::unordered_set OpGraph::GetInputNode() { - std::unordered_set inputNodes; - for (const auto &iter : nodes) { - auto node = iter.second; - MS_ASSERT(node != nullptr); - if (node->GetAllInEdge().empty()) { - inputNodes.insert(node->ID()); - } - } - return inputNodes; -} - -std::unordered_set OpGraph::GetOutputNode() { - std::unordered_set outputNodes; - for (const auto &iter : nodes) { - auto node = iter.second; - MS_ASSERT(node != nullptr); - if (node->GetAllOutEdge().empty()) { - outputNodes.insert(node->ID()); - } - } - return outputNodes; -} - -OpGraph::~OpGraph() { - for (auto iter : nodes) { - if (iter.second != nullptr) { - delete iter.second; - } - } - nodes.clear(); -} - -NODE_ID OpNode::ID() { return id; } - -void OpNode::AddInEdge(const NODE_ID &nodeId) { inEdges.insert(nodeId); } - -void OpNode::AddOutEdge(const NODE_ID &nodeId) { outEdges.insert(nodeId); } - -std::unordered_set OpNode::GetAllInEdge() { return inEdges; } - -std::unordered_set OpNode::GetAllOutEdge() { return outEdges; } -} // namespace predict -} // namespace mindspore diff --git a/predict/common/graph_util.h b/predict/common/graph_util.h deleted file mode 100644 index 9797edadf6..0000000000 --- a/predict/common/graph_util.h +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PREDICT_COMMON_GRAPH_UTIL_H_ -#define PREDICT_COMMON_GRAPH_UTIL_H_ - -#include -#include -#include -#include -#include -#include -#include "common/utils.h" -#include "schema/inner/ms_generated.h" - -namespace mindspore { -namespace predict { -using NODE_ID = std::string; - -class OpNode { - public: - explicit OpNode(NODE_ID nodeId) : id(std::move(nodeId)) {} - NODE_ID ID(); - void AddInEdge(const NODE_ID &nodeId); - void AddOutEdge(const NODE_ID &nodeId); - std::unordered_set GetAllInEdge(); - std::unordered_set GetAllOutEdge(); - - protected: - NODE_ID id; - std::unordered_set inEdges; - std::unordered_set outEdges; -}; - -class OpGraph { - public: - OpGraph() = default; - - ~OpGraph(); - - static OpGraph *Build(const SubGraphDef &subGraphDef); - - OpNode *GetNode(const NODE_ID &nodeId); - OpNode *AddNode(const NODE_ID &nodeId); - std::unordered_set GetInputNode(); - std::unordered_set GetOutputNode(); - - private: - int AddEdge(const NODE_ID &srcId, const NODE_ID &dstId); - int AddEdge(const NodeDef &srcNodeDef, const flatbuffers::Vector> &nodeDefs); - - protected: - std::unordered_map nodes; -}; -} // namespace predict -} // namespace mindspore - -#endif // PREDICT_COMMON_GRAPH_UTIL_H_ diff --git a/predict/common/module_registry.cc b/predict/common/module_registry.cc deleted file mode 100644 index da8992bb66..0000000000 --- a/predict/common/module_registry.cc +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "common/module_registry.h" - -namespace mindspore { -namespace predict { -ModuleRegistry *GetRegistryInstance() { - static ModuleRegistry registry; - return ®istry; -} -} // namespace predict -} // namespace mindspore diff --git a/predict/common/module_registry.h b/predict/common/module_registry.h deleted file mode 100644 index 9d7587e74a..0000000000 --- a/predict/common/module_registry.h +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PREDICT_COMMON_MODULE_REGISTRY_H_ -#define PREDICT_COMMON_MODULE_REGISTRY_H_ -#include -#include -#include -#include "common/mslog.h" - -#define MSPREDICT_API __attribute__((visibility("default"))) - -namespace mindspore { -namespace predict { -class ModuleBase { - public: - virtual ~ModuleBase() = default; -}; - -template -class Module; - -class ModuleRegistry { - public: - ModuleRegistry() = default; - - virtual ~ModuleRegistry() = default; - - template - bool Register(const std::string &name, const T &t) { - modules[name] = &t; - return true; - } - - template - std::shared_ptr Create(const std::string &name) { - auto it = modules.find(name); - if (it == modules.end()) { - return nullptr; - } - auto *module = (Module *)it->second; - if (module == nullptr) { - return nullptr; - } else { - return module->Create(); - } - } - - template - T *GetInstance(const std::string &name) { - auto it = modules.find(name); - if (it == modules.end()) { - return nullptr; - } - auto *module = (Module *)it->second; - if (module == nullptr) { - return nullptr; - } else { - return module->GetInstance(); - } - } - - protected: - std::unordered_map modules; -}; - -ModuleRegistry *GetRegistryInstance() MSPREDICT_API; - -template -class ModuleRegistrar { - public: - ModuleRegistrar(const std::string &name, const T &module) { - auto registryInstance = GetRegistryInstance(); - if (registryInstance == nullptr) { - MS_LOGW("registryInstance is nullptr."); - } else { - registryInstance->Register(name, module); - } - } -}; -} // namespace predict -} // namespace mindspore - -#endif // PREDICT_COMMON_MODULE_REGISTRY_H_ diff --git a/predict/common/mslog.cc b/predict/common/mslog.cc deleted file mode 100644 index a1b61bbc3d..0000000000 --- a/predict/common/mslog.cc +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "common/mslog.h" -#include -#include -#include -#include -#include "include/errorcode.h" - -namespace mindspore { -namespace predict { -std::string GetEnv(const std::string &envvar) { - const char *value = std::getenv(envvar.c_str()); - if (value == nullptr) { - return std::string(); - } - return std::string(value); -} - -bool IsPrint(int level) { - auto envString = GetEnv("MSLOG"); - static int env = static_cast(std::strtol(!envString.empty() ? envString.c_str() : "3", nullptr, 0)); - if (env == INT_MIN || env == INT_MAX) { - env = WARN; - // enable the SP for binscope checking - std::string errorStr = "env exceeded the value that type int is able to represent"; - MS_LOGE("%s", errorStr.c_str()); - } - - return level >= env; -} -} // namespace predict -} // namespace mindspore diff --git a/predict/common/mslog.h b/predict/common/mslog.h deleted file mode 100644 index a48d87f1fa..0000000000 --- a/predict/common/mslog.h +++ /dev/null @@ -1,230 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PREDICT_COMMON_MSLOG_H_ -#define PREDICT_COMMON_MSLOG_H_ - -#include -#include -#include -#include -#include -#include - -#if defined(__ANDROID__) || defined(ANDROID) -#include -#endif -namespace mindspore { -namespace predict { -constexpr const char *TAG = "MS_PREDICT"; - -constexpr int DEBUG = 1; -constexpr int INFO = 2; -constexpr int WARN = 3; -constexpr int ERROR = 4; - -#define MSPREDICT_API __attribute__((visibility("default"))) - -bool MSPREDICT_API IsPrint(int level); - -#if !defined(__ANDROID__) && !defined(ANDROID) - -#if LOG_TO_FILE -#define MS_LOGD(fmt, args...) \ - { \ - if (mindspore::predict::IsPrint(mindspore::predict::DEBUG)) { \ - syslog(LOG_DEBUG, "%s|%d|%s[%d]|: " #fmt, mindspore::predict::TAG, \getpid(), __func__, __LINE__, ##args); \ - } \ - } -#define MS_LOGI(fmt, args...) \ - { \ - if (mindspore::predict::IsPrint(mindspore::predict::INFO)) { \ - syslog(LOG_INFO, "%s|%d|%s[%d]|: " #fmt, mindspore::predict::TAG, \getpid(), __func__, __LINE__, ##args); \ - } \ - } -#define MS_LOGW(fmt, args...) \ - { \ - if (mindspore::predict::IsPrint(mindspore::predict::WARN)) { \ - syslog(LOG_WARNING, "%s|%d|%s[%d]|: " #fmt, mindspore::predict::TAG, \getpid(), __func__, __LINE__, ##args); \ - } \ - } -#define MS_LOGE(fmt, args...) \ - { \ - if (mindspore::predict::IsPrint(mindspore::predict::ERROR)) { \ - syslog(LOG_ERR, "%s|%d|%s[%d]|: " #fmt, mindspore::predict::TAG, getpid(), __func__, __LINE__, ##args); \ - } \ - } -#else - -#define MS_LOGD(fmt, args...) \ - { \ - if (mindspore::predict::IsPrint(mindspore::predict::DEBUG)) { \ - printf("[DEBUG] %s|%d|%s|%s[%d]|: " #fmt "\r\n", mindspore::predict::TAG, getpid(), __FILE__, __func__, \ - __LINE__, ##args); \ - } \ - } -#define MS_LOGI(fmt, args...) \ - { \ - if (mindspore::predict::IsPrint(mindspore::predict::INFO)) { \ - printf("[INFO] %s|%d|%s|%s[%d]|: " #fmt "\r\n", mindspore::predict::TAG, getpid(), __FILE__, __func__, \ - __LINE__, ##args); \ - } \ - } -#define MS_LOGW(fmt, args...) \ - { \ - if (mindspore::predict::IsPrint(mindspore::predict::WARN)) { \ - printf("[WARN] %s|%d|%s|%s[%d]|: " #fmt "\r\n", mindspore::predict::TAG, getpid(), __FILE__, __func__, \ - __LINE__, ##args); \ - } \ - } -#define MS_LOGE(fmt, args...) \ - { \ - if (mindspore::predict::IsPrint(mindspore::predict::ERROR)) { \ - printf("[ERROR] %s|%d|%s|%s[%d]|: " #fmt "\r\n", mindspore::predict::TAG, getpid(), __FILE__, __func__, \ - __LINE__, ##args); \ - } \ - } -#endif - -#else - -#define MS_LOGD(fmt, args...) \ - { \ - if (mindspore::predict::IsPrint(mindspore::predict::DEBUG)) \ - __android_log_print(ANDROID_LOG_DEBUG, mindspore::predict::TAG, "|%d|%s[%d]|: " fmt, getpid(), __func__, \ - __LINE__, ##args); \ - } - -#define MS_LOGI(fmt, args...) \ - { \ - if (mindspore::predict::IsPrint(mindspore::predict::INFO)) \ - __android_log_print(ANDROID_LOG_INFO, mindspore::predict::TAG, "|%d|%s[%d]|: " fmt, getpid(), __func__, \ - __LINE__, ##args); \ - } - -#define MS_LOGW(fmt, args...) \ - { \ - if (mindspore::predict::IsPrint(mindspore::predict::WARN)) \ - __android_log_print(ANDROID_LOG_WARN, mindspore::predict::TAG, "|%d|%s[%d]|: " fmt, getpid(), __func__, \ - __LINE__, ##args); \ - } - -#define MS_LOGE(fmt, args...) \ - { \ - if (mindspore::predict::IsPrint(mindspore::predict::ERROR)) \ - __android_log_print(ANDROID_LOG_ERROR, mindspore::predict::TAG, "|%d|%s[%d]|: " fmt, getpid(), __func__, \ - __LINE__, ##args); \ - } - -#endif - -#define MS_LOG(severity) std::cout << std::endl -#define MS_DLOG(verboselevel) std::cout << std::endl -// Kill the process for safe exiting. -inline void KillProcess(const std::string &ret) { - MS_LOG(ERROR) << "mindspore Exit Tip:" << ret; - if (raise(SIGKILL) != 0) { - MS_LOGE("Send SIGKILL to kill process failed"); - } -} -} // namespace predict -} // namespace mindspore - -#define MS_ASSERT(expression) \ - do { \ - if (!(expression)) { \ - std::stringstream ss; \ - ss << "Assertion failed: " << #expression << ", file: " << __FILE__ << ", line: " << __LINE__; \ - mindspore::predict::KillProcess(ss.str()); \ - } \ - } while (0) - -#define MS_EXIT(ret) \ - do { \ - std::stringstream ss; \ - ss << (ret) << " ( file: " << __FILE__ << ", line: " << __LINE__ << " )."; \ - mindspore::predict::KillProcess(ss.str()); \ - } while (0) - -#define MS_PRINT_ERROR(fmt, args...) \ - printf(#fmt "\n", ##args); \ - MS_LOGE(fmt, ##args); - -#define MS_PRINT_INFO(fmt, args...) \ - printf(fmt "\n", ##args); \ - MS_LOGI(fmt, ##args); - -constexpr int LOG_CHECK_EVERY_FIRSTNUM = 10; -constexpr int LOG_CHECK_EVERY_NUM1 = 10; -constexpr int LOG_CHECK_EVERY_NUM2 = 100; -constexpr int LOG_CHECK_EVERY_NUM3 = 1000; -constexpr int LOG_CHECK_EVERY_NUM4 = 10000; - -#define LOG_CHECK_ID_CONCAT(word1, word2) word1##word2 - -#define LOG_CHECK_ID LOG_CHECK_ID_CONCAT(__FUNCTION__, __LINE__) - -#define LOG_CHECK_FIRST_N \ - [](uint32_t firstNum) { \ - static uint32_t LOG_CHECK_ID = 0; \ - ++LOG_CHECK_ID; \ - return (LOG_CHECK_ID <= firstNum); \ - } - -#define LOG_CHECK_EVERY_N1 \ - [](uint32_t firstNum, uint32_t num) { \ - static uint32_t LOG_CHECK_ID = 0; \ - ++LOG_CHECK_ID; \ - return ((LOG_CHECK_ID <= firstNum) || (LOG_CHECK_ID % num == 0)); \ - } - -#define LOG_CHECK_EVERY_N2 \ - [](uint32_t firstNum, uint32_t num1, uint32_t num2) { \ - static uint32_t LOG_CHECK_ID = 0; \ - ++LOG_CHECK_ID; \ - return ((LOG_CHECK_ID <= firstNum) || (LOG_CHECK_ID < num2 && LOG_CHECK_ID % num1 == 0) || \ - (LOG_CHECK_ID % num2 == 0)); \ - } - -#define LOG_CHECK_EVERY_N3 \ - [](uint32_t firstNum, uint32_t num1, uint32_t num2, uint32_t num3) { \ - static uint32_t LOG_CHECK_ID = 0; \ - ++LOG_CHECK_ID; \ - return ((LOG_CHECK_ID <= firstNum) || (LOG_CHECK_ID < num2 && LOG_CHECK_ID % num1 == 0) || \ - (LOG_CHECK_ID < num3 && LOG_CHECK_ID % num2 == 0) || (LOG_CHECK_ID % num3 == 0)); \ - } - -#define LOG_CHECK_EVERY_N4 \ - [](uint32_t firstNum, uint32_t num1, uint32_t num2, uint32_t num3, uint32_t num4) { \ - static uint32_t LOG_CHECK_ID = 0; \ - ++LOG_CHECK_ID; \ - return ((LOG_CHECK_ID <= firstNum) || (LOG_CHECK_ID < num2 && LOG_CHECK_ID % num1 == 0) || \ - (LOG_CHECK_ID < num3 && LOG_CHECK_ID % num2 == 0) || (LOG_CHECK_ID < num4 && LOG_CHECK_ID % num3 == 0) || \ - (LOG_CHECK_ID % num4 == 0)); \ - } - -#define LOG_CHECK_EVERY_N \ - []() { \ - static uint32_t LOG_CHECK_ID = 0; \ - ++LOG_CHECK_ID; \ - return ((LOG_CHECK_ID <= LOG_CHECK_EVERY_FIRSTNUM) || \ - (LOG_CHECK_ID < LOG_CHECK_EVERY_NUM2 && LOG_CHECK_ID % LOG_CHECK_EVERY_NUM1 == 0) || \ - (LOG_CHECK_ID < LOG_CHECK_EVERY_NUM3 && LOG_CHECK_ID % LOG_CHECK_EVERY_NUM2 == 0) || \ - (LOG_CHECK_ID < LOG_CHECK_EVERY_NUM4 && LOG_CHECK_ID % LOG_CHECK_EVERY_NUM3 == 0) || \ - (LOG_CHECK_ID % LOG_CHECK_EVERY_NUM4 == 0)); \ - } - -#endif // PREDICT_COMMON_MSLOG_H_ diff --git a/predict/common/op_utils.h b/predict/common/op_utils.h deleted file mode 100644 index 35f01edce3..0000000000 --- a/predict/common/op_utils.h +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PREDICT_COMMON_OP_UTILS_H_ -#define PREDICT_COMMON_OP_UTILS_H_ - -#include -#include -#include "schema/inner/ms_generated.h" - -namespace mindspore { -namespace predict { -inline OpT GetOpType(const OpDef &opDef) { return opDef.attr_type(); } - -inline OpT GetOpType(const NodeDef &nodeDef) { return GetOpType(*(nodeDef.opDef())); } - -inline std::string GetOpTypeName(const NodeDef &nodeDef) { return EnumNameOpT(GetOpType(nodeDef)); } - -inline std::string GetOpTypeName(const OpDef &opDef) { return EnumNameOpT(GetOpType(opDef)); } - -inline OpT GetOpType(const OpDefT &opDefT) { return opDefT.attr.type; } - -inline OpT GetOpType(const NodeDefT &nodeDefT) { return GetOpType(*(nodeDefT.opDef.get())); } - -inline std::string GetOpTypeName(const NodeDefT &nodeDefT) { return EnumNameOpT(GetOpType(nodeDefT)); } - -inline std::string GetOpTypeName(const OpDefT &opDefT) { return EnumNameOpT(GetOpType(opDefT)); } -} // namespace predict -} // namespace mindspore - -#endif // PREDICT_COMMON_OP_UTILS_H_ diff --git a/predict/common/option.h b/predict/common/option.h deleted file mode 100644 index ca72dde29b..0000000000 --- a/predict/common/option.h +++ /dev/null @@ -1,119 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PREDICT_COMMON_OPTION_H_ -#define PREDICT_COMMON_OPTION_H_ - -#include -#include -#include "common/mslog.h" - -namespace mindspore { -namespace predict { -template -struct InnerSome { - explicit InnerSome(const T &t) : _t(std::move(t)) {} - - T _t; -}; - -template -InnerSome::type> Some(T &&t) { - return InnerSome::type>(std::forward(t)); -} - -struct None {}; - -template -class Option { - public: - Option() : state(NONE) {} - - explicit Option(const T &t) : data(t), state(SOME) {} - - explicit Option(T &&t) : data(std::move(t)), state(SOME) {} - - explicit Option(const InnerSome &some) : data(some._t), state(SOME) {} - - explicit Option(const None &none) : state(NONE) {} - - Option(const Option &that) : state(that.state) { - if (that.IsSome()) { - new (&data) T(that.data); - } - } - - virtual ~Option() = default; - - bool IsNone() const { return state == NONE; } - - bool IsSome() const { return state == SOME; } - - const T &Get() const & { - MS_ASSERT(IsSome()); - return data; - } - - T &Get() & { - MS_ASSERT(IsSome()); - return data; - } - - T &&Get() && { - MS_ASSERT(IsSome()); - return std::move(data); - } - - const T &&Get() const && { - MS_ASSERT(IsSome()); - return std::move(data); - } - - // oprerator override - Option &operator=(const Option &that) { - if (&that != this) { - if (IsSome()) { - data.~T(); - } - state = that.state; - if (that.IsSome()) { - new (&data) T(that.data); - } - } - - return *this; - } - - bool operator==(const Option &that) const { - return (IsNone() && that.IsNone()) || (IsSome() && that.IsSome() && data == that.data); - } - - bool operator!=(const Option &that) const { return !(*this == that); } - - bool operator==(const T &that) const { return IsSome() && data == that; } - - bool operator!=(const T &that) const { return !(*this == that); } - - private: - enum State { NONE = 0, SOME = 1 }; - - T data; - State state; -}; -} // namespace predict -} // namespace mindspore - -#endif // PREDICT_COMMON_OPTION_H_ diff --git a/predict/common/storage.cc b/predict/common/storage.cc deleted file mode 100644 index ade5861c74..0000000000 --- a/predict/common/storage.cc +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "common/storage.h" -#include "flatbuffers/flatbuffers.h" -#include "common/mslog.h" -#include "common/file_utils.h" - -namespace mindspore { -namespace predict { -int Storage::Save(const GraphDefT &graph, const std::string &outputPath) { - flatbuffers::FlatBufferBuilder builder(flatSize); - auto offset = GraphDef::Pack(builder, &graph); - builder.Finish(offset); - int size = builder.GetSize(); - auto content = builder.GetBufferPointer(); - if (content == nullptr) { - MS_LOGE("GetBufferPointer nullptr"); - return RET_ERROR; - } - std::string realPath = RealPath(outputPath.c_str()); - if (realPath.empty()) { - MS_LOGE("Output file path '%s' is not valid", outputPath.c_str()); - return RET_ERROR; - } - - std::ofstream output(realPath, std::ofstream::binary); - if (!output.is_open()) { - MS_LOGE("ofstream open failed"); - return RET_ERROR; - } - output.write((const char *)content, size); - output.close(); - return RET_OK; -} -} // namespace predict -} // namespace mindspore diff --git a/predict/common/storage.h b/predict/common/storage.h deleted file mode 100644 index fc612ffb6b..0000000000 --- a/predict/common/storage.h +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PREDICT_COMMON_STORAGE_H_ -#define PREDICT_COMMON_STORAGE_H_ - -#include -#include -#include "include/errorcode.h" -#include "flatbuffers/flatbuffers.h" -#include "schema/inner/ms_generated.h" - -namespace mindspore { -namespace predict { -class Storage { - public: - int Save(const GraphDefT &graph, const std::string &outputPath); - const int flatSize = 1024; -}; -} // namespace predict -} // namespace mindspore - -#endif // PREDICT_COMMON_STORAGE_H_ diff --git a/predict/common/utils.cc b/predict/common/utils.cc deleted file mode 100644 index b186a4b8d8..0000000000 --- a/predict/common/utils.cc +++ /dev/null @@ -1,228 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "common/utils.h" - -namespace mindspore { -namespace predict { -uint64_t GetTimeUs() { - struct timespec ts = {0, 0}; - if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { - return 0; - } - // USECS_IN_SEC *NSECS_IN_USEC; - auto retval = static_cast((ts.tv_sec * USEC) + (ts.tv_nsec / MSEC)); - return retval; -} - -static const unsigned int FP32_BIT_SIZE = 32; -static const unsigned int FP32_EXPONENT_BIAS = 127; -static const unsigned int FP32_SIGNIFICAND = 23; - -static const unsigned int FP32_EXPONENT_MAX = 255; - -static const unsigned int FP16_BIT_SIZE = 16; -static const unsigned int FP16_EXPONENT_BIAS = 15; -static const unsigned int FP16_SIGNIFICAND = 10; - -static const int FP16_EXPONENT_MAX = 30; -static const int FP16_EXPONENT_MIN = -10; - -float ShortToFloat32(int16_t srcValue) { - uint16_t expHalf16 = srcValue & 0x7C00; - int exp1 = static_cast(expHalf16); - uint16_t mantissa16 = srcValue & 0x03FF; - int mantissa1 = static_cast(mantissa16); - int sign = static_cast(srcValue & 0x8000); - sign = sign << FP16_BIT_SIZE; - - // nan or inf - if (expHalf16 == 0x7C00) { - // nan - if (mantissa16 > 0) { - int res = (0x7FC00000 | sign); - int *iRes = &res; - MS_ASSERT(iRes != nullptr); - auto fres = static_cast(*iRes); - return fres; - } - // inf - int res = (0x7F800000 | sign); - int *iRes = &res; - MS_ASSERT(iRes != nullptr); - auto fres = static_cast(*iRes); - return fres; - } - if (expHalf16 != 0) { - exp1 += ((FP32_EXPONENT_BIAS - FP16_EXPONENT_BIAS) << FP16_SIGNIFICAND); // exponents converted to float32 bias - int res = (exp1 | mantissa1); - res = res << (FP32_SIGNIFICAND - FP16_SIGNIFICAND); - res = (res | sign); - int *iRes = &res; - - auto fres = static_cast(*iRes); - return fres; - } - - int xmm1 = exp1 > (1 << FP16_SIGNIFICAND) ? exp1 : (1 << FP16_SIGNIFICAND); - xmm1 = (xmm1 << (FP32_SIGNIFICAND - FP16_SIGNIFICAND)); - xmm1 += ((FP32_EXPONENT_BIAS - FP16_EXPONENT_BIAS - FP16_SIGNIFICAND) - << FP32_SIGNIFICAND); // add the bias difference to xmm1 - xmm1 = xmm1 | sign; // Combine with the sign mask - - auto res = static_cast(mantissa1); // Convert mantissa to float - res *= static_cast(xmm1); - - return res; -} - -int16_t Float32ToShort(float srcValue) { - auto srcValueBit = static_cast(srcValue); - int sign = srcValueBit >> (FP32_BIT_SIZE - 1); - int mantissa = srcValueBit & 0x007FFFFF; - // exponent - int exp = ((srcValueBit & 0x7F800000) >> FP32_SIGNIFICAND) + FP16_EXPONENT_BIAS - FP32_EXPONENT_BIAS; - int16_t res; - if (exp > 0 && exp < FP16_EXPONENT_MAX) { - // use rte rounding mode, round the significand, combine sign, exponent and significand into a short. - res = (sign << (FP16_BIT_SIZE - 1)) | (exp << FP16_SIGNIFICAND) | - ((mantissa + 0x00001000) >> (FP32_SIGNIFICAND - FP16_SIGNIFICAND)); - } else if (srcValueBit == 0) { - res = 0; - } else { - if (exp <= 0) { - if (exp < FP16_EXPONENT_MIN) { - // value is less than min half float point - res = 0; - } else { - // normalized single, magnitude is less than min normal half float point. - mantissa = (mantissa | 0x00800000) >> (1 - exp); - // round to nearest - if ((mantissa & 0x00001000) > 0) { - mantissa = mantissa + 0x00002000; - } - // combine sign & mantissa (exp is zero to get denormalized number) - res = (sign << FP16_EXPONENT_BIAS) | (mantissa >> (FP32_SIGNIFICAND - FP16_SIGNIFICAND)); - } - } else if (exp == (FP32_EXPONENT_MAX - FP32_EXPONENT_BIAS + FP16_EXPONENT_BIAS)) { - if (mantissa == 0) { - // input float is infinity, return infinity half - res = (sign << FP16_EXPONENT_BIAS) | 0x7C00; - } else { - // input float is NaN, return half NaN - res = (sign << FP16_EXPONENT_BIAS) | 0x7C00 | (mantissa >> (FP32_SIGNIFICAND - FP16_SIGNIFICAND)); - } - } else { - // exp > 0, normalized single, round to nearest - if ((mantissa & 0x00001000) > 0) { - mantissa = mantissa + 0x00002000; - if ((mantissa & 0x00800000) > 0) { - mantissa = 0; - exp = exp + 1; - } - } - if (exp > FP16_EXPONENT_MAX) { - // exponent overflow - return infinity half - res = (sign << FP16_EXPONENT_BIAS) | 0x7C00; - } else { - // combine sign, exp and mantissa into normalized half - res = (sign << FP16_EXPONENT_BIAS) | (exp << FP16_SIGNIFICAND) | - (mantissa >> (FP32_SIGNIFICAND - FP16_SIGNIFICAND)); - } - } - } - return res; -} -std::string Remove(const std::string &from, const std::string &subStr, Mode mode) { - std::string result = from; - if (mode == PREFIX) { - if (from.substr(0, subStr.length()) == subStr) { - result = from.substr(subStr.size()); - } - } else if (mode == SUFFIX) { - if (from.rfind(subStr) == from.size() - subStr.size()) { - result = from.substr(0, from.size() - subStr.size()); - } - } else { - size_t index; - while ((index = result.find(subStr)) != std::string::npos) { - result = result.erase(index, subStr.size()); - } - } - - return result; -} - -std::vector StrSplit(const std::string &str, const std::string &pattern) { - std::string::size_type pos; - std::vector result; - std::string tmpStr(str + pattern); - std::string::size_type size = tmpStr.size(); - - for (std::string::size_type i = 0; i < size; i++) { - pos = tmpStr.find(pattern, i); - if (pos < size) { - std::string s = tmpStr.substr(i, pos - i); - result.push_back(s); - i = pos + pattern.size() - 1; - } - } - return result; -} - -std::vector Tokenize(const std::string &src, const std::string &delimiters, - const Option &maxTokenNum) { - if (maxTokenNum.IsSome() && maxTokenNum.Get() == 0) { - return {}; - } - - std::vector tokens; - size_t offset = 0; - - while (true) { - size_t nonDelimiter = src.find_first_not_of(delimiters, offset); - if (nonDelimiter == std::string::npos) { - break; - } - size_t delimiter = src.find_first_of(delimiters, nonDelimiter); - if (delimiter == std::string::npos || (maxTokenNum.IsSome() && tokens.size() == maxTokenNum.Get() - 1)) { - tokens.push_back(src.substr(nonDelimiter)); - break; - } - - tokens.push_back(src.substr(nonDelimiter, delimiter - nonDelimiter)); - offset = delimiter; - } - return tokens; -} - -void ShortToFloat32(const int16_t *srcdata, float *dstdata, size_t elementSize) { - MS_ASSERT(srcdata != nullptr); - MS_ASSERT(dstdata != nullptr); - for (size_t i = 0; i < elementSize; i++) { - dstdata[i] = ShortToFloat32(srcdata[i]); - } -} - -void Float32ToShort(const float *srcdata, int16_t *dstdata, size_t elementSize) { - MS_ASSERT(srcdata != nullptr); - MS_ASSERT(dstdata != nullptr); - for (size_t i = 0; i < elementSize; i++) { - dstdata[i] = Float32ToShort(srcdata[i]); - } -} -} // namespace predict -} // namespace mindspore diff --git a/predict/common/utils.h b/predict/common/utils.h deleted file mode 100644 index e7d44fe982..0000000000 --- a/predict/common/utils.h +++ /dev/null @@ -1,154 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PREDICT_COMMON_UTILS_H_ -#define PREDICT_COMMON_UTILS_H_ - -#include -#include -#include -#include -#include -#include "common/mslog.h" -#include "common/option.h" -#include "include/errorcode.h" - -namespace mindspore { -namespace predict { -const int USEC = 1000000; -const int MSEC = 1000; - -uint64_t GetTimeUs(); - -int16_t Float32ToShort(float srcValue); - -float ShortToFloat32(int16_t srcValue); - -void ShortToFloat32(const int16_t *srcData, float *dstData, size_t elementSize); - -void Float32ToShort(const float *srcData, int16_t *dstData, size_t elementSize); - -template -bool IsContain(const std::vector &vec, T element) { - for (auto iter = vec.begin(); iter != vec.end(); iter++) { - if (*iter == element) { - return true; - } - } - return false; -} - -const char WHITESPACE[] = "\t\n\v\f\r "; -const char STR_TRUE[] = "true"; -const char STR_FALSE[] = "false"; - -template -Option ToString(T t) { - std::ostringstream out; - out << t; - if (!out.good()) { - return Option(None()); - } - - return Option(out.str()); -} - -template <> -inline Option ToString(bool value) { - return value ? Option(STR_TRUE) : Option(STR_FALSE); -} - -// get the file name from a given path -// for example: "/usr/bin", we will get "bin" -inline std::string GetFileName(const std::string &path) { - char delim = '/'; - - size_t i = path.rfind(delim, path.length()); - if (i != std::string::npos) { - return (path.substr(i + 1, path.length() - i)); - } - - return ""; -} - -// trim the white space character in a string -// see also: macro WHITESPACE defined above -inline void Trim(std::string *input) { - if (input == nullptr) { - return; - } - if (input->empty()) { - return; - } - - input->erase(0, input->find_first_not_of(WHITESPACE)); - input->erase(input->find_last_not_of(WHITESPACE) + 1); -} - -// to judge whether a string is starting with prefix -// for example: "hello world" is starting with "hello" -inline bool StartsWithPrefix(const std::string &source, const std::string &prefix) { - if (source.length() < prefix.length()) { - return false; - } - - return (source.compare(0, prefix.length(), prefix) == 0); -} - -// split string -std::vector StrSplit(const std::string &str, const std::string &pattern); - -// tokenize string -std::vector Tokenize(const std::string &src, const std::string &delimiters, - const Option &maxTokenNum = Option(None())); - -enum Mode { PREFIX, SUFFIX, ANY }; - -// remove redundant character -std::string Remove(const std::string &from, const std::string &subStr, Mode mode = ANY); - -template -inline Option GenericParseValue(const std::string &value) { - T ret; - std::istringstream input(value); - input >> ret; - - if (input && input.eof()) { - return Option(ret); - } - - return Option(None()); -} - -template <> -inline Option GenericParseValue(const std::string &value) { - return Option(value); -} - -template <> -inline Option GenericParseValue(const std::string &value) { - if (value == "true") { - return Option(true); - } else if (value == "false") { - return Option(false); - } - - return Option(None()); -} -} // namespace predict -} // namespace mindspore - -#endif // PREDICT_COMMON_UTILS_H_ diff --git a/predict/include/context.h b/predict/include/context.h deleted file mode 100644 index cb30f1f8c2..0000000000 --- a/predict/include/context.h +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PREDICT_INCLUDE_CONTEXT_H_ -#define PREDICT_INCLUDE_CONTEXT_H_ - -#include -#include "dlpack/dlpack.h" -#include "include/tensor.h" - -#define MSPREDICT_API __attribute__((visibility("default"))) - -namespace mindspore { -namespace predict { -///\brief Resource management definition of MindSpore predict. -class MSPREDICT_API Context { - public: - ///\brief Constructor of MindSpore predict context using default value for parameters. - /// - ///\return Instance of MindSpore predict context. - Context(); - - ///\brief Custum constructor of MindSpore predict context using input value for parameters. - /// - ///\param[in] threadNum The number of thread during the runtime. - ///\param[in] allocator The memory management during the runtime - ///\param[in] deviceCtx The device information during the runtime. - /// - ///\return Instance of MindSpore predict context. - Context(int threadNum, std::shared_ptr allocator, DLContext deviceCtx); - - ///\brief Destructor of MindSpore predict context. - virtual ~Context(); - - public: - DLContext deviceCtx; - int threadNum = 1; - std::shared_ptr allocator; -}; -} // namespace predict -} // namespace mindspore - -#endif // PREDICT_INCLUDE_CONTEXT_H_ diff --git a/predict/include/errorcode.h b/predict/include/errorcode.h deleted file mode 100755 index 5487673f16..0000000000 --- a/predict/include/errorcode.h +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PREDICT_INCLUDE_ERRORCODE_H_ -#define PREDICT_INCLUDE_ERRORCODE_H_ - -namespace mindspore { -namespace predict { -using STATUS = int; - -/* Success */ -constexpr int RET_OK = 0; /**< No error occurs. */ - -/* Common error code, range: [-1, -100]*/ -constexpr int RET_ERROR = -1; /**< Common error code. */ -constexpr int RET_NULL_PTR = -2; /**< NULL pointer returned.*/ -constexpr int RET_PARAM_INVALID = -3; /**< Invalid parameter.*/ -constexpr int RET_NO_CHANGE = -4; /**< No change. */ - -/* Executor error code, range: [-101,-200] */ -constexpr int RET_OUT_OF_TENSOR_RANGE = -101; /**< Failed to checking range. */ -constexpr int RET_INPUT_TENSOR_ERROR = -102; /**< Failed to checking input tensor. */ -constexpr int RET_REENTRANT_ERROR = -103; /**< Exist executor running. */ - -/* Graph error code, range: [-201,-300] */ -constexpr int RET_GRAPH_FILE_ERR = -201; /**< Failed to verify graph file. */ - -/* Node error code, range: [-301,-400] */ -constexpr int RET_NOT_FIND_OP = -301; /**< Failed to find OP. */ -constexpr int RET_INVALID_OP_NAME = -302; /**< Invalid OP name. */ -constexpr int RET_INVALID_OP_ATTR = -303; /**< Invalid OP attr. */ -constexpr int RET_OP_EXECUTE_FAILURE = -304; /**< Failed to execution OP. */ - -/* Tensor error code, range: [-401,-500] */ -constexpr int RET_FORMAT_ERR = -401; /**< Failed to checking tensor format. */ -} // namespace predict -} // namespace mindspore - -#endif // PREDICT_INCLUDE_ERRORCODE_H_ diff --git a/predict/include/session.h b/predict/include/session.h deleted file mode 100644 index 76fa2c4d6e..0000000000 --- a/predict/include/session.h +++ /dev/null @@ -1,139 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PREDICT_INCLUDE_SESSION_H_ -#define PREDICT_INCLUDE_SESSION_H_ - -#include -#include -#include -#include -#include -#include "include/context.h" -#include "include/tensor.h" - -#define MSPREDICT_API __attribute__((visibility("default"))) - -namespace mindspore { -namespace predict { -using NODE_ID = std::string; - -///\brief Graph defined by MindSpore predict. -/// -///\note -/// The caller does not need to care about detailed implementation of this class, so just list the class name here. -class Graph; - -///\brief GraphExecution defined by MindSpore predict. -/// -///\note -/// The caller does not need to care about detailed implementation of this class, so just list the class name here. -class GraphExecution; - -///\brief MindSpore predict session. -/// -/// This class represents session of MindSpore predict. -/// -///\note -/// The caller needs to allocate and free memory of inputs and outputs. -/// New Session is not suggested, please use CreateSession function to create new session class. -class MSPREDICT_API Session { - public: - ///\brief Constructor of MindSpore predict session. - /// - ///\param[in] ctx The context of the session. - /// - ///\return Instance of MindSpore predict session. - explicit Session(const Context &ctx); - - ///\brief Destructor of MindSpore predict session. - ~Session(); - - ///\brief Init the session. - /// - ///\param[in] ctx The context of the session. - ///\param[in] size The size of the session. - ///\param[in] graphBuf The buffer of the graph, used for build session. - /// - ///\return Return RET_OK if the initialization is success, otherwhise return RET_ERROR. - int Init(const char *graphBuf, size_t size); - - ///\brief Get the input of session. - /// - ///\return Input node's input tensors if found, empty vector otherwise. - /// - ///\note - /// The caller needs to allocate and free memory of inputs. - std::vector GetInput(); - - ///\brief Run the session. - /// - ///\param[in] inputs The input of the session. - /// - ///\return Return RET_OK if run success, otherwhise return RET_ERROR. - ///\note - /// Currently input tensors' data format only support FORMAT_NCHW. - /// Currently input tensors' data type only support FLOAT. - int Run(const std::vector &inputs); - - ///\brief Get the output of session. - /// - ///\param[in] nodeName Given output node name. - /// - ///\return Output node's output tensors if found, empty vector otherwise. - /// - ///\note - /// The caller needs to free memory of outputs. - std::vector GetOutput(const std::string &nodeName); - - ///\brief Get the all output of session. - /// - ///\return Every output node's output tensors. - /// - ///\note - /// The caller needs to free memory of outputs. - std::map> GetAllOutput(); - - protected: - ///\brief Init the executor. - /// - ///\return Return RET_OK if the initialization is success, otherwhise return RET_ERROR. - int InitExecutor(); - - const Context &_ctx; - Graph *_graph = nullptr; - GraphExecution *_executor = nullptr; - bool reinitExecutor = true; -}; - -///\brief MindSpore predict neural network session create function -/// -/// This function used to create MindSpore predict neural network session, which will be used to run the neural network. -/// -///\param[in] sessionName The name of the session. -///\param[in] graphBuf The buffer of the graph, used for build session. -///\param[in] size The size of the session. -///\param[in] ctx The context of the session. -/// -///\return Instance of MindSpore predict session. -/// -///\note -/// The caller needs to allocate and free memory of graph buffer. -std::shared_ptr MSPREDICT_API CreateSession(const char *graphBuf, size_t size, const Context &ctx); -} // namespace predict -} // namespace mindspore - -#endif // PREDICT_INCLUDE_SESSION_H_ diff --git a/predict/include/tensor.h b/predict/include/tensor.h deleted file mode 100644 index 8a608b486d..0000000000 --- a/predict/include/tensor.h +++ /dev/null @@ -1,259 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PREDICT_INCLUDE_TENSOR_H_ -#define PREDICT_INCLUDE_TENSOR_H_ - -#include -#include -#include "dlpack/dlpack.h" -#include "schema/inner/ms_generated.h" - -#define MSPREDICT_API __attribute__((visibility("default"))) - -namespace mindspore { -namespace predict { -///\brief Allocator definition of MindSpore predict. -class Allocator; - -///\brief Tensor definition of MindSpore predict. -class MSPREDICT_API Tensor { - public: - ///\brief Constructor of MindSpore predict tensor. - /// - ///\param[in] tensor Define the parameters of the tensor. - ///\param[in] copyData Malloc data for the tensor, and copy origin data from - /// input tensor. - /// - ///\return Instance of MindSpore predict tensor. - Tensor(const Tensor &tensor, bool copyData = false); - - ///\brief Constructor of MindSpore predict tensor. - /// - ///\param[in] dt Data Type of the tensor, see introduction to 'enum DataType' - /// for supported type. - ///\param[in] dims Dimension Values such as height and width, which defined - /// the shape of the tensor. - ///\param[in] format Tensor format, see introduction to 'enum Format' for - /// supported format. - ///\param[in] data Data of the tensor. - /// - ///\return Instance of MindSpore predict tensor. - /// - ///\note - /// Length of data should align with dt, format and dims, otherwise the - /// application might run into unexpected error, - /// such as segment fault. - /// For example, dt is DT_FLOAT, format is FORMAT_NCHW, dims is [1,3,300,300], - /// then minimum length of data should - /// be 1 * 3 * 300 * 300 * sizeof(float). - Tensor(DataType dt, const std::vector &dims, Format format, void *data); - - ///\brief Destructor of MindSpore predict tensor. - ~Tensor(); - - ///\brief Get MindSpore predict tensor. - /// - ///\param[in] Definition of the tensor. - /// - ///\return Address of MindSpore predict tensor. - static Tensor *CopyFromTensorDef(const TensorDef &tensordef); - - ///\brief Get dtype of MindSpore predict tensor. - /// - ///\return Dtype of MindSpore predict tensor. - DLDataType GetTensorDtype() const; - - ///\brief Get data of MindSpore predict tensor. - /// - ///\return Address of MindSpore predict tensor data. - void *GetData() const; - - ///\brief Set data of MindSpore predict tensor. - /// - ///\param[in] data Address for data of the MindSpore predict tensor instance. - /// - ///\note - /// Length of data should align with dt, format and dims, otherwise the - /// application might run into unexpected error, - /// such as segment fault. - /// For example, dt is DT_FLOAT, format is FORMAT_NCHW, dims is [1,3,300,300], - /// then minimum length of data should - /// be 1 * 3 * 300 * 300 * sizeof(float). - void SetData(void *data); - - ///\brief Get data type of MindSpore predict tensor. - /// - ///\return Data Type of the tensor. - DataType GetDataType() const; - - ///\brief Set data type of MindSpore predict tensor. - /// - ///\param[in] dt Data Type of the tensor, see introduction to 'enum DataType' - /// for supported type. - void SetDataType(DataType dt); - - ///\brief Get number of dimension of MindSpore predict tensor. - /// - ///\return Number of dimension of the MindSpore predict tensor. - int GetNDim() const; - - ///\brief Get dimension of MindSpore predict tensor. - /// - ///\return Dimension of the MindSpore predict tensor. - std::vector GetDims() const; - - ///\brief Set dimension of MindSpore predict tensor. - /// - ///\param[in] dims Vector that has values of dimension. - void SetDims(const std::vector &dims); - - ///\brief Get format of MindSpore predict tensor. - /// - ///\return Format of the MindSpore predict tensor. - Format GetFormat() const { return format; } - - ///\brief Set format of MindSpore predict tensor. - /// - ///\param[in] format Format of the tensor. - void SetFormat(Format format) { this->format = format; } - - ///\brief Get reference count of MindSpore predict tensor. - /// - ///\return Reference count of the MindSpore predict tensor. - int RefCount() { return refCount; } - - ///\brief Increase reference count of MindSpore predict tensor. - /// - ///\param[in] ref The increase of the reference count. - void AddRef(int ref) { refCount += ref; } - - ///\brief Decrease reference count of MindSpore predict tensor. - /// - ///\param[in] ref The decrease of the reference count. - void DefRef(int ref) { refCount -= ref; } - - ///\brief Get element size of MindSpore predict tensor. - /// - ///\return Element size of MindSpore predict tensor. - size_t GetElementSize() const; - - ///\brief Get data size of MindSpore predict tensor. - /// - ///\return Data size of MindSpore predict tensor. - size_t GetDataSize() const; - - ///\brief Get element size of MindSpore predict tensor in NC4HW4 format. - /// - ///\param[in] isNhwc Whether the current format is NHWC. - /// - ///\return Element size of MindSpore predict tensor in NC4HW4 format. - size_t GetNC4HW4ElementSize(bool isNhwc); - - ///\brief Get data size of MindSpore predict tensor in NC4HW4 format. - /// - ///\param[in] isNhwc Whether the current format is NHWC. - /// - ///\return Data size of MindSpore predict tensor in NC4HW4 format. - size_t GetNC4HW4DataSize(bool isNhwc); - - ///\brief Malloc data for the MindSpore predict tensor. - /// - ///\param[in] allocator The malloc source for data. - ///\param[in] refCount The reference count of the data. - /// - ///\return Return RET_OK if the data is successfully allocated, otherwhise return RET_ERROR. - int MallocData(std::shared_ptr allocator = nullptr, int refCount = 0); - - ///\brief Free the MindSpore predict tensor. - void FreeTensor(); - - ///\brief Free the data of MindSpore predict tensor. - void ForceFreeData(); - - ///\brief Free the data of MindSpore predict tensor. - void FreeData(); - - ///\brief Compare data size of MindSpore predict tensor in NC4HW4 format. - /// - ///\param[in] dst The compare tensor. - /// - ///\return The result of fuction. - bool CompareShape(const Tensor &dst); - - ///\brief Compare shape of MindSpore predict tensor with another shape. - /// - ///\param[in] other The compare shape information. - /// - ///\return The result of function. - bool CompareShape(const std::vector &other); - - ///\brief Get instance of MindSpore predict tensor. - /// - ///\return Instance of MindSpore predict dlTensor. - DLTensor *GetDLTensor() { return &dlTensor; } - - ///\brief Get height of MindSpore predict tensor. - /// - ///\return Height of MindSpore predict tensor. - int64_t Height() const; - - ///\brief Get width of MindSpore predict tensor. - /// - ///\return Width of MindSpore predict tensor. - int64_t Width() const; - - ///\brief Get channel of MindSpore predict tensor. - /// - ///\return Channel of MindSpore predict tensor. - int64_t Channel() const; - - ///\brief Get batch of MindSpore predict tensor. - /// - ///\return Batch of MindSpore predict tensor. - int64_t Batch() const; - - ///\brief Get stride of MindSpore predict tensor. - /// - ///\param[in] index the index of stride. - /// - ///\return Stride of MindSpore predict tensor. - int64_t Stride(int index) const; - - ///\brief Set stride of MindSpore predict tensor by input. - /// - ///\param[in] index Index of stride - ///\param[in] stride The stride to set - void SetStride(int index, int64_t stride); - - ///\brief Set stride of MindSpore predict tensor by dims. - void SetStride(); - void SetScale(bool isScale = true); - - private: - bool isScale = false; - int refCount = 0; - int isConst; - Format format; - DLTensor dlTensor; - std::shared_ptr allocator = nullptr; - std::vector scale; - std::vector zeroPoint; -}; -} // namespace predict -} // namespace mindspore - -#endif // PREDICT_INCLUDE_TENSOR_H_ diff --git a/predict/module/CMakeLists.txt b/predict/module/CMakeLists.txt deleted file mode 100644 index 9b978f1172..0000000000 --- a/predict/module/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -add_subdirectory(tvm_kernel) diff --git a/predict/module/tvm_kernel/.gitignore b/predict/module/tvm_kernel/.gitignore deleted file mode 100644 index 3b552d75da..0000000000 --- a/predict/module/tvm_kernel/.gitignore +++ /dev/null @@ -1,27 +0,0 @@ -# Created by .ignore support plugin -# - -# filter python -*.pyc - -# filter build -*.so -*.o - -# filter coverage -coverage/ - -# filter report -*.xml - -# filter tvm -3rdparty/ - -# filter build -build/ -cmake-build-debug/ -.idea/ -TFLite_Detection_PostProcess_CI -app_run -output -tvm diff --git a/predict/module/tvm_kernel/.gitmodules b/predict/module/tvm_kernel/.gitmodules deleted file mode 100644 index 8a987193a4..0000000000 --- a/predict/module/tvm_kernel/.gitmodules +++ /dev/null @@ -1,4 +0,0 @@ -[submodule "3rdparty/incubator-tvm"] - path = 3rdparty/incubator-tvm - url = https://github.com/dmlc/tvm.git - branch = v0.5 diff --git a/predict/module/tvm_kernel/CMakeLists.txt b/predict/module/tvm_kernel/CMakeLists.txt deleted file mode 100755 index b99e257c3e..0000000000 --- a/predict/module/tvm_kernel/CMakeLists.txt +++ /dev/null @@ -1,25 +0,0 @@ -cmake_minimum_required(VERSION 3.12.1) -project(autotensor LANGUAGES CXX) -set (MINDSPORE "${PROJECT_SOURCE_DIR}/../../..") -set (TVM_KERNEL_LITE "${PROJECT_SOURCE_DIR}/lite") -set (THIRDPARTY "${MINDSPORE}/third_party") -set (TVM_CLEAN_SOURCE "${THIRDPARTY}/incubator-tvm") -set (TVM_BUILD_SOURCE "${PROJECT_SOURCE_DIR}/incubator-tvm") -set (BUILD_DIR "${PROJECT_SOURCE_DIR}") -set (TVM_KERNEL_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}) -set (TVM_OUTPUT_DIR ${TVM_KERNEL_OUTPUT_DIR}/incubator-tvm) - -set (LLVM_CONFIG $ENV{LLVM_PATH}) -if (NOT LLVM_CONFIG) - message(FATAL_ERROR "please set LLVM_PATH in env") -endif() -set (CMAKE_BUILD_TYPE "Release") - -include(${TVM_BUILD_SOURCE}/cmake/util/Util.cmake) -include(${TVM_BUILD_SOURCE}/cmake/util/FindLLVM.cmake) -if(EXISTS ${TVM_BUILD_SOURCE}/cmake/config.cmake) - include(${TVM_BUILD_SOURCE}/cmake/config.cmake) -endif() -add_subdirectory(${TVM_KERNEL_LITE}) -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) - diff --git a/predict/module/tvm_kernel/lite/CMakeLists.txt b/predict/module/tvm_kernel/lite/CMakeLists.txt deleted file mode 100755 index 50e2bf5c9b..0000000000 --- a/predict/module/tvm_kernel/lite/CMakeLists.txt +++ /dev/null @@ -1,140 +0,0 @@ -cmake_minimum_required(VERSION 3.12) -set(CMAKE_CXX_STANDARD 14) - -if(ENABLE_PREDICT_ARM64) - set(TARGS "arm64") -elseif(ENABLE_PREDICT_ARM32) - set(TARGS "arm32") -else() - set(TARGS "x86") -endif() -message("TARGET is set to ${TARGS}") - -set(CMAKE_VERBOSE_MAKEFILE ON) -set(CMAKE_SKIP_RPATH TRUE) - -if(MSVC) - message("not support MSVC") -else(MSVC) - include(CheckCXXCompilerFlag) - check_cxx_compiler_flag("-std=c++11" SUPPORT_CXX11) - if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") - message("Build in Debug mode") - set(CMAKE_C_FLAGS "-O0 -g -Wall -Werror -fPIC [${CMAKE_C_FLAGS} -rdynamic") - set(CMAKE_CXX_FLAGS "-O0 -g -Wall -Werror -fPIC -std=c++11 ${CMAKE_CXX_FLAGS} -rdynamic") - else() - set(CMAKE_C_FLAGS "-D_FORTIFY_SOURCE=2 -O2 -fno-rtti -fvisibility=hidden -Wall -Werror -fPIC -fstack-protector-strong ${CMAKE_C_FLAGS}") - set(CMAKE_CXX_FLAGS "-D_FORTIFY_SOURCE=2 -O2 -fno-rtti -fvisibility=hidden -Wall -Werror -fPIC -fstack-protector-strong -std=c++11 ${CMAKE_CXX_FLAGS}") - set(CMAKE_EXE_LINKER_FLAGS "-Wl,-z,relro,-z,now -Wl,-z,noexecstack") - endif () - if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND - CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0) - set(CMAKE_CXX_FLAGS "-Wall -Werror -faligned-new ${CMAKE_CXX_FLAGS}") - endif() - if (CODE_COVERAGE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -fprofile-arcs -ftest-coverage -O0") - endif() -endif(MSVC) - - -if("${TARGS}" STREQUAL "x86") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D__x86_64__ -fno-strict-aliasing") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__x86_64__ -fno-strict-aliasing") -endif() - - -set(PRJ_SRC_DIR "${PROJECT_SOURCE_DIR}") -set(PRJ_KLIB_DIR "${PROJECT_SOURCE_DIR}") -set(PRJ_LITE_DIR "${PROJECT_SOURCE_DIR}/lite") - -# include directories -message("current PRJ DIR: ${PROJECT_SOURCE_DIR}") -message("current SUB_PRJ DIR: ${PRJ_SRC_DIR}") -message("current KLIB DIR: ${PRJ_KLIB_DIR}") -message("current PRJ_LITE_DIR: ${PRJ_LITE_DIR}") -message("CMAKE_CURRENT_BINARY_DIR: ${CMAKE_CURRENT_BINARY_DIR}") -set(DMLC_CORE "${TVM_BUILD_SOURCE}/3rdparty/dmlc-core") -set(DLPACK "${TVM_BUILD_SOURCE}/3rdparty/dlpack") -set(PREDICT "${PRJ_SRC_DIR}/../../") -set(SECUREC "${PRJ_SRC_DIR}/../../../third_party/securec") -message("include dir: ${DLPACK}/include") -include_directories(${DLPACK}/include) -include_directories(${DMLC_CORE}/include) -include_directories(${TVM_BUILD_SOURCE}/include) -include_directories(${TVM_BUILD_SOURCE}/src/pass) -include_directories(${PRJ_LITE_DIR}) -include_directories(${PRJ_LITE_DIR}/include) -include_directories(${PRJ_LITE_DIR}/../../..) -include_directories(${PRJ_LITE_DIR}/../../../include) -include_directories(${PRJ_LITE_DIR}/../../../src/runtime) -include_directories(${PRJ_LITE_DIR}/../../../common) -include_directories(${SECUREC}) -message("SECUREC: " "${SECUREC}/build/src") -include_directories(${PREDICT}) -include_directories(${PREDICT}/src) -include_directories(${PRJ_SRC_DIR}/../../../third_party/flatbuffers/include) -include_directories(${PRJ_SRC_DIR}/../../../third_party) -# Source file lists -file(GLOB_RECURSE TVM_KERNEL_SRC - src/api/*.cc - src/tflite/TFLite_Detection_PostProcess.cc) - -set (TVM_RUNTIME_FLG $ENV{TVM_RUNTIME_ON}) -if ("${TVM_RUNTIME_FLG}" STREQUAL "true") - message("Using TVM runtime function") - file(GLOB TVM_RUNTIME_SRCS - ${TVM_ROOT}/apps/howto_deploy/tvm_runtime_pack.cc) -else() - message("Using LITE runtime function") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DLITE_RUNTIME_ON -DTVM_RUNTIME_HEADER_ONLY -DLITE_THREAD_POOL_SHARED") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DLITE_RUNTIME_ON -DTVM_RUNTIME_HEADER_ONLY -DLITE_THREAD_POOL_SHARED") - file(GLOB_RECURSE TVM_RUNTIME_SRCS - ${PREDICT}/src/runtime/*.cc) -endif() - -if("${TARGS}" STREQUAL "arm32" OR "${TARGS}" STREQUAL "arm64") - set(CMAKE_SKIP_BUILD_RPATH TRUE) - set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) - set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) -endif() - -set(LIB_X86_PATH "${PRJ_KLIB_DIR}/build/lib_x86") -set(LIB_ARM64_PATH "${PRJ_KLIB_DIR}/build/lib_arm64") -set(LIB_ARM32_PATH "${PRJ_KLIB_DIR}/build/lib_arm32") -if("${TARGS}" STREQUAL "x86") - set(KLIBS_PATH "${LIB_X86_PATH}") -elseif("${TARGS}" STREQUAL "arm64") - set(KLIBS_PATH "${LIB_ARM64_PATH}") -elseif("${TARGS}" STREQUAL "arm32") - set(KLIBS_PATH "${LIB_ARM32_PATH}") -else() - message(ERROR " not suport ${TARGS}") -endif() - -file(GLOB_RECURSE KERNEL_LIBS "${KLIBS_PATH}/*.o") -message("KERNEL_PATH= ${KLIBS_PATH}") - -add_compile_options(-DTVM_CUDA_RUNTIM=0) -add_compile_options(-DTVM_METAL_RUNTIM=0) -add_compile_options(-DTVM_OPENCL_RUNTIM=0) - -link_directories(${KLIBS_PATH}) - -add_library(tvm_runtime_pack STATIC ${TVM_RUNTIME_SRCS}) -add_library(kernel_manager STATIC ${TVM_KERNEL_SRC}) -add_library(tvm_kernel_static STATIC ${TVM_KERNEL_SRC} ${KERNEL_LIBS}) -add_library(tvm_kernel SHARED ${TVM_KERNEL_SRC} ${KERNEL_LIBS}) -set_target_properties(tvm_kernel PROPERTIES LINK_FLAGS "-Wl,-z,relro,-z,now -Wl,-z,noexecstack") - -set(KERNEL_LD_LIB tvm_runtime_pack dl) - -if("${TARGS}" STREQUAL "x86") - set(KERNEL_LD_LIB ${KERNEL_LD_LIB} pthread) -else() - set(ANDROID_ALLOW_UNDEFINED_SYMBOLS TRUE) -endif() - -target_link_libraries(tvm_kernel ${KERNEL_LD_LIB} libsecurec.a) -target_link_libraries(tvm_kernel_static OBJECT tvm_runtime_pack libsecurec.a) - -add_dependencies(tvm_kernel securec) diff --git a/predict/module/tvm_kernel/lite/include/lite/api/km_api.h b/predict/module/tvm_kernel/lite/include/lite/api/km_api.h deleted file mode 100644 index 7ccd4964cb..0000000000 --- a/predict/module/tvm_kernel/lite/include/lite/api/km_api.h +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this ${file} except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef PREDICT_MODULE_TVM_KERNEL_LITE_INCLUDE_LITE_API_KM_API_H_ -#define PREDICT_MODULE_TVM_KERNEL_LITE_INCLUDE_LITE_API_KM_API_H_ - -#include -#include -#include -#include -#include "schema/inner/ms_generated.h" -#include "schema/inner/op_generated.h" - -#define PUBLIC __attribute__((visibility("default"))) - -/*! - * \brief Call tvm kernel. - * \param fid tvm kernel id. - * \param tensors tvm kernel arguments. - * \return 0 if SUCCESS. - */ -PUBLIC int CallKernel(const std::string &fid, const std::vector &tensors); - -/*! - * \brief Get tvm kernel by id. - * \param fid tvm kernel id. - * \return std::function if SUCCESS else nullptr. - */ -PUBLIC std::function &)> GetKernel(const std::string &fid); - -/*! - * \brief Get tvm kernel by OpDef. - * \param opdef defined by predict schema. - * \param tensors. - * \param option. - * \return std::function if SUCCESS else nullptr. - */ -struct PUBLIC KernelOption { - int numThreads = 0; - std::string device; -}; - -PUBLIC std::function &)> GetKernel(const mindspore::predict::OpDef &opdef, - const std::vector &tensors, - const KernelOption &option); - -/*! - * \brief load TVM Kernel lib - * \param mode 0 indicate shared lib - * \param fname shared lib path when mode equals 0 - * \return 0 if SUCCESS - */ -PUBLIC void InitKernelManager(int mode, const std::string &fname); - -/* - * \brief config ThreadPool using mode - * \param mode: -1 using mid speed cpu first, 1 using higher speed cpu first - * \param nthreads: threads num to be used, can't exceed cpu num - * if mode==-1 bind mid cpu first - * if mode==1 bind higher cpu first - * if mode==0 no bind - * \param execute_self: cur thread do arithmetic or not - * execute_self: true cur thread do arithmetic work - * execute_self: false cur thread not do arithmetic work - */ -PUBLIC void ConfigThreadPool(int mode = -1, int nthreads = 2, bool execute_self = true); - -/* - * \brief provid simple api for mslite, mslite not care mode - */ -inline void CfgThreadPool(int nthread) { ConfigThreadPool(-1, nthread, true); } - -/* - * the Callback function to do cpu bind for master thread. - */ -PUBLIC void DoMasterThreadBind(bool bindflg); - -PUBLIC void DoAllThreadBind(bool ifBind); - -#undef PUBLIC - -#endif // PREDICT_MODULE_TVM_KERNEL_LITE_INCLUDE_LITE_API_KM_API_H_ diff --git a/predict/module/tvm_kernel/lite/python/__init__.py b/predict/module/tvm_kernel/lite/python/__init__.py deleted file mode 100644 index 5a51943fbe..0000000000 --- a/predict/module/tvm_kernel/lite/python/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2019 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================ -"""Neural network operators""" -# from . import arm_cpu -# from . import at_ops diff --git a/predict/module/tvm_kernel/lite/python/arm_cpu/__init__.py b/predict/module/tvm_kernel/lite/python/arm_cpu/__init__.py deleted file mode 100644 index dce9d5e96c..0000000000 --- a/predict/module/tvm_kernel/lite/python/arm_cpu/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2019 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================ -"""Schedule for ARM CPU""" - -from . import conv2d diff --git a/predict/module/tvm_kernel/lite/python/arm_cpu/conv2d.py b/predict/module/tvm_kernel/lite/python/arm_cpu/conv2d.py deleted file mode 100644 index ded792f689..0000000000 --- a/predict/module/tvm_kernel/lite/python/arm_cpu/conv2d.py +++ /dev/null @@ -1,470 +0,0 @@ -# Copyright 2019 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================ -"""Conv2D schedule for ARM CPU""" -from __future__ import absolute_import as _abs - -import functools - -import tvm -from tvm import autotvm -import tvm.contrib.nnpack - -from topi.generic import schedule_conv2d_nchw -from topi.util import traverse_inline, get_const_tuple -from topi.nn import pad, conv2d -from topi.nn.util import get_const_int, get_pad_tuple - - -@autotvm.register_topi_compute(conv2d, "arm_cpu", ["asm"]) -def conv2d_arm_cpu(cfg, data, kernel, strides, padding, dilation, out_dtype): - """TOPI compute callback for conv2d - - Parameters - ---------- - cfg: ConfigEntity - The config for this template - - data : tvm.Tensor - 4-D with shape [batch, in_channel, in_height, in_width] - - kernel : tvm.Tensor - 4-D with shape [num_filter, in_channel, filter_height, filter_width] or - pre-packed 5-D with shape [num_filter_chunk, in_channel, filter_height, - filter_width, num_filter_block] - - strides : list of two ints - [stride_height, stride_width] - - padding : list of two ints - [pad_height, pad_width] - - dilation : list of two ints - [dilation_height, dilation_width] - - out_dtype: str - The output type. This is used for mixed precision. - - Returns - ------- - output : tvm.Tensor - 4-D with shape [batch, out_channel, out_height, out_width] - """ - args = _gen_cfg(cfg, data, kernel, strides, padding, dilation, num_tile=2) - return _conv_spatial_pack_asm( - args, data, kernel, strides, padding, dilation, out_dtype - ) - - -@autotvm.register_topi_schedule(schedule_conv2d_nchw, "arm_cpu", ["asm"]) -def schedule_conv2d_nchw_arm_cpu(outs): - """TOPI schedule callback for conv2d - - Parameters - ---------- - outs: Array of Tensor - The computation graph description of conv2d - in the format of an array of tensors. - - Returns - ------- - s: Schedule - The computation schedule for conv2d. - """ - s = _conv_schedule_asm(outs) - return s - - -def _gen_cfg(cfg, data, kernel, strides, padding, dilation, num_tile): - """_gen_cfg""" - if len(kernel.shape) == 4: - co_, _, kh_, kw_ = get_const_tuple(kernel.shape) - else: # kernel tensor is pre packed - co_, _, kh_, kw_, vc_ = get_const_tuple(kernel.shape) - co_ = co_ * vc_ - - if isinstance(dilation, int): - dilation_h = dilation_w = dilation - else: - dilation_h, dilation_w = dilation - - n_, ci_, ih_, iw_ = get_const_tuple(data.shape) - - dilated_kernel_h = (kh_ - 1) * dilation_h + 1 - dilated_kernel_w = (kw_ - 1) * dilation_w + 1 - pad_top, pad_left, pad_bottom, pad_right = get_pad_tuple( - padding, (dilated_kernel_h, dilated_kernel_w) - ) - hstr, wstr = strides if isinstance(strides, (tuple, list)) else (strides, strides) - oh_ = (ih_ + pad_top + pad_bottom - dilated_kernel_h) // hstr + 1 - ow_ = (iw_ + pad_left + pad_right - dilated_kernel_w) // wstr + 1 - - n, co, oh, ow = cfg.axis(n_), cfg.axis(co_), cfg.axis(oh_), cfg.axis(ow_) - ci, kh, kw = cfg.reduce_axis(ci_), cfg.reduce_axis(kh_), cfg.reduce_axis(kw_) - - if num_tile == 2: # for arm cpu - candidate_vc = [] - for iv in range(3, co_): - if co_ % iv == 0: - candidate_vc.append([co_ // iv, iv]) - candidate_vc.append([1, co_]) - co, vc = cfg.define_split( - "tile_co", co, num_outputs=2, policy="candidate", candidate=candidate_vc - ) - oh, vh = cfg.define_split("tile_oh", oh, num_outputs=2) - ow, vw = cfg.define_split("tile_ow", ow, num_outputs=2) - elif num_tile == 3: # for mali gpu - co, _, vc = cfg.define_split("tile_co", co, num_outputs=3) - oh, _, vh = cfg.define_split("tile_oh", oh, num_outputs=3) - ow, _, vw = cfg.define_split("tile_ow", ow, num_outputs=3) - else: - raise RuntimeError("Invalid num_tile") - - cfg.define_reorder( - "reorder_0", - [n, co, oh, ow, ci, kh, kw, vh, vw, vc], - policy="candidate", - candidate=[[n, co, oh, ow, ci, kh, kw, vh, vw, vc],], - ) - - vc_ = cfg["tile_co"].size[-1] - vh_ = cfg["tile_oh"].size[-1] - vw_ = cfg["tile_ow"].size[-1] - is_var = False - return (is_var, vh_, vw_, vc_) - -def _conv_spatial_pack_asm(args, data, kernel, strides, padding, - dilation, out_dtype): - """_conv_spatial_pack_asm""" - is_var, vh_, vw_, vc_ = args - - # create workload according to raw arguments - out_dtype = out_dtype or data.dtype - n_, ci_, ih_, iw_ = data.shape if is_var else get_const_tuple(data.shape) - - if isinstance(dilation, int): - dilation_h = dilation_w = dilation - else: - dilation_h, dilation_w = dilation - - if len(kernel.shape) == 4: - pre_packed = False - co_, _, kh_, kw_ = kernel.shape if is_var else get_const_tuple(kernel.shape) - else: # kernel tensor is pre packed - pre_packed = True - co_, _, kh_, kw_, vc_ = kernel.shape if is_var else get_const_tuple(kernel.shape) - co_ = co_ * vc_ - - dilated_kernel_h = (kh_ - 1) * dilation_h + 1 - dilated_kernel_w = (kw_ - 1) * dilation_w + 1 - pad_top, pad_left, pad_bottom, pad_right = get_pad_tuple( - padding, (dilated_kernel_h, dilated_kernel_w) - ) - hstr, wstr = strides if isinstance(strides, (tuple, list)) else (strides, strides) - oh_ = (ih_ + pad_top + pad_bottom - dilated_kernel_h) // hstr + 1 - ow_ = (iw_ + pad_left + pad_right - dilated_kernel_w) // wstr + 1 - data_pad = pad(data, [0, 0, pad_top, pad_left], [0, 0, pad_bottom, pad_right]) - - oh_div = oh_ // vh_ - ow_div = ow_ // vw_ - kvshape = (co_ // vc_, ci_, kh_, kw_, vc_) - ovshape = (n_, co_ // vc_, oh_div, ow_div, vh_, vw_, vc_) - oshape = (n_, co_, oh_div * vh_, ow_div * vw_) - - if dilation_h != 1 or dilation_w != 1: - # undilate input data - dvshape = (n_, oh_ // vh_, ow_ // vw_, kh_, kw_, vh_, vw_, ci_) - data_vec = tvm.compute( - dvshape, - lambda n, h, w, kh, kw, vh, vw, ci: data_pad[n][ci][ - (h * vh_ + vh) * hstr + kh * dilation_h - ][(w * vw_ + vw) * wstr + kw * dilation_w], - name="data_vec_undilated", - ) - else: - dvshape = ( - n_, - oh_ // vh_, - ow_ // vw_, - (vh_ - 1) * hstr + kh_, - (vw_ - 1) * wstr + kw_, - ci_, - ) - data_vec = tvm.compute( - dvshape, - lambda n, h, w, vh, vw, ci: data_pad[n][ci][h * vh_ * hstr + vh][ - w * vw_ * wstr + vw - ], - name="data_vec", - ) - - if pre_packed: - kernel_vec = kernel - else: - kernel_vec = tvm.compute( - kvshape, - lambda co, ci, kh, kw, vc: kernel[co * vc_ + vc][ci][kh][kw], - name="kernel_vec", - ) - - ci = tvm.reduce_axis((0, ci_), name="ci") - kh = tvm.reduce_axis((0, kh_), name="kh") - kw = tvm.reduce_axis((0, kw_), name="kw") - - # asm begin---- - type_map = { - "int8": "int32", - "uint8": "uint32", - "float32": "float32", - "float16": "float16", - } - acum_dtype = type_map[data.dtype] - attrs = { - "SH": hstr, - "SW": wstr, - "PH": pad_top, - "PW": pad_left, - "DILA_H": dilation_h, - "DILA_W": dilation_w, - "VH": vh_, - "VW": vw_, - "VC": vc_, - "ACUM_DTYPE": acum_dtype, - } - # asm end---- - - if dilation_h != 1 or dilation_w != 1: - conv = tvm.compute( - ovshape, - lambda n, co, h, w, vh, vw, vc: tvm.sum( - data_vec[n, h, w, kh, kw, vh, vw, ci].astype(out_dtype) - * kernel_vec[co, ci, kh, kw, vc].astype(out_dtype), - axis=[ci, kh, kw], - ), - name="conv", - attrs=attrs, - ) - else: - conv = tvm.compute( - ovshape, - lambda n, co, h, w, vh, vw, vc: tvm.sum( - data_vec[n, h, w, vh * hstr + kh, vw * wstr + kw, ci].astype(out_dtype) - * kernel_vec[co, ci, kh, kw, vc].astype(out_dtype), - axis=[ci, kh, kw], - ), - name="conv", - attrs=attrs, - ) - - output = tvm.compute( - oshape, - lambda n, co, h, w: conv[n][co // vc_][h // vh_][w // vw_][h % vh_][w % vw_][ - co % vc_ - ], - name="output_unpack", - tag="asm_conv2d_output", - ) - - return output - - -def intrin_conv(args): - """intrin_conv""" - ( - ci_, - vh_, - vw_, - vc_, - kh_, - kw_, - sh_, - sw_, - dila_h, - dila_w, - dtype, - acum_dtype, - opname, - core_id, - ) = args - hstr, wstr = sh_, sw_ - ci_ = tvm.var("ci_") if ci_ is None else ci_ - kvshape = (ci_, kh_, kw_, vc_) - ovshape = (vh_, vw_, vc_) - if dila_h != 1 or dila_w != 1: - dvshape = (kh_, kw_, vh_, vw_, ci_) - else: - dvshape = ((vh_ - 1) * hstr + kh_, (vw_ - 1) * wstr + kw_, ci_) - - data_vec = tvm.placeholder(dvshape, name="a", dtype=dtype) - kernel_vec = tvm.placeholder(kvshape, name="b", dtype=dtype) - ci = tvm.reduce_axis((0, ci_), name="ci") - kh = tvm.reduce_axis((0, kh_), name="kh") - kw = tvm.reduce_axis((0, kw_), name="kw") - if dila_h != 1 or dila_w != 1: - conv = tvm.compute( - ovshape, - lambda vh, vw, vc: tvm.sum( - data_vec[kh, kw, vh, vw, ci].astype(acum_dtype) - * kernel_vec[ci, kh, kw, vc].astype(acum_dtype), - axis=[ci, kh, kw], - ), - name="conv", - ) - else: - conv = tvm.compute( - ovshape, - lambda vh, vw, vc: tvm.sum( - data_vec[vh * hstr + kh, vw * wstr + kw, ci].astype(acum_dtype) - * kernel_vec[ci, kh, kw, vc].astype(acum_dtype), - axis=[ci, kh, kw], - ), - name="conv", - ) - - stride_a = [ - functools.reduce(lambda x, y: x * y, dvshape[i + 1: len(dvshape)]) - for i in range(0, len(dvshape) - 1) - ] - stride_a.append(1) - stride_b = [ - functools.reduce(lambda x, y: x * y, kvshape[i + 1: len(kvshape)]) - for i in range(0, len(kvshape) - 1) - ] - stride_b.append(1) - stride_c = [ - functools.reduce(lambda x, y: x * y, ovshape[i + 1: len(ovshape)]) - for i in range(0, len(ovshape) - 1) - ] - stride_c.append(1) - - a_buffer = tvm.decl_buffer( - data_vec.shape, data_vec.dtype, name="A", offset_factor=1, strides=stride_a - ) - b_buffer = tvm.decl_buffer( - kernel_vec.shape, kernel_vec.dtype, name="B", offset_factor=1, strides=stride_b - ) - c_buffer = tvm.decl_buffer( - conv.shape, conv.dtype, name="C", offset_factor=1, strides=stride_c - ) - - def intrin_func(ins, outs): - aa, bb = ins - cc = outs[0] - - def _body(): - ib = tvm.ir_builder.create() - ib.emit( - tvm.call_extern( - "int32", - opname, - cc.access_ptr("w"), - aa.access_ptr("r"), - bb.access_ptr("r"), - ci_, - vh_, - vw_, - vc_, - kh_, - sh_, - core_id, - ) - ) - return ib.get() - - return _body() - - return tvm.decl_tensor_intrin( - conv.op, intrin_func, binds={data_vec: a_buffer, kernel_vec: b_buffer, conv: c_buffer} - ) - - -def _schedule_asm(s, data_vec, kernel_vec, conv, output, last): - """schedule implementation""" - n, co, oh, ow, vh, vw, vc = s[conv].op.axis - - axis_extent = [] - for i in (vh, vw, vc): - axis_extent.append(get_const_int(i.dom.extent)) - reduce_extent = [] - for i in s[conv].op.reduce_axis[1:]: - reduce_extent.append(get_const_int(i.dom.extent)) - vh_, vw_, vc_ = axis_extent - - # schedule fusion - n, co, h, w = s[last].op.axis - co, vc = s[last].split(co, vc_) - oh, vh = s[last].split(h, vh_) - ow, vw = s[last].split(w, vw_) - s[last].reorder(n, co, oh, ow, vh, vw, vc) - if last != output: - s[output].compute_inline() - - s[conv].compute_at(s[last], ow) - - # mark parallel - s[last].parallel(co) - - if data_vec.op.name == "data_vec_undilated": - _, h, _, _, _, _, _, _ = s[data_vec].op.axis - else: - _, h, _, _, _, _ = s[data_vec].op.axis - s[data_vec].parallel(h) - - if kernel_vec.op.name == "kernel_vec": - co, _, _, _, _ = s[kernel_vec].op.axis - if autotvm.GLOBAL_SCOPE.in_tuning: - # kernel packing will be pre-computed during compilation, so we skip - # this part to make tuning records correct - s[kernel_vec].pragma(co, "debug_skip_region") - else: - s[kernel_vec].parallel(co) - elif kernel_vec.op.name == "kernel_vec_conv2d_transpose": # for conv2d transpose - co, _, _, _, _ = s[kernel_vec].op.axis - s[kernel_vec].parallel(co) - - return s - - -def _conv_schedule_asm(outs): - """_conv_schedule_asm""" - s = tvm.create_schedule([x.op for x in outs]) - - def _callback(op): - if "asm_conv2d_output" in op.tag: - # schedule conv2d - output = op.output(0) - conv = op.input_tensors[0] - - sidx = 0 - if conv.op.input_tensors[0].name == "attr": - sidx = 1 - data_vec = conv.op.input_tensors[sidx] - data_pad = data_vec.op.input_tensors[0] - s[data_pad].compute_inline() - - kernel_vec = conv.op.input_tensors[sidx + 1] - if kernel_vec.op.name == "kernel_vec": - kernel = kernel_vec.op.input_tensors[0] - else: - kernel = kernel_vec - if (isinstance(kernel.op, tvm.tensor.ComputeOp) and "dilate" in kernel.op.tag): - s[kernel].compute_inline() - - if conv.op.input_tensors[0].name == "attr": - _schedule_asm(s, data_vec, kernel_vec, conv, output, outs[0]) - else: - _schedule_asm(s, data_vec, kernel_vec, conv, output, outs[0]) - - traverse_inline(s, outs[0].op, _callback) - return s diff --git a/predict/module/tvm_kernel/lite/python/arm_cpu/deconv.py b/predict/module/tvm_kernel/lite/python/arm_cpu/deconv.py deleted file mode 100644 index 4ed29900a2..0000000000 --- a/predict/module/tvm_kernel/lite/python/arm_cpu/deconv.py +++ /dev/null @@ -1,477 +0,0 @@ -# Copyright 2019 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================ -"""Conv2D_transpose of stride=2, kernel=2*2 schedule for ARM CPU""" -from __future__ import absolute_import as _abs - -import functools - -import tvm -from tvm import autotvm -import tvm.contrib.nnpack - -from topi.generic import schedule_conv2d_nchw -from topi.util import traverse_inline, get_const_tuple -from topi.nn import conv2d - - -@autotvm.register_topi_compute(conv2d, "arm_cpu", ["deconv"]) -def conv2d_arm_cpu_deconv(cfg, data, kernel, out_dtype): - """TOPI compute callback for conv2d - - Parameters - ---------- - cfg: ConfigEntity - The config for this template - - data : tvm.Tensor - 4-D with shape [batch, in_channel, in_height, in_width] - - kernel : tvm.Tensor - 4-D with shape [num_filter, in_channel, filter_height, filter_width] or - pre-packed 5-D with shape [num_filter_chunk, in_channel, filter_height, - filter_width, num_filter_block] - - out_dtype: str - The output type. This is used for mixed precision. - - Returns - ------- - output : tvm.Tensor - 4-D with shape [batch, out_channel, out_height, out_width] - """ - args = _gen_cfg_deconv(cfg, data, kernel, num_tile=2) - return _conv_spatial_pack_deconv( - args, data, kernel, out_dtype - ) - - -@autotvm.register_topi_schedule(schedule_conv2d_nchw, "arm_cpu", ["deconv"]) -def schedule_conv2d_nchw_arm_cpu_deconv(cfg, outs): - """TOPI schedule callback for conv2d - - Parameters - ---------- - cfg: ConfigEntity - The config for this template - - outs: Array of Tensor - The computation graph description of conv2d - in the format of an array of tensors. - - Returns - ------- - s: Schedule - The computation schedule for conv2d. - """ - s = _conv_schedule_deconv(cfg, outs) - return s - - -def _gen_cfg_deconv(cfg, data, kernel, num_tile): - """generation config from input args""" - if len(kernel.shape) == 4: - co_, _, _, _ = get_const_tuple(kernel.shape) - else: # kernel tensor is pre packed - co_, _, _, _, vc_ = get_const_tuple(kernel.shape) - co_ = co_ * vc_ - - if len(data.shape) == 4: - _, ci_, ih_, iw_ = get_const_tuple(data.shape) - c4 = 4 - ci_ = ci_ // 4 - else: - _, ci_, ih_, iw_, c4 = get_const_tuple(data.shape) - - oh_ = ih_ * 2 - ow_ = iw_ * 2 - - co, oh, ow = cfg.axis(co_), cfg.axis(oh_), cfg.axis(ow_) - ci, ki = cfg.reduce_axis(ci_), cfg.reduce_axis(c4) - - if num_tile == 2: # for arm cpu - candidate_vc = [[co_ // c4, c4]] - co, vc = cfg.define_split( - "tile_co", co, num_outputs=2, policy="candidate", candidate=candidate_vc - ) - candidate_vw = [] - for iv in range(4, ow_ + 1): # [4, 6, 8, 12, 16, 24, 32, 40]: - if iv % 4 == 0 and (ow_ % iv == 0): - candidate_vw.append([ow_ // iv, iv]) - ow, vw = cfg.define_split( - "tile_ow", ow, num_outputs=2, policy="candidate", candidate=candidate_vw - ) - candidate_vh = [[1, 2]] - oh, vh = cfg.define_split( - "tile_oh", oh, num_outputs=2, policy="candidate", candidate=candidate_vh - ) - elif num_tile == 3: # for mali gpu - co, _, vc = cfg.define_split("tile_co", co, num_outputs=3) - oh, _, vh = cfg.define_split("tile_oh", oh, num_outputs=3) - ow, _, vw = cfg.define_split("tile_ow", ow, num_outputs=3) - else: - raise RuntimeError("Invalid num_tile") - - cfg.define_annotate("ann_reduce", [ci, ki], policy="try_unroll") - cfg.define_annotate("ann_spatial", [vh, vw, vc], policy="try_unroll_vec") - - vc_ = cfg["tile_co"].size[-1] - vh_ = cfg["tile_oh"].size[-1] - vw_ = cfg["tile_ow"].size[-1] - is_var = False - return (is_var, vh_, vw_, vc_) - - -def _conv_spatial_pack_deconv(args, data, kernel, out_dtype): - """conv2d_arm_cpu_deconv inner implement""" - is_var, vh_, vw_, vc_ = args - # create workload according to raw arguments - out_dtype = out_dtype or data.dtype - if len(data.shape) == 4: - n_, ci_, ih_, iw_ = data.shape if is_var else get_const_tuple(data.shape) - c4 = 4 - ci_ = ci_ // c4 - else: - n_, ci_, ih_, iw_, c4 = data.shape if is_var else get_const_tuple(data.shape) - - if len(kernel.shape) == 4: - pre_packed = False - _, co_, kh_, kw_ = kernel.shape if is_var else get_const_tuple(kernel.shape) - else: # kernel tensor is pre packed - pre_packed = True - _, co_, kh_, kw_, vc_ = kernel.shape if is_var else get_const_tuple(kernel.shape) - co_ = co_ * c4 - - oh_ = ih_ * 2 - ow_ = iw_ * 2 - ow_div = ow_ // vw_ - oh_div = oh_ // vh_ - kvshape = (co_ // vc_, kh_, kw_, ci_, c4, c4) - ovshape = (n_, co_ // vc_, oh_div, ow_div, vh_, vw_, c4) - - dvshape = (n_, ih_ // (vh_ // 2), iw_ // (vw_ // 2), vh_ // 2, ci_, vw_ // 2, c4) - if len(data.shape) == 4: - data_vec = tvm.compute( - dvshape, - lambda n, h, w, vh, ci, vw, ki: data[n][ci * c4 + ki][h * vh_ // 2 + vh][ - w * vw_ // 2 + vw - ], - name="data_vec", - ) - else: - data_vec = tvm.compute( - dvshape, - lambda n, h, w, vh, ci, vw, ki: data[n][ci][h * vh_ // 2 + vh][ - w * vw_ // 2 + vw - ][ki], - name="data_vec", - ) - - if pre_packed: - kernel_vec = kernel - else: - kernel_vec = tvm.compute( - kvshape, - lambda co, kh, kw, ci, ki, vc: kernel[ci * c4 + ki][co * vc_ + vc][kh][kw], - name="kernel_vec", - ) - - ci = tvm.reduce_axis((0, ci_), name="ci") - ki = tvm.reduce_axis((0, c4), name="ki") - - type_map = { - "int8": "int32", - "uint8": "uint32", - "float32": "float32", - "float16": "float16", - } - acum_dtype = type_map[data.dtype] - attrs = { - "SH": 2, - "SW": 2, - "PH": 0, - "PW": 0, - "DILA_H": 1, - "DILA_W": 1, - "VH": vh_, - "VW": vw_, - "VC": vc_, - "ACUM_DTYPE": acum_dtype, - } - - conv = tvm.compute( - ovshape, - lambda n, co, h, w, vh, vw, vc: tvm.sum( - data_vec[n, h, w, vh // 2, ci, vw // 2, ki].astype(out_dtype) - * kernel_vec[co, (h * vh_ + vh) % 2, (w * vw_ + vw) % 2, ci, ki, vc].astype( - out_dtype - ), - axis=[ci, ki], - ), - name="conv", - attrs=attrs, - ) - if len(data.shape) == 4: - osshape = (n_, co_, oh_, ow_div * vw_) - output = tvm.compute( - osshape, - lambda n, co, h, w: conv[n][co // c4][h][w // vw_][w % vw_][co % c4], - name="output_unpack", - tag="deconv_conv2d_output", - ) - else: - osshape = (n_, co_ // c4, oh_, ow_div * vw_, c4) - output = tvm.compute( - osshape, - lambda n, co, h, w, vc: conv[n][co][h // vh_][w // vw_][h % vh_][w % vw_][vc], - name="output_unpack", - tag="deconv_conv2d_output", - ) - - return output - - -def intrin_deconv(args): - """deconv inner implement""" - ( - ci_, - vh_, - vw_, - vc_, - kh_, - kw_, - sh_, - sw_, - dila_h, - dila_w, - dtype, - acum_dtype, - opname, - core_id, - ) = args - hstr, wstr = sh_, sw_ - ci_ = tvm.var("ci_") if ci_ is None else ci_ - kvshape = (ci_, kh_, kw_, vc_) - ovshape = (vh_, vw_, vc_) - if dila_h != 1 or dila_w != 1: - dvshape = (kh_, kw_, vh_, vw_, ci_) - else: - dvshape = ((vh_ - 1) * hstr + kh_, (vw_ - 1) * wstr + kw_, ci_) - - data_vec = tvm.placeholder(dvshape, name="a", dtype=dtype) - kernel_vec = tvm.placeholder(kvshape, name="b", dtype=dtype) - ci = tvm.reduce_axis((0, ci_), name="ci") - kh = tvm.reduce_axis((0, kh_), name="kh") - kw = tvm.reduce_axis((0, kw_), name="kw") - if DILA_H != 1 or dila_w != 1: - conv = tvm.compute( - ovshape, - lambda vh, vw, vc: tvm.sum( - data_vec[kh, kw, vh, vw, ci].astype(acum_dtype) - * kernel_vec[ci, kh, kw, vc].astype(acum_dtype), - axis=[ci, kh, kw], - ), - name="conv", - ) - else: - conv = tvm.compute( - ovshape, - lambda vh, vw, vc: tvm.sum( - data_vec[vh * hstr + kh, vw * wstr + kw, ci].astype(acum_dtype) - * kernel_vec[ci, kh, kw, vc].astype(acum_dtype), - axis=[ci, kh, kw], - ), - name="conv", - ) - - stride_a = [ - functools.reduce(lambda x, y: x * y, dvshape[i + 1: len(dvshape)]) - for i in range(0, len(dvshape) - 1) - ] - stride_a.append(1) - stride_b = [ - functools.reduce(lambda x, y: x * y, kvshape[i + 1: len(kvshape)]) - for i in range(0, len(kvshape) - 1) - ] - stride_b.append(1) - stride_c = [ - functools.reduce(lambda x, y: x * y, ovshape[i + 1: len(ovshape)]) - for i in range(0, len(ovshape) - 1) - ] - stride_c.append(1) - - a_buffer = tvm.decl_buffer( - data_vec.shape, data_vec.dtype, name="A", offset_factor=1, strides=stride_a - ) - b_buffer = tvm.decl_buffer( - kernel_vec.shape, kernel_vec.dtype, name="B", offset_factor=1, strides=stride_b - ) - c_buffer = tvm.decl_buffer( - conv.shape, conv.dtype, name="C", offset_factor=1, strides=stride_c - ) - - def intrin_func(ins, outs): - aa, bb = ins - cc = outs[0] - - def _body(): - ib = tvm.ir_builder.create() - ib.emit( - tvm.call_extern( - "int32", - opname, - cc.access_ptr("w"), - aa.access_ptr("r"), - bb.access_ptr("r"), - ci_, - vh_, - vw_, - vc_, - kh_, - sh_, - core_id, - ) - ) - return ib.get() - - return _body() - - return tvm.decl_tensor_intrin( - conv.op, intrin_func, binds={data_vec: a_buffer, kernel_vec: b_buffer, conv: c_buffer} - ) - - -def _schedule_deconv(cfg, s, data_vec, kernel_vec, conv, output, last): - """schedule implementation""" - is_tune = bool(isinstance(cfg, (tvm.autotvm.ConfigEntity, tvm.autotvm.ConfigSpace))) - if is_tune: - vh_ = cfg["tile_oh"].size[-1] - vw_ = cfg["tile_ow"].size[-1] - vc_ = cfg["tile_co"].size[-1] - cfg = { - "ci_": tvm.var("ci_"), - "VH": vh_, - "VW": vw_, - "VC": vc_, - "tile_oh": vh_, - "tile_ow": vw_, - "tile_co": vc_, - "tile_ci": 4, - "ann_reduce": cfg["ann_reduce"].anns, - "ann_spatial": cfg["ann_spatial"].anns, - } # ,'reorder_0':cfg['reorder_0'].perm} - else: - pass - n, co, oh, ow, vh, vw, vc = s[conv].op.axis - ci, ki = s[conv].op.reduce_axis - s[conv].reorder(n, co, oh, ow, ci, vw, ki, vc) - if cfg["ann_reduce"][0] == "unroll": - s[conv].unroll(ci) - elif cfg["ann_reduce"][0] == "vec": - s[conv].vectorize(ci) - if cfg["ann_reduce"][1] == "unroll": - s[conv].unroll(ki) - elif cfg["ann_reduce"][1] == "vec": - s[conv].vectorize(ki) - if cfg["ann_spatial"][0] == "vec": - s[conv].vectorize(vh) - elif cfg["ann_spatial"][0] == "unroll": - s[conv].unroll(vh) - if cfg["ann_spatial"][1] == "vec": - s[conv].vectorize(vw) - elif cfg["ann_spatial"][1] == "unroll": - s[conv].unroll(vw) - if cfg["ann_spatial"][2] == "vec": - s[conv].vectorize(vc) - elif cfg["ann_spatial"][2] == "unroll": - s[conv].unroll(vc) - - # schedule conv - attrs = conv.op.attrs - vh_, vw_, vc_ = (attrs["VH"].value, attrs["VW"].value, attrs["VC"].value) - - # schedule fusion - if len(s[last].op.axis) == 4: - n, co, h, w = s[last].op.axis - co, vc = s[last].split(co, vc_) - ow, vw = s[last].split(w, vw_) - oh, vh = s[last].split(h, vh_) - s[last].reorder(n, co, oh, ow, vh, vw, vc) - else: - n, co, h, w, vc = s[last].op.axis - oh, vh = s[last].split(h, vh_) - ow, vw = s[last].split(w, vw_) - s[last].reorder(n, co, oh, ow, vh, vw, vc) - if last != output and isinstance(output.op, tvm.tensor.ComputeOp): - s[output].compute_inline() - if cfg["ann_spatial"][0] == "vec": - s[last].vectorize(vh) - elif cfg["ann_spatial"][0] == "unroll": - s[last].unroll(vh) - if cfg["ann_spatial"][1] == "vec": - s[last].vectorize(vw) - elif cfg["ann_spatial"][1] == "unroll": - s[last].unroll(vw) - if cfg["ann_spatial"][2] == "vec": - s[last].vectorize(vc) - elif cfg["ann_spatial"][2] == "unroll": - s[last].unroll(vc) - - s[conv].compute_at(s[last], ow) - - # mark parallel - s[last].parallel(co) - - if data_vec.op.name == "data_vec_undilated": - _, h, _, _, _, _, _, _, _ = s[data_vec].op.axis - else: - _, h, _, _, _, _, _ = s[data_vec].op.axis - s[data_vec].parallel(h) - - co, _, _, _, _, vc = s[kernel_vec].op.axis - s[kernel_vec].parallel(co) - if cfg["ann_spatial"][2] == "vec": - s[kernel_vec].vectorize(vc) - elif cfg["ann_spatial"][2] == "unroll": - s[kernel_vec].unroll(vc) - return s - - -def _conv_schedule_deconv(cfg, outs): - """schedule_conv2d_nchw_arm_cpu_deconv inner implementation""" - s = tvm.create_schedule([x.op for x in outs]) - - def _callback(op): - if "deconv_conv2d_output" in op.tag: - # schedule conv2d - output = op.output(0) - conv = op.input_tensors[0] - - sidx = 0 - if conv.op.input_tensors[0].name == "attr": - sidx = 1 - data_vec = conv.op.input_tensors[sidx] - - kernel_vec = conv.op.input_tensors[sidx + 1] - if kernel_vec.op.name == "kernel_vec": - kernel = kernel_vec.op.input_tensors[0] - else: - kernel = kernel_vec - if (isinstance(kernel.op, tvm.tensor.ComputeOp) and "dilate" in kernel.op.tag): - s[kernel].compute_inline() - - _schedule_deconv(cfg, s, data_vec, kernel_vec, conv, output, outs[0]) - - traverse_inline(s, outs[0].op, _callback) - return s diff --git a/predict/module/tvm_kernel/lite/python/arm_cpu/depthwise_conv2d.py b/predict/module/tvm_kernel/lite/python/arm_cpu/depthwise_conv2d.py deleted file mode 100644 index f54076eb84..0000000000 --- a/predict/module/tvm_kernel/lite/python/arm_cpu/depthwise_conv2d.py +++ /dev/null @@ -1,289 +0,0 @@ -# Copyright 2019 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================ -"""Depthwise convolution schedule for ARM CPU""" - -import tvm -from tvm import autotvm - -from topi.generic import schedule_depthwise_conv2d_nchw -from topi.nn import depthwise_conv2d_nchw, pad -from topi.util import traverse_inline, get_const_tuple -from topi.nn.util import get_pad_tuple - -# register customized schedule for arm cpu. -@autotvm.register_topi_schedule( - schedule_depthwise_conv2d_nchw, ["arm_cpu", "cpu"], ["custom"] -) -def schedule_depthwise_conv2d_nchw_arm(cfg, outs): - """Schedule depthwise conv2d - - Parameters - ---------- - cfg: ConfigEntity - The configuration of this template - outs: Array of Tensor - The computation graph description of depthwise convolution2d - in the format of an array of tensors. - - Returns - ------- - s: Schedule - The computation schedule for depthwise_conv2d nchw. - """ - s = _depthwise_schedule_spatial_pack(cfg, outs) - return s - - -@autotvm.register_topi_compute(depthwise_conv2d_nchw, ["arm_cpu", "cpu"], ["custom"]) -def depthwise_conv2d_arm_cpu(cfg, data, kernel, strides, padding, dilation, out_dtype): - """TOPI compute callback for depthwise_conv2d nchw - - Parameters - ---------- - cfg: ConfigEntity - The config for this template - - data : tvm.Tensor - 4-D with shape [batch, in_channel, in_height, in_width] - - kernel : tvm.Tensor - 4-D with shape [num_filter, multiplier, filter_height, filter_width] or - pre-packed 5-D with shape [num_filter_chunk, multiplier, filter_height, - filter_width, num_filter_block] - - strides : list of two ints - [stride_height, stride_width] - - padding : list of two ints - [pad_height, pad_width] - - dilation : list of two ints - [dilation_height, dilation_width] - - out_dtype: str - The output type. This is used for mixed precision. - - Returns - ------- - output : tvm.Tensor - 4-D with shape [batch, out_channel, out_height, out_width] - """ - - return _depthwise_spatial_pack( - cfg, data, kernel, strides, padding, dilation, out_dtype - ) - - -def _depthwise_spatial_pack(args, data, kernel, strides, padding, dilation, out_dtype): - """depthwise_conv2d_arm_cpu's inner implement""" - is_var, u_vh, u_vw, u_vc = args - out_dtype = out_dtype or data.dtype - - u_n, u_c, ih, iw = data.shape if is_var else get_const_tuple(data.shape) - - if isinstance(dilation, int): - dilation_h = dilation_w = dilation - else: - dilation_h, dilation_w = dilation - - if len(kernel.shape) == 4: - pre_packed = False - u_c, um, ukh, ukw = kernel.shape if is_var else get_const_tuple(kernel.shape) - else: # kernel tensor is pre packed - pre_packed = True - u_c, um, ukh, ukw, u_vc = kernel.shape if is_var else get_const_tuple(kernel.shape) - u_c = u_c * u_vc - - dilated_kernel_h = (ukh - 1) * dilation_h + 1 - dilated_kernel_w = (ukw - 1) * dilation_w + 1 - - pad_top, pad_left, pad_down, pad_right = get_pad_tuple( - padding, (dilated_kernel_h, dilated_kernel_w) - ) - hstr, wstr = strides if isinstance(strides, (tuple, list)) else (strides, strides) - u_oh = (ih + pad_top + pad_down - dilated_kernel_h) // hstr + 1 - u_ow = (iw + pad_left + pad_right - dilated_kernel_w) // wstr + 1 - # pack data - hpad = pad_top + pad_down - wpad = pad_left + pad_right - dopad = hpad != 0 or wpad != 0 - if dopad: - data_pad = pad( - data, - (0, 0, pad_top, pad_left), - (0, 0, pad_down, pad_right), - name="data_pad", - ) - else: - data_pad = data - - oh_div = u_oh // u_vh - ow_div = u_ow // u_vw - kvshape = (u_c // u_vc, um, ukh, ukw, u_vc) - ovshape = (u_n, u_c * um // u_vc, oh_div, u_ow // u_vw, u_vh, u_vw, u_vc) - oshape = (u_n, u_c * um, oh_div * u_vh, ow_div * u_vw) - - if dilation_h != 1 or dilation_w != 1: - # undilate input data - dvshape = (u_n, oh_div, ow_div, u_c, ukh, ukw, u_vh, u_vw) - data_vec = tvm.compute( - dvshape, - lambda n, h, w, c, kh, kw, vh, vw: data_pad[n][c][ - (h * u_vh + vh) * hstr + kh * dilation_h - ][(w * u_vw + vw) * wstr + kw * dilation_w], - name="data_vec_undilated", - ) - else: - dvshape = (u_n, oh_div, ow_div, u_c, u_vh * hstr + ukh - 1, u_vw * wstr + ukw - 1) - data_vec = tvm.compute( - dvshape, - lambda n, h, w, c, vh, vw: data_pad[n][c][h * u_vh * hstr + vh][ - w * u_vw * wstr + vw - ], - name="data_vec", - ) - - if pre_packed: - kernel_vec = kernel - else: - kernel_vec = tvm.compute( - kvshape, - lambda co, m, kh, kw, vc: kernel[co * u_vc + vc][m][kh][kw], - name="kernel_vec", - ) - - kh = tvm.reduce_axis((0, ukh), name="kh") - kw = tvm.reduce_axis((0, ukw), name="kw") - - if dilation_h != 1 or dilation_w != 1: - conv = tvm.compute( - ovshape, - lambda n, co, h, w, vh, vw, vc: tvm.sum( - data_vec[n, h, w, (co * u_vc + vc) // um, kh, kw, vh, vw].astype(out_dtype) - * kernel_vec[co // um, co % um, kh, kw, vc].astype(out_dtype), - axis=[kh, kw], - ), - name="depthwise_conv", - ) - else: - conv = tvm.compute( - ovshape, - lambda n, co, h, w, vh, vw, vc: tvm.sum( - data_vec[ - n, h, w, (co * u_vc + vc) // um, vh * hstr + kh, vw * wstr + kw - ].astype(out_dtype) - * kernel_vec[co // um, co % um, kh, kw, vc].astype(out_dtype), - axis=[kh, kw], - ), - name="depthwise_conv", - ) - - output = tvm.compute( - oshape, - lambda n, co, h, w: conv[n][co // u_vc][h // u_vh][w // u_vw][h % u_vh][w % u_vw][ - co % u_vc - ], - name="output_unpack", - tag="spatial_depthwise_conv_nchw_output", - ) - return output - - -def _schedule_spatial_pack(cfg, s, data_vec, kernel_vec, conv, output, last): - """schedule implementation""" - u_vc = cfg["tile_co"].size[-1] if not isinstance(cfg, dict) else cfg["VC"] - u_vh = cfg["tile_oh"].size[-1] if not isinstance(cfg, dict) else cfg["VH"] - u_vw = cfg["tile_ow"].size[-1] if not isinstance(cfg, dict) else cfg["VW"] - - n, co, oh, ow, vh, vw, vc = s[conv].op.axis - kh, kw = s[conv].op.reduce_axis - - if data_vec.op.name == "data_vec_undilated": - _, _, dv_ow, _, _, _, _, _ = s[data_vec].op.axis - else: - _, _, dv_ow, _, _, _ = s[data_vec].op.axis - - data_pad = data_vec.op.input_tensors[0] - - if isinstance(data_pad.op, tvm.tensor.ComputeOp): - s[data_pad].vectorize(list(s[data_pad].op.axis)[-1]) - s[data_pad].compute_at(s[data_vec], dv_ow) - - s[data_vec].vectorize(list(s[data_vec].op.axis)[-1]) - s[data_vec].compute_at(s[conv], ow) - - # schedule conv - s[conv].reorder(n, co, oh, ow, kh, kw, vh, vw, vc) - s[conv].unroll(kh) - s[conv].unroll(vh) - s[conv].vectorize(vw) - s[conv].unroll(vc) - s[conv].parallel(co) - - n, co, h, w = s[last].op.axis - co, vc = s[last].split(co, u_vc) - oh, vh = s[last].split(h, u_vh) - ow, vw = s[last].split(w, u_vw) - if last != output: - s[output].compute_inline() - s[last].vectorize(vw) - s[last].unroll(vc) - else: - s[last].vectorize(vw) - s[conv].compute_at(s[last], oh) - - # mark parallel - s[last].parallel(co) - - if data_vec.op.name == "data_vec_undilated": - _, h, _, _, _, _, _, _ = s[data_vec].op.axis - else: - _, h, _, _, _, _ = s[data_vec].op.axis - s[data_vec].parallel(h) - - if kernel_vec.op.name == "kernel_vec": - co, _, _, _, _ = s[kernel_vec].op.axis - if autotvm.GLOBAL_SCOPE.in_tuning: - # kernel packing will be pre-computed during compliation, so we skip - # this part to make tuning records correct - s[kernel_vec].pragma(co, "debug_skip_region") - else: - s[kernel_vec].parallel(co) - - return s - - -def _depthwise_schedule_spatial_pack(cfg, outs): - """schedule_depthwise_conv2d_nchw_arm's inner implement""" - outs = [outs] if isinstance(outs, tvm.tensor.Tensor) else outs - s = tvm.create_schedule([x.op for x in outs]) - - def _callback(op): - if op.tag == "spatial_depthwise_conv_nchw_output": - output = op.output(0) - conv = op.input_tensors[0] - data_vec = conv.op.input_tensors[0] - kernel_vec = conv.op.input_tensors[1] - if kernel_vec.op.name == "kernel_vec": - kernel = kernel_vec.op.input_tensors[0] - else: - kernel = kernel_vec - if isinstance(kernel.op, tvm.tensor.ComputeOp) and "dilate" in kernel.op.tag: - s[kernel].compute_inline() - - _schedule_spatial_pack(cfg, s, data_vec, kernel_vec, conv, output, outs[0]) - - traverse_inline(s, outs[0].op, _callback) - return s diff --git a/predict/module/tvm_kernel/lite/python/arm_cpu/matmul.py b/predict/module/tvm_kernel/lite/python/arm_cpu/matmul.py deleted file mode 100644 index 6430f24f6f..0000000000 --- a/predict/module/tvm_kernel/lite/python/arm_cpu/matmul.py +++ /dev/null @@ -1,472 +0,0 @@ -# Copyright 2019 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================ -"""Conv2D schedule for ARM CPU""" -from __future__ import absolute_import as _abs - -import functools - -import tvm -from tvm import autotvm -import tvm.contrib.nnpack - -from topi.generic import schedule_conv2d_nchw -from topi.util import traverse_inline -from topi.nn import conv2d - - -@autotvm.register_topi_compute(conv2d, "arm_cpu", ["matmul"]) -def matmul_arm_cpu(cfg, a_, b_, layout, out_dtype): - """TOPI compute callback for - - Parameters - ---------- - cfg: ConfigEntity - The config for this template - - a_ : tvm.Tensor - 2-D with shape [M, k_] - - b_ : tvm.Tensor - 2-D with shape [k_, N] - - out_dtype: str - The output type. This is used for mixed precision. - - Returns - ------- - output : tvm.Tensor - 4-D with shape [batch, out_channel, out_height, out_width] - """ - args = _gen_cfg(cfg, a_, b_) - return _matmul_spatial_pack_asm(args, a_, b_, layout, out_dtype) - - -@autotvm.register_topi_schedule(schedule_conv2d_nchw, "arm_cpu", ["matmul"]) -def schedule_matmul_arm_cpu(cfg, outs): - """TOPI schedule callback for conv2d - - Parameters - ---------- - cfg: ConfigEntity - The config for this template - - outs: Array of Tensor - The computation graph description of conv2d - in the format of an array of tensors. - - Returns - ------- - s: Schedule - The computation schedule for conv2d. - """ - s = _matmul_schedule_asm(cfg, outs) - return s - - -def _gen_cfg(cfg, a_, b_): - """get best loginfo from cfg""" - if len(a_.shape) == 2: - w_, ci_ = get_const_tuple(a_.shape) - h_ = 1 - elif len(a_.shape) == 3: - _, ci_, w_ = get_const_tuple(a_.shape) - h_ = 1 - elif len(a_.shape) == 4: - _, ci_, h_, w_ = get_const_tuple(a_.shape) - else: - raise ValueError("not support shape: " + a_.shape) - - co_, k_ = get_const_tuple(b_.shape) - - oh, ow = cfg.axis(h_), cfg.axis(w_) - co = cfg.axis(co_) - k = cfg.reduce_axis(k_) - - oh, vh = cfg.define_split("tile_oh", oh, num_outputs=2) - ow, vw = cfg.define_split("tile_ow", ow, num_outputs=2) - oc, vc = cfg.define_split("tile_co", co, num_outputs=2) - - cfg.define_reorder( - "reorder_0", - [oc, oh, ow, k, vh, vw, vc], - policy="candidate", - candidate=[[oc, oh, ow, k, vh, vw, vc],], - ) - - vh_ = cfg["tile_oh"].size[-1] - vw_ = cfg["tile_ow"].size[-1] - vc_ = cfg["tile_co"].size[-1] - is_var = False - is_transpose = False - return (is_var, is_transpose, ci_, vh_, vw_, vc_) - - -def _matmul_spatial_pack_asm(args, a_, b_, layout, out_dtype): - """matmul_spatial_pack_asm's inner interace""" - is_var, is_transpose, ci_, vh_, vw_, vc_ = args - - # create workload according to raw arguments - out_dtype = out_dtype or a_.dtype - if layout == "NCHW": - batch, k_, h_, w_ = a_.shape if is_var else get_const_tuple(a_.shape) - n_, _ = b_.shape if is_var else get_const_tuple(b_.shape) - elif layout == "NCH": - batch, k_, h_ = a_.shape if is_var else get_const_tuple(a_.shape) - n_, _ = b_.shape if is_var else get_const_tuple(b_.shape) - w_ = 1 - elif layout == "NC": - w_, k_ = a_.shape if is_var else get_const_tuple(a_.shape) - n_, _ = b_.shape if is_var else get_const_tuple(b_.shape) - h_ = 1 - else: - raise ValueError("not support layout: " + layout) - - ki = tvm.reduce_axis((0, k_), name="ki") - type_map = { - "int8": "int32", - "uint8": "uint32", - "float32": "float32", - "float16": "float16", - } - acum_dtype = type_map[a_.dtype] - attrs = {"ci_": ci_, "vh_": vh_, "vw_": vw_, "vc_": vc_, "ACUM_DTYPE": acum_dtype} - - if layout == "NCHW": - h_div = h_ // vh_ - w_div = w_ // vw_ - n_div = n_ // vc_ - avshape = (batch, h_div, w_div, vh_, vw_, k_) - bvshape = (n_div, k_, vc_) - ovshape = (batch, n_div, h_div, w_div, vh_, vw_, vc_) - - a_vec = tvm.compute( - avshape, - lambda n, oh, ow, vh, vw, ci: a_[n][ci][oh * vh_ + vh][ow * vw_ + vw], - name="a_vec", - ) - b_vec = tvm.compute( - bvshape, lambda oc, ci, vc: b_[oc * vc_ + vc][ci], name="b_vec" - ) - - ma = tvm.compute( - ovshape, - lambda n, oc, oh, ow, vh, vw, vc: tvm.sum( - a_vec[n, oh, ow, vh, vw, ki].astype(out_dtype) - * b_vec[oc, ki, vc].astype(out_dtype), - axis=[ki], - ), - name="matmul", - attrs=attrs, - ) - - if is_transpose: - oshape = (batch, h_div * vh_, w_div * vw_, n_div * vc_) - - output = tvm.compute( - oshape, - lambda n, h, w, c: ma[n][c // vc_][h // vh_][w // vw_][h % vh_][w % vw_][ - c % vc_ - ], - name="output_unpack", - tag="asm_matmul_output", - ) - else: - oshape = (batch, n_div * vc_, h_div * vh_, w_div * vw_) - output = tvm.compute( - oshape, - lambda n, c, h, w: ma[n][c // vc_][h // vh_][w // vw_][h % vh_][w % vw_][ - c % vc_ - ], - name="output_unpack", - tag="asm_matmul_output", - ) - elif layout == "NCH": - w_div = w_ // vw_ - n_div = n_ // vc_ - avshape = (batch, w_div, vw_, k_) - bvshape = (n_div, k_, vc_) - ovshape = (batch, n_div, w_div, vw_, vc_) - oshape = (batch, n_div * vc_, w_div * vw_) - - a_vec = tvm.compute( - avshape, lambda b, om, vw, ci: a_[b][ci][om * vw_ + vw], name="a_vec" - ) - b_vec = tvm.compute( - bvshape, lambda on, ci, vc: b_[on * vc_ + vc][ci], name="b_vec" - ) - - ma = tvm.compute( - ovshape, - lambda b, on, om, vm, vn: tvm.sum( - a_vec[b, om, vm, ki].astype(out_dtype) - * b_vec[on, ki, vn].astype(out_dtype), - axis=[ki], - ), - name="matmul", - attrs=attrs, - ) - - output = tvm.compute( - oshape, - lambda b, n, m: ma[b][n // vc_][m // vw_][m % vw_][n % vc_], - name="output_unpack", - tag="asm_matmul_output", - ) - elif layout == "NC": - w_div = w_ // vw_ - n_div = n_ // vc_ - avshape = (w_div, vw_, k_) - bvshape = (n_div, k_, vc_) - ovshape = (w_div, n_div, vw_, vc_) - oshape = (w_div * vw_, n_div * vc_) - - a_vec = tvm.compute( - avshape, lambda om, vw, ci: a_[om * vw_ + vw][ci], name="a_vec" - ) - b_vec = tvm.compute( - bvshape, lambda on, ci, vc: b_[on * vc_ + vc][ci], name="b_vec" - ) - - ma = tvm.compute( - ovshape, - lambda om, on, vm, vn: tvm.sum( - a_vec[om, vm, ki].astype(out_dtype) - * b_vec[on, ki, vn].astype(out_dtype), - axis=[ki], - ), - name="matmul", - attrs=attrs, - ) - - output = tvm.compute( - oshape, - lambda m, n: ma[m // vw_][n // vc_][m % vw_][n % vc_], - name="output_unpack", - tag="asm_matmul_output", - ) - else: - raise ValueError("not support layout: " + layout) - - return output - - -def intrin_conv(args): - """intrin_conv is a conv inner interface""" - ( - ndim, - ci_, - vh_, - vw_, - vc_, - _, - _, - _, - _, - _, - _, - _, - _, - dtype, - acum_dtype, - opname, - core_id, - ) = args - ci_ = tvm.var("ci_") if ci_ is None else ci_ - kvshape = (ci_, vc_) - if ndim == 2: - dvshape = (vw_, ci_) - ovshape = (vw_, vc_) - - data_vec = tvm.placeholder(dvshape, name="a", dtype=dtype) - kernel_vec = tvm.placeholder(kvshape, name="b", dtype=dtype) - ci = tvm.reduce_axis((0, ci_), name="ci") - conv = tvm.compute( - ovshape, - lambda vw, vc: tvm.sum( - data_vec[vw, ci].astype(acum_dtype) - * kernel_vec[ci, vc].astype(acum_dtype), - axis=[ci], - ), - name="conv", - ) - else: - dvshape = (vh_, vw_, ci_) - ovshape = (vh_, vw_, vc_) - - data_vec = tvm.placeholder(dvshape, name="a", dtype=dtype) - kernel_vec = tvm.placeholder(kvshape, name="b", dtype=dtype) - ci = tvm.reduce_axis((0, ci_), name="ci") - conv = tvm.compute( - ovshape, - lambda vh, vw, vc: tvm.sum( - data_vec[vh, vw, ci].astype(acum_dtype) - * kernel_vec[ci, vc].astype(acum_dtype), - axis=[ci], - ), - name="conv", - ) - - stride_a = [ - functools.reduce(lambda x, y: x * y, dvshape[i + 1: len(dvshape)]) - for i in range(0, len(dvshape) - 1) - ] - stride_a.append(1) - stride_b = [ - functools.reduce(lambda x, y: x * y, kvshape[i + 1: len(kvshape)]) - for i in range(0, len(kvshape) - 1) - ] - stride_b.append(1) - stride_c = [ - functools.reduce(lambda x, y: x * y, ovshape[i + 1: len(ovshape)]) - for i in range(0, len(ovshape) - 1) - ] - stride_c.append(1) - - ab_ = tvm.decl_buffer( - data_vec.shape, data_vec.dtype, name="a_", offset_factor=1, strides=stride_a - ) - bb_ = tvm.decl_buffer( - kernel_vec.shape, kernel_vec.dtype, name="b_", offset_factor=1, strides=stride_b - ) - cb_ = tvm.decl_buffer( - conv.shape, conv.dtype, name="C", offset_factor=1, strides=stride_c - ) - - def intrin_func(ins, outs): - aa, bb = ins - cc = outs[0] - - def _body(): - b_ = tvm.ir_builder.create() - b_.emit( - tvm.call_extern( - "int32", - opname, - cc.access_ptr("w"), - aa.access_ptr("r"), - bb.access_ptr("r"), - ci_, - vh_, - vw_, - vc_, - core_id, - ) - ) - return b_.get() - - return _body() - - return tvm.decl_tensor_intrin( - conv.op, intrin_func, binds={data_vec: ab_, kernel_vec: bb_, conv: cb_} - ) - - -def _schedule_asm(cfg, s, a_vec, b_vec, mat, output, last): - """schedule implementation""" - is_transpose = 0 if not isinstance(cfg, dict) else cfg["is_transpose"] - attrs = mat.op.attrs - vh_, vw_, vc_ = (attrs["vh_"].value, attrs["vw_"].value, attrs["vc_"].value) - - # axis split and reorder - if len(a_vec.shape) == 3: - ow, oc = s[last].op.axis - oc, vc = s[last].split(oc, vc_) - ow, vw = s[last].split(ow, vw_) - s[last].reorder(ow, oc, vw, vc) - s[last].vectorize(vc) - oh = ow = oc - elif len(a_vec.shape) == 4: - n, oc, ow, vw, vc = s[last].op.axis - oc, vc = s[last].split(oc, vc_) - ow, vw = s[last].split(ow, vw_) - s[last].reorder(n, oc, ow, vw, vc) - elif len(a_vec.shape) == 6: - if is_transpose: - n, oh, ow, oc = s[last].op.axis - else: - n, oc, oh, ow = s[last].op.axis - oc, vc = s[last].split(oc, vc_) - oh, vh = s[last].split(oh, vh_) - ow, vw = s[last].split(ow, vw_) - s[last].reorder(n, oc, oh, ow, vh, vw, vc) - else: - raise ValueError("not support a_vec: " + str(len(a_vec.shape))) - if last != output and isinstance(output.op, tvm.tensor.ComputeOp): - s[output].compute_inline() - - s[mat].compute_at(s[last], ow) - s[mat].vectorize(s[mat].op.axis[-1]) - - # mark parallel - s[last].parallel(oh) - - if len(a_vec.shape) == 3: - om, _, _ = s[a_vec].op.axis - s[a_vec].compute_at(s[last], ow) - s[a_vec].parallel(om) - elif len(a_vec.shape) == 4: - _, om, _, _ = s[a_vec].op.axis - s[a_vec].compute_at(s[last], ow) - s[a_vec].parallel(om) - else: - _, oh, _, _, _, _ = s[a_vec].op.axis - s[a_vec].parallel(oh) - s[a_vec].vectorize(s[a_vec].op.axis[-1]) - s[a_vec].compute_inline() - - oc, _, _ = s[b_vec].op.axis - s[b_vec].parallel(oc) - s[b_vec].vectorize(s[b_vec].op.axis[-1]) - s[b_vec].compute_inline() - return s - - -def _matmul_schedule_asm(cfg, outs): - """schedule_conv2d_nchw schedule implementation""" - s = tvm.create_schedule([x.op for x in outs]) - - def _callback(op): - if "asm_matmul_output" in op.tag: - # schedule conv2d - output = op.output(0) - mat = op.input_tensors[0] - - sidx = 0 - if mat.op.input_tensors[0].name == "attr": - sidx = 1 - a_vec = mat.op.input_tensors[sidx] - b_vec = mat.op.input_tensors[sidx + 1] - - def recurs_inline(a_): - if a_.op.input_tensors: - a1 = a_.op.input_tensors[0] - if a1.shape == a_.shape: - s[a1].compute_inline() - recurs_inline(a1) - - def recurs_inline_(a_): - if isinstance(a_, tvm.tensor.ComputeOp): - if a_.op.input_tensors: - a1 = a_.op.input_tensors[0] - s[a1].compute_inline() - recurs_inline_(a1) - - recurs_inline_(a_vec) - recurs_inline_(b_vec) - - _schedule_asm(cfg, s, a_vec, b_vec, mat, output, outs[0]) - - traverse_inline(s, outs[0].op, _callback) - return s diff --git a/predict/module/tvm_kernel/lite/python/at_ops/__init__.py b/predict/module/tvm_kernel/lite/python/at_ops/__init__.py deleted file mode 100644 index 274ad9a7e5..0000000000 --- a/predict/module/tvm_kernel/lite/python/at_ops/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2019 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================ -"""Neural network operators""" -# from .at_lib import * -# from .at_gen import * diff --git a/predict/module/tvm_kernel/lite/python/at_ops/at_gen_strip.py b/predict/module/tvm_kernel/lite/python/at_ops/at_gen_strip.py deleted file mode 100644 index 519740c6fe..0000000000 --- a/predict/module/tvm_kernel/lite/python/at_ops/at_gen_strip.py +++ /dev/null @@ -1,516 +0,0 @@ -# Copyright 2019 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================ -""" -This module is rule to generation tvm operate. you can use it like: -python3 at_gen_strip.py [x86:arm64:arm32] -""" -import os -import sys -import itertools -from functools import partial -from at_ops.at_lib import Deconv, tvm, ConvVar, BatchNorm, Eltwise, Resize, CaffeCrop, CaffePReLU -from at_ops.at_lib import FullConnection, Power, ArgMax, Concat, Pad, Pooling, Mean, MatMul, Softmax -from at_ops.at_lib import Activation, Exp, Split, Cast, ExpandDims, Tile, Range -from at_rt import at_runtime_reset - - -check_correctness = False -ARCH_TYPE = sys.argv[1] - -dtypes = ("float32",) # "float16", "uint8", "int8", "uint32", "int32" - -device_map = { - "x86": "llvm", - "arm64": "llvm -device=arm_cpu -model=kirin970 -target=arm64-linux-android", - "arm32": "llvm -device=arm_cpu -model=kirin970 -target=armv7a-linux-eabi -mfloat-abi=soft", -} - -lib_path_map = { - "x86": "../../../build/lib_x86/", - "arm64": "../../../build/lib_arm64/", - "arm32": "../../../build/lib_arm32/", -} - -best_log_map = { - "x86": None, - "arm64": None, - "arm32": None, -} - -lib_path = lib_path_map[ARCH_TYPE] -device = device_map[ARCH_TYPE] -if ARCH_TYPE == "arm64": - if dtypes[0] == "float16": - device += " -mattr=+fp16fml" - else: - device += " -mattr=+neon" -best_log = best_log_map[ARCH_TYPE] - -kwargs = { - "device": device, - "lib_path": lib_path, - "check_correctness": check_correctness, -} - -use_arm32 = ARCH_TYPE == "arm32" - -MAX_DIMS = 5 -const_op_list = [ - ( - "Deconvolution", - partial(Deconv, optype="Deconvolution"), - { - "ndim": (5,), - "dtype": dtypes, - "kernels": ((2, 2),), - "strides": ((2, 2),), - "pad": ((0, 0, 0, 0),), - "dilations": ((1, 1),), - "hasbias": (False, True), - "activation_type": ("NO_ACTIVATION",), - "cfg": [ - { - "CI": tvm.var("CI"), - "VH": 2, - "VW": 12, - "VC": 4, - "VI": 4, - "tile_oh": 2, - "tile_ow": 12, - "tile_co": 4, - "ann_reduce": ["none", "unroll"], - "ann_spatial": ["unroll", "unroll", "vec"], - }, - { - "CI": tvm.var("CI"), - "VH": 2, - "VW": 10, - "VC": 4, - "VI": 4, - "tile_oh": 2, - "tile_ow": 10, - "tile_co": 4, - "ann_reduce": ["none", "unroll"], - "ann_spatial": ["unroll", "unroll", "vec"], - }, - { - "CI": tvm.var("CI"), - "VH": 2, - "VW": 16, - "VC": 4, - "VI": 4, - "tile_oh": 2, - "tile_ow": 16, - "tile_co": 4, - "ann_reduce": ["none", "unroll"], - "ann_spatial": ["unroll", "unroll", "vec"], - }, - { - "CI": tvm.var("CI"), - "VH": 2, - "VW": 8, - "VC": 4, - "VI": 4, - "tile_oh": 2, - "tile_ow": 8, - "tile_co": 4, - "ann_reduce": ["none", "unroll"], - "ann_spatial": ["unroll", "unroll", "vec"], - }, - { - "CI": tvm.var("CI"), - "VH": 2, - "VW": 4, - "VC": 4, - "VI": 4, - "tile_oh": 2, - "tile_ow": 4, - "tile_co": 4, - "ann_reduce": ["none", "unroll"], - "ann_spatial": ["unroll", "unroll", "vec"], - }, - { - "CI": tvm.var("CI"), - "VH": 2, - "VW": 2, - "VC": 4, - "VI": 4, - "tile_oh": 2, - "tile_ow": 2, - "tile_co": 4, - "ann_reduce": ["none", "unroll"], - "ann_spatial": ["unroll", "unroll", "vec"], - }, - ], - }, - ), - ( - "Convolution", - partial(ConvVar, optype="Convolution"), - { - "ndim": (4,), - "layout": ("NCHW",), - "dtype": dtypes, - "kernels": ((1, 1), (3, 3), (5, 5),), - "strides": ((1, 1), (2, 2)), - "pad": ((1, 1, 1, 1), (0, 0, 0, 0), (2, 2, 2, 2)), - "dilations": ((1, 1),), - "hasbias": (False, True), - "activation_type": ("NO_ACTIVATION", "RELU"), - "cfg": [ - { - "CI": tvm.var("CI"), - "VH": 1, - "VW": 1, - "VC": 1, - "VI": 1, - "tile_oh": 1, - "tile_ow": 1, - "tile_co": 1, - "ann_reduce": ["none", "unroll"], - "ann_spatial": ["unroll", "unroll", "vec"], - "core_id": 0, - }, - ], - }, - ), - ( - "ConvolutionDepthwise", - partial(ConvVar, optype="ConvolutionDepthwise"), - { - "ndim": (4,), - "layout": ("NCHW",), - "dtype": dtypes, - "kernels": ((2, 2), (3, 3),), - "strides": ((1, 1),), - "pad": ((0, 0, 0, 0), (0, 1, 0, 1), (1, 0, 1, 0), (1, 1, 1, 1),), - "dilations": ((1, 1),), - "hasbias": (False, True), - "activation_type": ("NO_ACTIVATION", "RELU"), - "channel_multiplier": (1,), - "cfg": [ - { - "CI": tvm.var("CI"), - "VH": 1, - "VW": 1, - "VC": 1, - "VI": 1, - "tile_oh": 1, - "tile_ow": 1, - "tile_co": 1, - "ann_reduce": ["none", "unroll"], - "ann_spatial": ["unroll", "unroll", "vec"], - "core_id": 0, - }, - ], - }, - ), - ( - "DeConvolutionDepthwise", - partial(ConvVar, optype="DeConvolutionDepthwise"), - { - "ndim": (4,), - "layout": ("NCHW",), - "dtype": dtypes, - "kernels": ((1, 1), (2, 2), (3, 3),), - "strides": ((1, 1), (2, 2),), - "pad": ((0, 0, 0, 0), (1, 0, 1, 0), (1, 1, 1, 1),), - "dilations": ((1, 1),), - "hasbias": (False, True), - "activation_type": ("NO_ACTIVATION", "RELU"), - "channel_multiplier": (1,), - "cfg": [ - { - "CI": tvm.var("CI"), - "VH": 1, - "VW": 1, - "VC": 1, - "VI": 1, - "tile_oh": 1, - "tile_ow": 1, - "tile_co": 1, - "ann_reduce": ["none", "unroll"], - "ann_spatial": ["unroll", "unroll", "vec"], - "core_id": 0, - }, - ], - }, - ), - ( - "BatchNorm", - BatchNorm, - {"ndim": (4,), "dtype": dtypes, "optype": ("TFBatchNorm",), "axis": (1, 3,)}, - ), - ( - "BiasAdd", - BatchNorm, - {"ndim": (2, 4), "dtype": dtypes, "optype": ("TFBiasAdd",), "axis": (1, 3)}, - ), - ( - "CaffeBatchNorm", - BatchNorm, - {"ndim": (2, 4), "dtype": dtypes, "optype": ("CaffeBatchNorm",), "axis": (1, 3)}, - ), - ( - "Scale", - BatchNorm, - {"ndim": (2, 4), "dtype": dtypes, "optype": ("CaffeScale",), "axis": (1,)}, - ), - ( - "Eltwise", - Eltwise, - { - "ndim_a": tuple(range(0, MAX_DIMS + 1)), - "ndim_b": tuple(range(0, MAX_DIMS + 1)), - "dtype": dtypes, - "mode": ("add", "subtract", "multiply", "divide", "maximum"), - }, - ), - ( - "Add", - Eltwise, - { - "ndim_a": tuple(range(0, MAX_DIMS + 1)), - "ndim_b": tuple(range(0, MAX_DIMS + 1)), - "dtype": dtypes, - "mode": ("add",), - }, - ), - ( - "Sub", - Eltwise, - { - "ndim_a": tuple(range(0, MAX_DIMS + 1)), - "ndim_b": tuple(range(0, MAX_DIMS + 1)), - "dtype": dtypes, - "mode": ("subtract",), - }, - ), - ( - "Mul", - Eltwise, - { - "ndim_a": tuple(range(0, MAX_DIMS + 1)), - "ndim_b": tuple(range(0, MAX_DIMS + 1)), - "dtype": dtypes, - "mode": ("multiply",), - }, - ), - ( - "RealDiv", - Eltwise, - { - "ndim_a": tuple(range(0, MAX_DIMS + 1)), - "ndim_b": tuple(range(0, MAX_DIMS + 1)), - "dtype": dtypes, - "mode": ("divide",), - }, - ), - ( - "Maximum", - Eltwise, - { - "ndim_a": tuple(range(0, MAX_DIMS + 1)), - "ndim_b": tuple(range(0, MAX_DIMS + 1)), - "dtype": dtypes, - "mode": ("maximum",), - }, - ), - ( - "ResizeBilinear", - Resize, - { - "ndim": (4,), - "dtype": dtypes, - "method": ("bilinear",), # "bicubic" - "align_corners": (True, False), - }, - ), - ( - "ResizeNearestNeighbor", - Resize, - { - "ndim": (4,), - "dtype": dtypes, - "method": ("nearest_neighbor",), # "bicubic" - "align_corners": (True, False), - }, - ), - ( - "CaffeCrop", - CaffeCrop, - {"ndim": (4,), "dtype": dtypes, "axis": tuple(range(0, 4))}, - ), - ( - "CaffePReLU", - CaffePReLU, - {"ndim": (2, 4), "dtype": dtypes, "channel_shared": (True, False)}, - ), - ( - "FullConnection", - FullConnection, - {"ndim_a": (2, 4), "dtype": dtypes, "has_bias": (True, False)}, - ), - ("Power", Power, {"ndim": tuple(range(1, MAX_DIMS + 1)), "dtype": dtypes}), - ( - "ArgMax", - ArgMax, - { - "ndim": tuple(range(1, MAX_DIMS + 1)), - "dtype": dtypes, - "axis": tuple(range(0, MAX_DIMS)), # not support None - "keep_dims": (True, False), - "top_k": (1,), - "out_dtype": dtypes, - }, - ), - ( - "Concat", - Concat, - { - "ndim": tuple(range(1, MAX_DIMS + 1)), - "dtype": dtypes, - "input_num": tuple(range(2, 6 + 1)), - "axis": tuple(range(0, MAX_DIMS)), - }, - ), - ( - "Pad", - Pad, - { - "ndim": tuple(range(2, MAX_DIMS + 1)), - "dtype": dtypes, - "paddingmode": ("CONSTANT", "REFLECT", "SYMMETRIC"), - }, - ), - ( - "Pooling", - Pooling, - { - "ndim": (4,), - "dtype": dtypes, - "pooling_mode": ("max", "avg"), - "caffe_mode": (True, False), - "kernel": ((1, 1), (2, 2), (3, 3), (5, 5)), - "stride": ((1, 1), (2, 2), (3, 3)), - "pad": ((0, 0, 0, 0), (0, 1, 0, 1), (1, 1, 1, 1)), - "use_global": (True, False), - }, - ), - ( - "Mean", - Mean, - { - "ndim": (4,), - "dtype": dtypes, - "axis": ( - (0,), - (1,), - (2,), - (3,), - (0, 1), - (0, 2), - (0, 3), - (1, 2), - (1, 3), - (2, 3), - (0, 1, 2), - (0, 1, 3), - (0, 2, 3), - (1, 2, 3), - (0, 1, 2, 3), - ), - "keep_dims": (True, False), - }, - ), - ( - "MatMul", - MatMul, - { - "ndim_a": (2,), - "ndim_b": (2,), - "dtype": dtypes, - "transpose_a": (True, False), - "transpose_b": (True, False), - }, - ), - ( - "Softmax", - Softmax, - { - "ndim": tuple(range(1, MAX_DIMS + 1)), - "dtype": dtypes, - "axis": tuple(range(0, MAX_DIMS)), - }, - ), - ( - "Activation", - Activation, - { - "ndim": tuple(range(1, MAX_DIMS + 1)), - "dtype": dtypes, - "optype": ("NO_ACTIVATION", "RELU", "RELU6", "SIGMOID"), - }, - ), - ("Exp", Exp, {"ndim": tuple(range(1, MAX_DIMS + 1)), "dtype": dtypes}), - ( - "Split", - Split, - { - "ndim": tuple(range(1, MAX_DIMS + 1)), - "dtype": dtypes, - "output_num": tuple(range(1, 5)), - "axis": tuple(range(0, MAX_DIMS)), - }, - ), - ( - "Cast", - Cast, - { - "ndim": tuple(range(1, MAX_DIMS + 1)), - "src_dtype": dtypes, - "dst_dtype": dtypes, - }, - ), - ( - "ExpandDims", - ExpandDims, - { - "ndim": tuple(range(1, MAX_DIMS + 1)), - "dtype": dtypes, - "axis": tuple(range(0, MAX_DIMS)), - }, - ), - ("Tile", Tile, {"ndim": tuple(range(1, MAX_DIMS + 1)), "dtype": dtypes}), - ("Range", Range, {"out_dtype": ("float32", "uint32", "int32")}), -] - - -def gen_const_libs(some_op=None): - for optype, func, attr in const_op_list: - if some_op and some_op != optype: - continue - for values in itertools.product(*attr.values()): - args = dict((k, v) for k, v in zip(attr.keys(), values)) - func(device=device, lib_path=lib_path, **args) - - -if __name__ == "__main__": - if not os.path.exists(lib_path): - os.makedirs(lib_path) - # skip best_history log: - with tvm.target.create(device): - with at_runtime_reset.AtRuntimeReset(): - gen_const_libs() diff --git a/predict/module/tvm_kernel/lite/python/at_ops/at_lib.py b/predict/module/tvm_kernel/lite/python/at_ops/at_lib.py deleted file mode 100644 index 655064b29e..0000000000 --- a/predict/module/tvm_kernel/lite/python/at_ops/at_lib.py +++ /dev/null @@ -1,1193 +0,0 @@ -# Copyright 2019 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================ -""" -This module is rule to generation tvm operate, call by at_gen_strip.py -""" -import numpy as np -import tvm -import topi -from topi.image import resize -from topi.nn import mirror_pad -from topi import tag -import topi.testing - -from arm_cpu.deconv import _conv_spatial_pack_deconv, schedule_conv2d_nchw_arm_cpu_deconv -from arm_cpu.conv2d import _conv_spatial_pack_asm, schedule_conv2d_nchw_arm_cpu -from arm_cpu.matmul import _matmul_spatial_pack_asm, _matmul_schedule_asm -from arm_cpu.depthwise_conv2d import _depthwise_spatial_pack, schedule_depthwise_conv2d_nchw_arm -from config_tool import activation_enum_map - -map_conv = { - 'Convolution': "Conv2D", - 'ConvolutionDepthwise': "DepthwiseConv2D", - 'Deconvolution': "DeConv2D", - 'DeConvolutionDepthwise': "DeDepthwiseConv2D", -} - - -def Genlib(sch, tensor_list, device, opname, lib_path, print_lower=False): - if print_lower: - print(tvm.lower(sch, tensor_list, simple_mode=True)) - ctx = tvm.context(device, 0) - func_o = tvm.build(sch, tensor_list, device + " --system-lib", name=opname) - func_so = tvm.build(sch, tensor_list, device, name=opname) - func_o.save(lib_path + opname + ".o", "o") - return func_o, func_so, ctx - - -def AsType(as_input, dtype): - if as_input.dtype == dtype: - return as_input - return tvm.compute(as_input.shape, - lambda *i: as_input(*i).astype(dtype), - tag="injective") - - -@tvm.tag_scope(tag=tag.ELEMWISE) -def TopiNNrelu6(x): - return tvm.compute(x.shape, lambda *i: tvm.min(tvm.max(x(*i), tvm.const(0, x.dtype)), tvm.const(6, x.dtype))) - - -def TopiActivation(in_tensor, a_type, memcpy=False): - ''' - activativation - Args: - in_tensor: - a_type: - memcpy: - - Returns: - ''' - if a_type == 'NO_ACTIVATION': - if memcpy: - return tvm.compute(in_tensor.shape, lambda *i: in_tensor[i], tag=tag.ELEMWISE) - return in_tensor - if a_type == 'RELU': - return topi.nn.relu(in_tensor) - if a_type == 'RELU6': - return TopiNNrelu6(in_tensor) - if a_type == 'SIGMOID': - if in_tensor.dtype in ["uint8", "int8", "uint32", "int32"]: - a_fp32 = AsType(in_tensor, 'float32') - out_tensor = topi.sigmoid(a_fp32) - return AsType(out_tensor, in_tensor.dtype) - return topi.sigmoid(in_tensor) - raise ValueError("not support activation type" + a_type) - - -def Deconv(device="llvm", lib_path="./", optype=None, - ndim=None, dtype=None, kernels=None, - strides=None, pad=None, dilations=None, - hasbias=None, activation_type=None, - config_entity=None, impl_dtype=None, - use_arm32=False, cfg=None): - ''' - Deconvolution - Args: - device: - lib_path: - optype: - ndim: - dtype: - kernels: - strides: - pad: - dilations: - hasbias: - activationType: - configEntity: - impl_dtype: - use_arm32: - cfg: - - Returns: - ''' - if cfg is None: - cfg = {'CI': tvm.var('ci'), 'VH': 2, 'VW': 2, 'VC': 4, 'VI': 4, - 'tile_oh': 2, 'tile_ow': 2, 'tile_co': 4, - 'ann_reduce': ['none', 'none'], - "ann_spatial": ['none', 'none', 'none'] - } - has_bias = hasbias - batch = tvm.var("batch") - in_channel = tvm.var("in_channel") - in_height, in_width = tvm.var("in_height"), tvm.var("in_width") - kh, kw = kernels - ow = cfg['VW'] - oh = cfg['VH'] - oc = cfg['VC'] - op_name = "%s_ndim%d_%s_k%d_s%d_p%d%d%d%d_d%d_act%d_vc%d_vh%d_vw%d_hasbias%d" % (\ - map_conv[optype], ndim, dtype,\ - kh, strides[0], pad[0], pad[1], pad[2], pad[3], dilations[0],\ - activation_enum_map[activation_type], oc, oh, ow, hasbias) - opname = op_name - print("DEconv", opname, config_entity) - - if impl_dtype is None: - impl_dtype = dtype - - out_channel = tvm.var("out_channel") - - # define placeholder - input_tensor = in_tensor = tvm.placeholder((batch, in_channel, in_height, in_width, 4), \ - dtype=dtype, name='in_tensor') - temp_tensor = kernel_tensor = tvm.placeholder((in_channel*4, out_channel, kh, kw), dtype=dtype, \ - name='kernel_tensor') - if has_bias: - bias = tvm.placeholder((out_channel,), dtype=dtype, name='bias') - bias1 = topi.reshape(bias, (out_channel, 1, 1)) - - if impl_dtype != dtype: - input_tensor = AsType(input_tensor, impl_dtype) - temp_tensor = AsType(temp_tensor, impl_dtype) - if has_bias: - bias1 = AsType(bias1, impl_dtype) - - # define compute & schedule - cfg1 = (True, 1, 1, 1) if cfg is None else (True, cfg["tile_oh"], cfg["tile_ow"], cfg["tile_co"]) - out_tensor = _conv_spatial_pack_deconv(cfg1, input_tensor, temp_tensor, out_dtype=impl_dtype) - - if has_bias: - out_tensor = tvm.compute(out_tensor.shape, lambda n, co, h, w, c4: \ - out_tensor[n, co, h, w, c4] + bias1[co*4 + c4][0][0], tag="injective") - out_tensor = TopiActivation(out_tensor, activation_type) - if impl_dtype != dtype: - out_tensor = AsType(out_tensor, dtype) - - # create schedule - if use_arm32: - s = tvm.create_schedule(out_tensor.op) - else: - s = schedule_conv2d_nchw_arm_cpu_deconv(cfg, [out_tensor]) - - attr = [batch, in_channel, in_height, in_width, out_channel, in_tensor, kernel_tensor] - if has_bias: attr.append(bias) - attr.append(out_tensor) - tensor_list = attr - - Genlib(s, tensor_list, device, opname, lib_path) - - -def ConvVar(device="llvm", lib_path="./", optype=None,\ - ndim=None, layout=None, dtype=None, kernels=None,\ - strides=None, pad=None, dilations=None,\ - hasbias=None, activation_type=None,\ - config_entity=None, impl_dtype=None, channel_multiplier=None,\ - use_arm32=False, cfg=None): - ''' - convolution - Args: - device: - lib_path: - optype: - ndim: - layout: - dtype: - kernels: - strides: - pad: - dilations: - hasbias: - activationType: - configEntity: - impl_dtype: - channel_multiplier: - use_arm32: - cfg: - - Returns: - ''' - use_depthwise = optype == 'ConvolutionDepthwise' - use_deconv = optype == 'Deconvolution' - use_deconv_depthwise = optype == 'DeConvolutionDepthwise' - has_bias = hasbias - - ow = 1 if cfg is None else cfg['VW'] - oh = 1 if cfg is None else cfg['VH'] - oc = 1 if cfg is None else cfg['VC'] - kh, kw = kernels - op_name = "%s_ndim%d_%s_k%d_s%d_p%d%d%d%d_d%d_act%d_vc%d_vh%d_vw%d_hasbias%d" % ( \ - map_conv[optype], ndim, dtype, \ - kh, strides[0], pad[0], pad[1], pad[2], pad[3], dilations[0], \ - activation_enum_map[activation_type], oc, oh, ow, hasbias) - batch = tvm.var("batch") - in_channel = tvm.var("in_channel") - in_height, in_width = tvm.var("in_height"), tvm.var("in_width") - pad_up, pad_down, pad_left, pad_right = pad - opname = op_name - - print("Conv", opname, config_entity) - - if impl_dtype is None: - impl_dtype = dtype - - if use_depthwise: - multiplier = channel_multiplier - out_channel = in_channel * multiplier - elif use_deconv_depthwise: - multiplier = channel_multiplier - out_channel = in_channel * multiplier - else: - out_channel = tvm.var("out_channel") - - # define placeholder - input_tensor = in_tensor = tvm.placeholder((batch, in_channel, in_height, in_width), dtype=dtype, name='in_tensor') - - if use_depthwise: - temp_tensor = kernel_tensor = tvm.placeholder((in_channel, multiplier, kh, kw), dtype=dtype,\ - name='kernel_tensor') - elif use_deconv: - temp_tensor = kernel_tensor = tvm.placeholder((in_channel, out_channel, kh, kw), dtype=dtype,\ - name='kernel_tensor') - elif use_deconv_depthwise: - temp_tensor = kernel_tensor = tvm.placeholder((in_channel, multiplier, kh, kw), dtype=dtype,\ - name='kernel_tensor') - else: - temp_tensor = kernel_tensor = tvm.placeholder((out_channel, in_channel, kh, kw), dtype=dtype,\ - name='kernel_tensor') - if has_bias: - bias = tvm.placeholder((out_channel,), dtype=dtype, name='bias') - bias1 = topi.reshape(bias, (out_channel, 1, 1)) - - if impl_dtype != dtype: - input_tensor = AsType(input_tensor, impl_dtype) - temp_tensor = AsType(temp_tensor, impl_dtype) - if has_bias: - bias1 = AsType(bias1, impl_dtype) - - # define compute & schedule - if pad_up != pad_down or pad_left != pad_right: - input_tensor = topi.nn.pad(input_tensor, [0, 0, pad_up, pad_left], [0, 0, pad_down, pad_right], name='data_pad') - padding = 0, 0 - else: - padding = pad_up, pad_left - if use_depthwise: - cfg1 = (True, 1, 1, 1) if cfg is None else (True, cfg["tile_oh"], cfg["tile_ow"], cfg["tile_co"]) - out_tensor = _depthwise_spatial_pack(cfg1, input_tensor, temp_tensor, strides, padding, dilations,\ - out_dtype=impl_dtype) - elif use_deconv: - - def GetInput(input_tensor, temp_tensor, padding): - _, out_c, filter_h, filter_w = temp_tensor.shape - if out_c is None: - print("temp_tensor.shape err") - stride_h, stride_w = strides - # dilate stage - dilated_input = topi.nn.dilate(input_tensor, [1, 1, stride_h, stride_w], - name='DilatedInput') - # padding stage - fpad_top, fpad_left, fpad_bottom, fpad_right = topi.nn.get_pad_tuple(padding, ( - filter_h, filter_w)) - bpad_top = filter_h - 1 - fpad_top - bpad_bottom = filter_h - 1 - fpad_bottom - bpad_left = filter_w - 1 - fpad_left - bpad_right = filter_w - 1 - fpad_right - padded_input = topi.nn.pad(dilated_input, \ - [0, 0, bpad_top, bpad_left], \ - [0, 0, bpad_bottom, bpad_right], \ - name='PaddedInput') - return padded_input - - special_deconv = kh == 2 and kw == 2 and strides[0] == 2 and strides[1] == 2 - # special_deconv = False - if special_deconv: - out_tensor = OptimalOut(input_tensor, temp_tensor, in_channel) - else: - out_tensor = BaseImplementation(input_tensor, temp_tensor, GetInput, layout, padding) - elif use_deconv_depthwise: - def GetInput(input_tensor, temp_tensor, padding): - _, out_c, filter_h, filter_w = temp_tensor.shape - if out_c is None: - print("temp_tensor.shape err") - stride_h, stride_w = strides - # dilate stage - dilated_input = topi.nn.dilate(input_tensor, [1, 1, stride_h, stride_w], - name='DilatedInput') - # padding stage - fpad_top, fpad_left, fpad_bottom, fpad_right = topi.nn.get_pad_tuple(padding, ( - filter_h, filter_w)) - bpad_top = filter_h - 1 - fpad_top - bpad_bottom = filter_h - 1 - fpad_bottom - bpad_left = filter_w - 1 - fpad_left - bpad_right = filter_w - 1 - fpad_right - padded_input = topi.nn.pad(dilated_input, \ - [0, 0, bpad_top, bpad_left], \ - [0, 0, bpad_bottom, bpad_right], \ - name='PaddedInput') - return padded_input - - temp_tensor = topi.flip(temp_tensor, axis=-1) - temp_tensor = topi.flip(temp_tensor, axis=-2) - out_tensor = topi.nn.depthwise_conv2d_nchw(GetInput(input_tensor, temp_tensor, padding), temp_tensor, (1, 1), \ - padding, (1, 1), out_dtype=input_tensor.dtype) - else: - cfg1 = (True, 1, 1, 1) if cfg is None else (True, cfg["tile_oh"], cfg["tile_ow"], cfg["tile_co"]) - out_tensor = _conv_spatial_pack_asm(cfg1, input_tensor, temp_tensor, strides, padding, dilations,\ - out_dtype=impl_dtype) - - if has_bias: - out_tensor = tvm.compute(out_tensor.shape, lambda n, co, h, w: out_tensor[n, co, h, w] + bias1[co][0][0],\ - tag="injective") - out_tensor = TopiActivation(out_tensor, activation_type) - if impl_dtype != dtype: - out_tensor = AsType(out_tensor, dtype) - - # create schedule - if use_arm32: - s = tvm.create_schedule(out_tensor.op) - elif use_depthwise: - s = schedule_depthwise_conv2d_nchw_arm(cfg, [out_tensor]) - elif use_deconv: - if special_deconv: - s = tvm.create_schedule([out_tensor.op]) - else: - s = topi.generic.schedule_conv2d_nchw([out_tensor]) - elif use_deconv_depthwise: - s = tvm.create_schedule([out_tensor.op]) - else: - s = schedule_conv2d_nchw_arm_cpu([out_tensor]) - - # generate lib - attr = [batch, in_channel, in_height, in_width, out_channel, in_tensor, kernel_tensor] - tensor_list = [*attr, bias, out_tensor] if has_bias else [*attr, out_tensor] - Genlib(s, tensor_list, device, opname, lib_path) - - -def BaseImplementation(input_tensor, temp_tensor, get_input, layout, padding): - temp_tensor = topi.flip(temp_tensor, axis=-1) - temp_tensor = topi.flip(temp_tensor, axis=-2) - temp_tensor = topi.transpose(temp_tensor, axes=(1, 0, 2, 3)) - out_tensor = topi.nn.conv2d(get_input(input_tensor, temp_tensor, padding), temp_tensor, (1, 1), padding, (1, 1), - layout=layout, out_dtype=input_tensor.dtype) - return out_tensor - - -def OptimalOut(input_tensor, temp_tensor, in_channel): - ''' - deconv compute - Args: - input_tensor: - temp_tensor: - in_channel: - - Returns: - ''' - temp_tensor = topi.transpose(temp_tensor, axes=(1, 0, 2, 3)) - out_shape = [] - for i in range(len(input_tensor.shape)): - if i == 0: - out_shape.append(input_tensor.shape[i]) - continue - if i == 1: - out_shape.append(temp_tensor.shape[0]) - continue - out_shape.append(2 * input_tensor.shape[i]) - rc = tvm.reduce_axis((0, in_channel), name='rc') - return tvm.compute(out_shape, lambda i, j, k, l:\ - tvm.sum(input_tensor[i, rc, k // 2, l // 2].astype(input_tensor.dtype) *\ - temp_tensor[j, rc, k % 2, l % 2].astype(input_tensor.dtype), axis=[rc])) - - -def Concat(device="llvm", lib_path="./", - ndim=None, dtype=None, input_num=None, axis=None): - ''' - concat - Args: - device: - lib_path: - all_tensors: - ndim: - dtype: - input_num: - axis: - - Returns: - ''' - if axis >= ndim: - return - shapes = [] - for i in range(input_num): - shape = [] - for j in range(ndim): - if j == axis: - shape.append(tvm.var("axis" + str(i))) - else: - shape.append(tvm.var("n" + str(j))) - shapes.append(shape) - in_tensor = [tvm.placeholder(shape, dtype=dtype, name='in_tensor%d' % i) for i, shape in enumerate(shapes)] - opname = "Concat_ndim%d_%s_input_num%d_axis%d" % (ndim, dtype, input_num, axis) - print(opname) - - # define compute - out_tensor = topi.concatenate(tuple(in_tensor), axis) - tensor_list = in_tensor + [out_tensor] - if ndim < 5: - s = topi.generic.schedule_concatenate(out_tensor) - else: - s = tvm.create_schedule(out_tensor.op) - Genlib(s, tensor_list, device, opname, lib_path) - - -def Activation(device="llvm", lib_path="./", - ndim=None, dtype=None, optype=None): - ''' - activation - Args: - device: - lib_path: - ndim: - dtype: - optype: - - Returns: - ''' - shape = [tvm.var("n" + str(i)) for i in range(ndim)] - opname = "Activation_ndim%d_%s_%s" % (ndim, dtype, optype) - print(opname) - - # define compute - in_tensor = tvm.placeholder(shape, dtype=dtype, name='in_tensor') - out_tensor = TopiActivation(in_tensor, optype, memcpy=True) - tensor_list = [in_tensor, out_tensor] - s = tvm.create_schedule(out_tensor.op) - Genlib(s, tensor_list, device, opname, lib_path) - - -def BatchNorm(device="llvm", lib_path="./", - ndim=None, dtype=None, optype=False, axis=None): - ''' - batchnorm - Args: - device: - lib_path: - ndim: - dtype: - optype: - axis: - - Returns: - ''' - if axis >= ndim: - return - shape = [tvm.var("n" + str(i)) for i in range(ndim)] - channel = shape[axis] - eps = tvm.var("epsilon", dtype="float32") - opname = optype + ("_ndim%d_%s_axis%d" % (ndim, dtype, axis)) - print(opname) - - # define compute - in_tensor = tvm.placeholder(shape, dtype=dtype, name='in_tensor') - mean = tvm.placeholder((channel,), dtype=dtype, name='mean') - variance = tvm.placeholder((channel,), dtype=dtype, name='var') - scale = tvm.placeholder((channel,), dtype=dtype, name='scale') - offset = tvm.placeholder((channel,), dtype=dtype, name='offset') - - variance_sqrt = tvm.compute((channel,), lambda i: tvm.sqrt(variance[i] + eps.astype(dtype))) - if optype == "TFBatchNorm": - out_tensor = tvm.compute(shape, lambda *idx: ((in_tensor[idx] - mean[idx[axis]]) / variance_sqrt[idx[axis]]) *\ - scale[idx[axis]] + offset[idx[axis]]) - tensor_list = [eps, in_tensor, scale, offset, mean, variance, out_tensor] - elif optype == "CaffeBatchNorm": - out_tensor = tvm.compute(shape, lambda *idx: (in_tensor[idx] - mean[idx[axis]]) / variance_sqrt[idx[axis]]) - tensor_list = [eps, in_tensor, mean, variance, out_tensor] - elif optype == "CaffeScale": - out_tensor = tvm.compute(shape, lambda *idx: in_tensor[idx] * scale[idx[axis]] + offset[idx[axis]]) - tensor_list = [in_tensor, scale, offset, out_tensor] - elif optype == "TFBiasAdd": - out_tensor = tvm.compute(shape, lambda *idx: in_tensor[idx] + offset[idx[axis]]) - tensor_list = [in_tensor, offset, out_tensor] - else: - raise RuntimeError("no support for {}".format(optype)) - - # define schedule & generate lib - s = tvm.create_schedule(out_tensor.op) - Genlib(s, tensor_list, device, opname, lib_path) - - -def Pooling(device="llvm", lib_path="./", - ndim=None, dtype=None, pooling_mode=None, kernel=None, stride=None, pad=None, caffe_mode=None, - use_global=False): - ''' - pooling - Args: - device: - lib_path: - ndim: - dtype: - pooling_mode: - kernel: - stride: - pad: - caffe_mode: - use_global: - - Returns: - ''' - shape = [tvm.var("n" + str(i)) for i in range(0, ndim)] - layout = 'NCHW' - if use_global: - opname = "GlobalPooling_ndim%d_%s_%s" % (ndim, dtype, pooling_mode) - else: - kernel_h, kernel_w = kernel - stride_h, stride_w = stride - pad_up, pad_down, pad_left, pad_right = pad - if pad_up == 0 and pad_down == 0 and pad_left == 0 and pad_right == 0 and caffe_mode: - caffe_mode = False - opname = "Pooling_ndim%d_%s_%s_kernel%d%d_stride%d%d_pad%d%d%d%d%s" \ - % (ndim, dtype, pooling_mode, kernel_h, kernel_w, stride_h, stride_w, - pad_up, pad_down, pad_left, pad_right, "_caffe" if caffe_mode else "") - print(opname) - - # define compute - in_tensor = tvm.placeholder(shape, dtype=dtype, name='in_tensor') - if use_global: - out_tensor = topi.nn.global_pool(in_tensor, pool_type=pooling_mode, layout=layout) - sch = topi.generic.schedule_adaptive_pool(out_tensor) - else: - out_tensor = topi.nn.pool(in_tensor, - kernel=(kernel_h, kernel_w), - stride=(stride_h, stride_w), - padding=(pad_up, pad_left, pad_down, pad_right), - pool_type=pooling_mode, - ceil_mode=False, - layout=layout, - count_include_pad=False) - sch = topi.generic.schedule_pool(out_tensor, layout) - tensor_list = [in_tensor, out_tensor] - Genlib(sch, tensor_list, device, opname, lib_path, print_lower=False) - - -def Eltwise(device="llvm", lib_path="./", - ndim_a=None, ndim_b=None, dtype=None, mode=None): - ''' - eltwise - Args: - device: - lib_path: - ndim_a: - ndim_b: - dtype: - mode: - - Returns: - ''' - ndim_max = max(ndim_a, ndim_b) - shape = [tvm.var("n" + str(i)) for i in range(ndim_max)] - shape_b1 = [dim if i == 1 else 1 for i, dim in enumerate(shape)] - shape_a = shape[ndim_max - ndim_a:] if ndim_a else (1,) - shape_b = shape[ndim_max - ndim_b:] if ndim_b == ndim_a else shape_b1 if ndim_b == 1 else (1,) - opname = "Eltwise_%s_ndimA%d_ndimB%d_%s" % (mode, ndim_a, ndim_b, dtype) - print(opname) - - # define compute - in_tensor = tvm.placeholder(shape_a, dtype=dtype, name='in_tensor') - b_tensor = tvm.placeholder(shape_b, dtype=dtype, name='b_tensor') - - topi_funs = { - 'add': topi.add, - 'subtract': topi.subtract, - 'multiply': topi.multiply, - 'divide': topi.divide, - 'maximum': topi.maximum, - 'minimum': topi.minimum, - } - - out_tensor = topi_funs[mode](in_tensor, b_tensor) - tensor_list = [in_tensor, b_tensor, out_tensor] - s = topi.generic.schedule_elemwise(out_tensor) - Genlib(s, tensor_list, device, opname, lib_path) - - -def Softmax(device="llvm", lib_path="./", - ndim=None, dtype=None, axis=None): - ''' - softmax - Args: - device: - lib_path: - ndim: - dtype: - axis: - - Returns: - ''' - if axis >= ndim: - return - shape = [tvm.var("n" + str(i)) for i in range(ndim)] - opname = "Softmax_ndim%d_%s_axis%s" % (ndim, dtype, axis) - print(opname) - - # define compute - in_tensor = tvm.placeholder(shape, dtype=dtype, name='in_tensor') - out_tensor = topi.nn.softmax(in_tensor, axis) - tensor_list = [in_tensor, out_tensor] - s = topi.generic.schedule_elemwise(out_tensor) - Genlib(s, tensor_list, device, opname, lib_path) - - -def Resize(device="llvm", lib_path="./", - ndim=None, dtype=None, method=None, align_corners=None): - ''' - resize - Args: - device: - lib_path: - ndim: - dtype: - method: - align_corners: - - Returns: - ''' - shape = [tvm.var("n" + str(i)) for i in range(ndim)] - new_height = tvm.var("newHeight") - new_width = tvm.var("new_width") - opname = "Resize_ndim%d_%s_%s_%s" % (ndim, dtype, method, "Align" if align_corners else "NotAlign") - print(opname) - - # define compute - in_tensor = tvm.placeholder(shape, dtype=dtype, name='in_tensor') - out_tensor = resize(in_tensor, [new_height, new_width], align_corners=align_corners, method=method) - tensor_list = [new_height, new_width, in_tensor, out_tensor] - s = topi.generic.schedule_injective(out_tensor) - Genlib(s, tensor_list, device, opname, lib_path) - - -def Mean(device="llvm", lib_path="./", - ndim=None, dtype=None, axis=None, keep_dims=None): - ''' - mean - Args: - device: - lib_path: - ndim: - dtype: - axis: - keepDims: - - Returns: - ''' - if axis[-1] >= ndim: - return - shape = [tvm.var("n" + str(i)) for i in range(ndim)] - axis_str = "" - for dim in axis: - axis_str += str(dim) - opname = "Mean_ndim%d_%s_axis%s_%s" % (ndim, dtype, axis_str, "keepDims" if keep_dims else "notkeepDims") - print(opname) - - # define compute - in_tensor = tvm.placeholder(shape, dtype=dtype, name='in_tensor') - c_shape = shape[:] - reduced_num = 1 - for dim in axis: - c_shape[dim] = 1 - reduced_num *= shape[dim] - - def _ComputeSum(*b_idx): - reduce_axis = [tvm.reduce_axis((0, shape[dim])) for dim in axis] - a_idx = list(b_idx) - for i, dim in enumerate(axis): - a_idx[dim] = reduce_axis[i] - a_idx = tuple(a_idx) - return tvm.sum(in_tensor[a_idx], axis=reduce_axis) - - out_tensor = tvm.compute(c_shape, _ComputeSum) - out_tensor = tvm.compute(c_shape, lambda *i: out_tensor(*i) / reduced_num) - if not keep_dims: - out_tensor = topi.squeeze(out_tensor, axis) - - # define schedule & generate lib - tensor_list = [in_tensor, out_tensor] - s = tvm.create_schedule(out_tensor.op) - Genlib(s, tensor_list, device, opname, lib_path) - - -def CaffeCrop(device="llvm", lib_path="./", - ndim=None, dtype=None, axis=None): - ''' - caffe crop op - Args: - device: - lib_path: - ndim: - dtype: - axis: - - Returns: - ''' - shape = [tvm.var("n" + str(i)) for i in range(axis)] - shape_a = shape[:] - shape_b = shape[:] - offsets = [] - for i in range(axis, ndim): - shape_a.append(tvm.var("nA" + str(i))) - shape_b.append(tvm.var("nB" + str(i))) - offsets.append(tvm.var("offset" + str(i))) - opname = "CaffeCrop_ndim%d_%s_axis%d" % (ndim, dtype, axis) - print(opname) - - # define compute - in_tensor = tvm.placeholder(shape_a, dtype=dtype, name='in_tensor') - b_tensor = tvm.placeholder(shape_b, dtype=dtype, name='b_tensor') - begin = [0] * axis + offsets - end = shape_a[:] - for i in range(axis, len(shape_a)): - end[i] = offsets[i - axis] + shape_b[i] - shape_c = [end[i] - begin[i] for i in range(ndim)] - - def _Compute(*C_idx): - a_idx = [idx + begin[i] for i, idx in enumerate(list(C_idx))] - a_idx = tuple(a_idx) - return in_tensor[a_idx] - - out_tensor = tvm.compute(shape_c, _Compute) - tensor_list = offsets + [in_tensor, b_tensor, out_tensor] - - s = tvm.create_schedule(out_tensor.op) - Genlib(s, tensor_list, device, opname, lib_path) - - -def FullConnection(device="llvm", lib_path="./", - ndim_a=None, dtype=None, has_bias=None): - ''' - full connection - Args: - device: - lib_path: - ndim_a: - dtype: - hasBias: - - Returns: - ''' - n_dim, ci, h_dim, kernel_tensor = (tvm.var("n_dim"), tvm.var("out_tensor"), tvm.var("h_dim"), \ - tvm.var("kernel_tensor")) - co = tvm.var("co") - if ndim_a == 4: - shape_a = (n_dim, ci, h_dim, kernel_tensor) - chw = ci * h_dim * kernel_tensor - else: - shape_a = (n_dim, ci) - chw = ci - shape_w = (co, chw) - opname = "FullConnection_ndimA%d_%s_%s" % (ndim_a, dtype, "hasBias" if has_bias else "notHasBias") - is_var = True - vh, vw, vc = 1, 1, 1 - print(opname) - - in_tensor = tvm.placeholder(shape_a, dtype=dtype, name='in_tensor') - kernel_tensor = tvm.placeholder(shape_w, dtype=dtype, name='kernel_tensor') - input_tensor = topi.reshape(in_tensor, (n_dim, chw)) if len(shape_a) == 4 else in_tensor - - out_tensor = _matmul_spatial_pack_asm((is_var, 0, ci, vh, vw, vc), input_tensor, kernel_tensor, \ - layout='NC', out_dtype=dtype) - if has_bias: - bias = tvm.placeholder((co,), dtype=dtype, name='bias') - out_tensor = tvm.compute((n_dim, co), lambda n, co: out_tensor[n, co] + bias[co], tag='injective') - - tensor_list = [in_tensor, kernel_tensor, bias, out_tensor] if has_bias else [in_tensor, kernel_tensor, out_tensor] - cfg = {'is_var': is_var, 'is_transpose': 0, 'core_id': 0, 'CI': ci, 'VH': vh, 'VW': vw, 'VC': vc} - s = _matmul_schedule_asm(cfg, [out_tensor]) - Genlib(s, tensor_list, device, opname, lib_path) - - -def Power(device="llvm", lib_path="./", - ndim=None, dtype=None): - ''' - power - Args: - device: - lib_path: - ndim: - dtype: - - Returns: - ''' - shape = [tvm.var("n" + str(i)) for i in range(ndim)] - power = tvm.var("power", dtype="float32") - scale = tvm.var("scale", dtype="float32") - shift = tvm.var("shift", dtype="float32") - opname = "Power_ndim%d_%s" % (ndim, dtype) - print(opname) - - in_tensor = tvm.placeholder(shape, dtype=dtype, name='in_tensor') - out_tensor = tvm.compute(shape, lambda *i: tvm.power(in_tensor[i] * scale.astype(in_tensor.dtype) + \ - shift.astype(in_tensor.dtype), \ - power.astype(in_tensor.dtype))) - tensor_list = [power, scale, shift, in_tensor, out_tensor] - - s = tvm.create_schedule(out_tensor.op) - Genlib(s, tensor_list, device, opname, lib_path) - - -def CaffePReLU(device="llvm", lib_path="./", - ndim=None, dtype=None, channel_shared=None): - ''' - caffe prelu - Args: - device: - lib_path: - ndim: - dtype: - channel_shared: - - Returns: - ''' - shape = [tvm.var("n" + str(i)) for i in range(ndim)] - channel = 1 if channel_shared else shape[1] - opname = "CaffePReLU_ndim%d_%s_%s" % (ndim, dtype, - "channelShared" if channel_shared else "channelNotShared") - print(opname) - - in_tensor = tvm.placeholder(shape, dtype=dtype, name='in_tensor') - slope = tvm.placeholder((channel,), dtype=dtype, name='slope') - if channel_shared: - out_tensor = tvm.compute(shape, lambda *idx: tvm.if_then_else(in_tensor[idx] >= 0, in_tensor[idx],\ - in_tensor[idx] * slope[0])) - else: - out_tensor = tvm.compute(shape, lambda *idx: tvm.if_then_else(in_tensor[idx] >= 0, in_tensor[idx],\ - in_tensor[idx] * slope[idx[1]])) - - tensor_list = [in_tensor, slope, out_tensor] - s = tvm.create_schedule(out_tensor.op) - Genlib(s, tensor_list, device, opname, lib_path) - - -def Pad(device="llvm", lib_path="./", - ndim=None, dtype=None, paddingmode=None): - ''' - pad - Args: - device: - lib_path: - ndim: - dtype: - paddingmode: - - Returns: - ''' - shape = [tvm.var("n" + str(i)) for i in range(ndim)] - pad_before = [tvm.var("pad_before" + str(i)) for i in range(ndim)] - pad_after = [tvm.var("pad_after" + str(i)) for i in range(ndim)] - pad_before_const = [0, 0] + pad_before[2:] - pad_after_const = [0, 0] + pad_after[2:] - paddings = [None] * 2 * len(shape) - paddings[0:: 2] = pad_before - paddings[1:: 2] = pad_after - pad_value = 0 - opname = "Pad_ndim%d_%s_%s" % (ndim, dtype, paddingmode) - print(opname) - - # define compute - in_tensor = tvm.placeholder(shape, dtype=dtype, name='in_tensor') - if paddingmode == "CONSTANT": - out_tensor = topi.nn.pad(in_tensor, pad_before_const, pad_after_const, pad_value=pad_value, name='out_tensor') - else: - out_tensor = mirror_pad(in_tensor, pad_before_const, pad_after_const, mode=paddingmode, name='out_tensor') - tensor_list = paddings + [in_tensor, out_tensor] - def SchedulePad(inputs): - s = tvm.create_schedule(inputs.op) - if s[inputs].op.axis: - s[inputs].parallel(s[inputs].op.axis[1]) - return s - - s = SchedulePad(out_tensor) - Genlib(s, tensor_list, device, opname, lib_path) - - -def MatMul(device="llvm", lib_path="./", - ndim_a=None, ndim_b=None, dtype=None, transpose_a=None, transpose_b=None): - ''' - matmul - Args: - device: - lib_path: - ndim_a: - ndim_b: - dtype: - transpose_a: - transpose_b: - - Returns: - ''' - m, k, n_dim = tvm.var("m"), tvm.var("k"), tvm.var("n_dim") - a_shape = (m, k) if not transpose_a else (k, m) - b_shape = (k, n_dim) if not transpose_b else (n_dim, k) - opname = "MatMul_ndimA%d_ndimB%d_%s_%d_%d" % (ndim_a, ndim_b, dtype, transpose_a, transpose_b) - print(opname) - - # define compute - in_tensor = tvm.placeholder(a_shape, dtype=dtype, name='in_tensor') - b_tensor = tvm.placeholder(b_shape, dtype=dtype, name='b_tensor') - out_tensor = topi.matmul(in_tensor, b_tensor, transpose_a, transpose_b) - tensor_list = [in_tensor, b_tensor, out_tensor] - s = topi.generic.schedule_elemwise(out_tensor) - Genlib(s, tensor_list, device, opname, lib_path) - - -def Stack(device="llvm", lib_path="./", - ndim=None, dtype=None, input_num=None, axis=None): - ''' - stack - Args: - device: - lib_path: - ndim: - dtype: - input_num: - axis: - - Returns: - ''' - if axis > ndim: - return - shape = [tvm.var("n" + str(i)) for i in range(ndim)] - shapes = [shape] * input_num - in_tensor = [tvm.placeholder(shape, dtype=dtype, name='in_tensor%d' % i) for i, shape in enumerate(shapes)] - opname = "Stack_ndim%d_%s_input_num%d_axis%d" % (ndim, dtype, input_num, axis) - print(opname) - - input_tensor = [topi.expand_dims(ai, axis) for ai in in_tensor] - out_tensor = topi.concatenate(tuple(input_tensor), axis=axis) - tensor_list = in_tensor + [out_tensor] - if ndim < 4: - s = topi.generic.schedule_concatenate(out_tensor) - else: - s = tvm.create_schedule(out_tensor.op) - Genlib(s, tensor_list, device, opname, lib_path) - - -def ArgMax(device="llvm", lib_path="./", - ndim=None, dtype=None, axis=None, keep_dims=None, top_k=None, - out_dtype=None): - ''' - argmax - Args: - device: - lib_path: - ndim: - dtype: - axis: - keepDims: - top_k: - out_dtype: - - Returns: - ''' - if axis >= ndim: - return - shape = [tvm.var("n" + str(i)) for i in range(ndim)] - opname = "ArgMax_ndim%d_%s_axis%d_%s_top%d_%s" \ - % (ndim, dtype, axis, "keepDims" if keep_dims else "notKeepDims", top_k, out_dtype) - print(opname) - - in_tensor = tvm.placeholder(shape, dtype=dtype, name='in_tensor') - out_tensor = topi.argmax(in_tensor, axis=axis, keepdims=keep_dims) - out_tensor = AsType(out_tensor, out_dtype) - tensor_list = [in_tensor, out_tensor] - s = tvm.create_schedule(out_tensor.op) - Genlib(s, tensor_list, device, opname, lib_path) - - -def Exp(device="llvm", lib_path="./", - ndim=None, dtype=None): - ''' - exp - Args: - device: - lib_path: - ndim: - dtype: - - Returns: - ''' - shape = [tvm.var("n" + str(i)) for i in range(ndim)] - opname = "Exp_ndim%d_%s" % (ndim, dtype) - print(opname) - - # define compute - in_tensor = tvm.placeholder(shape, dtype=dtype, name='in_tensor') - if 'int' in dtype: - input_tensor = AsType(in_tensor, 'float32') - out_tensor = topi.exp(input_tensor) - out_tensor = AsType(out_tensor, in_tensor.dtype) - else: - out_tensor = topi.exp(in_tensor) - tensor_list = [in_tensor, out_tensor] - s = topi.generic.schedule_injective(out_tensor) - Genlib(s, tensor_list, device, opname, lib_path) - - -def Cast(device="llvm", lib_path="./", - ndim=None, src_dtype=None, dst_dtype=None): - ''' - cast - Args: - device: - lib_path: - ndim: - src_dtype: - dst_dtype: - - Returns: - ''' - shape = [tvm.var("n" + str(i)) for i in range(ndim)] - opname = "Cast_ndim%d_%s_%s" % (ndim, src_dtype, dst_dtype) - print(opname) - - # define compute - in_tensor = tvm.placeholder(shape, dtype=src_dtype, name='in_tensor') - out_tensor = topi.cast(in_tensor, dst_dtype) - tensor_list = [in_tensor, out_tensor] - s = topi.generic.schedule_injective(out_tensor) - Genlib(s, tensor_list, device, opname, lib_path) - - -def ExpandDims(device="llvm", lib_path="./", - ndim=None, axis=None, dtype=None): - ''' - expand dims - Args: - device: - lib_path: - ndim: - axis: - dtype: - - Returns: - ''' - if axis > ndim: - return - shape = [tvm.var("n" + str(i)) for i in range(ndim)] - opname = "ExpandDim_ndim%d_%s_axis%d" % (ndim, dtype, axis) - print(opname) - - # define compute - in_tensor = tvm.placeholder(shape, dtype=dtype, name='in_tensor') - out_tensor = topi.expand_dims(in_tensor, axis=axis) - tensor_list = [in_tensor, out_tensor] - s = topi.generic.schedule_injective(out_tensor) - Genlib(s, tensor_list, device, opname, lib_path) - - -def Tile(device="llvm", lib_path="./", - ndim=None, dtype=None): - ''' - tile - Args: - device: - lib_path: - ndim: - dtype: - - Returns: - ''' - shape = [tvm.var("n" + str(i)) for i in range(ndim)] - multiples = [tvm.var("k" + str(i)) for i in range(ndim)] - opname = "Tile_ndim%d_%s" % (ndim, dtype) - print(opname) - - def _Compute(*C_idx): - a_idx = [tvm.floordiv(idx, multiples[i]) for i, idx in enumerate(list(C_idx))] - a_idx = tuple(a_idx) - return in_tensor[a_idx] - - # define compute - in_tensor = tvm.placeholder(shape, dtype=dtype, name='in_tensor') # tvm 0.6-dev: topi.tile - shape_c = (np.array(shape) * np.array(multiples)).tolist() - out_tensor = tvm.compute(shape_c, _Compute) - - tensor_list = multiples + [in_tensor, out_tensor] - s = topi.generic.schedule_injective(out_tensor) - Genlib(s, tensor_list, device, opname, lib_path) - - -def Range(device="llvm", lib_path="./", - out_dtype=None): - ''' - range - Args: - device: - lib_path: - out_dtype: - - Returns: - ''' - start = tvm.var("start") - delta = tvm.var("delta") - opname = "Range_ndim_" + out_dtype - print(opname) - - out_tensor = tvm.compute((tvm.var("n0"),), lambda i: start.astype(out_dtype) + delta.astype(out_dtype) * i, \ - name='out_tensor') - out_tensor = AsType(out_tensor, out_dtype) - tensor_list = [start, delta, out_tensor] - s = topi.generic.schedule_injective(out_tensor) - Genlib(s, tensor_list, device, opname, lib_path) - - -def Split(device="llvm", lib_path="./", - ndim=None, dtype=None, output_num=None, axis=None): - ''' - split - Args: - device: - lib_path: - ndim: - dtype: - output_num: - axis: - - Returns: - ''' - if axis >= ndim: - return - size_splits = [tvm.var("split" + str(i)) for i in range(output_num)] - a_shape = [tvm.var("n" + str(i)) for i in range(axis)] \ - + [np.sum(size_splits)] \ - + [tvm.var("n" + str(i)) for i in range(axis + 1, ndim)] - c_shapes = [] - for i in range(output_num): - c_shape = [] - for j in range(ndim): - if j == axis: - c_shape.append(tvm.var("split" + str(i))) - else: - c_shape.append(tvm.var("n" + str(j))) - c_shapes.append(c_shape) - indices_or_sections = np.cumsum(size_splits).tolist()[:-1] - opname = "Split_ndim%d_%s_output_num%d_axis%d" % (ndim, dtype, output_num, axis) - print(opname) - - # define compute - in_tensor = tvm.placeholder(a_shape, dtype=dtype, name='in_tensor') - - def _Compute(*C_idx): - a_idx = list(C_idx) - a_idx[axis] += idx_shift - a_idx = tuple(a_idx) - return in_tensor[a_idx] - - indices_or_sections_add0 = [0] + indices_or_sections - out_tensor = [] - for i in range(output_num): - idx_shift = indices_or_sections_add0[i] - ci = tvm.compute(c_shapes[i], _Compute) - out_tensor.append(ci) - tensor_list = size_splits + [in_tensor] + out_tensor - - s = topi.generic.schedule_injective(out_tensor) - Genlib(s, tensor_list, device, opname, lib_path) diff --git a/predict/module/tvm_kernel/lite/python/at_ops/config_tool.py b/predict/module/tvm_kernel/lite/python/at_ops/config_tool.py deleted file mode 100644 index b3006b1174..0000000000 --- a/predict/module/tvm_kernel/lite/python/at_ops/config_tool.py +++ /dev/null @@ -1,211 +0,0 @@ -# Copyright 2019 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================ -""" -This module is define some data struct for tvm kernel. -""" -import tvm -import topi - -format_map = {"NCHW": 0, "NHWC": 1} - -pool_map = {"max_pool": 0, "avg_pool": 1, "global_pool": 2} - -activation_map = { - "no_activation": 0, - "relu": 1, - "sigmoid": 2, - "relu6": 3, - "elu": 4, - "leaky_relu": 5, - "abs": 6, - "relu1": 7, - "softsign": 8, - "softplus": 9, - "tanh ": 10, -} -activation_enum_map = { - "NO_ACTIVATION": 0, - "RELU": 1, - "SIGMOID": 2, - "RELU6": 3, - "elu": 4, - "leaky_relu": 5, - "abs": 6, - "relu1": 7, - "softsign": 8, - "softplus": 9, - "tanh ": 10, -} - -padmode_map = {"NOTSET": 0, "SAME": 1, "VALID": 2} - -mslite_datatype_map = { - "float16": 1, - "float32": 0, - "double": 11, - "int8": 2, - "int16": 6, - "int32": 3, - "int64": 9, - "uint8": 4, - "uint16": 7, - "uint32": 8, - "uint64": 10, -} - - -def get_key_by_value(dicts, value): - for k, v in dicts.items(): - if v == value: - return k - return None - - -def relu6(x): - return tvm.compute( - x.shape, - lambda *i: tvm.min( - tvm.max(x(*i), tvm.const(0, x.dtype)), tvm.const(6, x.dtype) - ), - ) - - -activation_topi_funs = {"NO_ACTIVATION": None, "RELU": topi.nn.relu, "RELU6": relu6} - -name_funcs = { - "Concat": ( - lambda opname, x: ( - opname + "_%d_%d" + "_%d" + "_%d" * x["ndim"] + "_%d" * len(x["shapeAxis"]) - ) - % ( - format_map[x["format"]], - x["ndim"], - x["axis"], - *x["shapeOut"], - *x["shapeAxis"], - ) - ), - "Softmax": ( - lambda opname, x: (opname + "_%d_%d" + "_%d" * x["ndim"] + "_%d") - % (format_map[x["format"]], x["ndim"], *x["shape"], x["axis"]) - ), - "Activation": ( - lambda opname, x: (opname + "_%d_%d" + "_%d" + "_%d" * x["ndim"]) - % (format_map[x["format"]], x["ndim"], activation_map[x["type"]], *x["shape"]) - ), - "Add": ( - lambda opname, x: (opname + "_%d_%d" + "_%d" * x["ndim"]) - % (format_map[x["format"]], x["ndim"], *x["shape"]) - ), - "Convolution": ( - lambda opname, x: ( - opname + "_%d_%d_%d_%d_%d_%d_%d_%d_%d_%d_%d_%d_%d_%d_%d_%d_%d_%d" - ) - % ( - format_map[x["format"]], - x["ndim"], - x["batch"], - x["in_channel"], - *x["in_size"], - x["num_filter"], - *x["filter_size"], - *x["pad"], - *x["stride"], - x["dilation"], - x["hasbias"], - activation_map[x["activation_type"]], - ) - ), - "Identity": ( - lambda opname, x: (opname + "_%d_%d" + "_%d" * x["ndim"]) - % (format_map[x["format"]], x["ndim"], *x["shape"]) - ), - "BatchNorm": ( - lambda opname, x: (opname + "_%d_%d" + "_%d" * x["ndim"] + "_%d") - % (format_map[x["format"]], x["ndim"], *x["shape"], x["epsilon"]) - ), - "Squeeze": ( - lambda opname, x: ( - opname + "_%d_%d" + "_%d" * x["ndim"] + "_%d" * len(x["axis"]) - ) - % (format_map[x["format"]], x["ndim"], *x["shape"], *x["axis"]) - ), - "BiasAdd": ( - lambda opname, x: (opname + "_%d_%d" + "_%d" * x["ndim"] + "_%d") - % (format_map[x["format"]], x["ndim"], *x["shape"], x["axis"]) - ), - "Pooling": ( - lambda opname, x: (opname + "_%d_%d_%d" + "_%d" * x["ndim"] + "_%d_%d_%d") - % ( - format_map[x["format"]], - x["ndim"], - pool_map[x["type"]], - *x["shape"], - x["kernel"], - x["stride"], - x["pad"], - ) - ), - "ConvolutionDepthwise": ( - lambda opname, x: ( - opname + "_%d_%d_%d_%d_%d_%d_%d_%d_%d_%d_%d_%d_%d_%d_%d_%d_%d_%d" - ) - % ( - format_map[x["format"]], - x["ndim"], - x["batch"], - x["in_channel"], - *x["in_size"], - x["in_channel"] * x["channel_multiplier"], - *x["filter_size"], - *x["pad"], - *x["stride"], - x["dilation"], - x["hasbias"], - activation_map[x["activation_type"]], - ) - ), - "Reshape": ( - lambda opname, x: ( - opname + "_%d_%d" + "_%d" * x["ndimA"] + "_%d" * len(x["shapeB"]) - ) - % (format_map[x["format"]], x["ndimA"], *x["shapeA"], *x["shapeB"]) - ), - "Shape": ( - lambda opname, x: (opname + "_%d_%d" + "_%d" * x["ndim"]) - % (format_map[x["format"]], x["ndim"], *x["shape"]) - ), - "RealDiv": ( - lambda opname, x: ( - opname + "_%d_%d" + "_%d" * x["ndim"] + "_%d" * len(x["shapeB"]) - ) - % (format_map[x["format"]], x["ndim"], *x["shapeA"], *x["shapeB"]) - ), - "ResizeBilinear": (lambda opname, x: "ResizeBilinear"), - "TFLite_Detection_PostProcess": (lambda opname, x: "TFLite_Detection_PostProcess"), -} - -config_dict = {op_type: [] for op_type in name_funcs} - - -def config_dict_append(op_type, config, opname=None): - if opname is None: - config["opname"] = name_funcs[op_type](op_type, config) - else: - config["opname"] = opname - duplicate = [True for x in config_dict[op_type] if config == x] - - if duplicate: - config_dict[op_type].append(config) diff --git a/predict/module/tvm_kernel/lite/python/at_rt/at_runtime_reset.py b/predict/module/tvm_kernel/lite/python/at_rt/at_runtime_reset.py deleted file mode 100644 index cc0cc72885..0000000000 --- a/predict/module/tvm_kernel/lite/python/at_rt/at_runtime_reset.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright 2019 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================ -""" -This module is Using to make Lite Runtime funcitons instead TVM Runtime funcitons while codegen. -""" - -import os -from tvm import codegen - -class AtRuntimeReset(): - """Using this class to make Lite Runtime funcitons instead TVM Runtime funcitons while codegen - Usage like: - with at_runtime_reset.AtRuntimeReset(): - fadd = tvm.build(s, [A, B], tgt, target_host = tgt_host, name = "myadd") - then the module fadd will using Lite runtime functions. - """ - - def __enter__(self): - if os.getenv("TVM_RUNTIME_ON") is not None: - return - codegen.SetRTFuncTransPair( - "TVMBackendAllocWorkspace", "LiteBackendAllocWorkspace" - ) - codegen.SetRTFuncTransPair( - "TVMBackendFreeWorkspace", "LiteBackendFreeWorkspace" - ) - codegen.SetRTFuncTransPair("TVMAPISetLastError", "LiteAPISetLastError") - codegen.SetRTFuncTransPair( - "TVMBackendParallelLaunch", "LiteBackendParallelLaunch" - ) - codegen.SetRTFuncTransPair( - "TVMBackendParallelBarrier", "LiteBackendParallelBarrier" - ) - codegen.SetRTFuncTransPair( - "TVMBackendRegisterSystemLibSymbol", "LiteBackendRegisterSystemLibSymbol" - ) - codegen.SetRTFuncTransPair("TVMFuncCall", "LiteFuncCall") - codegen.SetRTFuncTransPair( - "TVMBackendGetFuncFromEnv", "LiteBackendGetFuncFromEnv" - ) - - def __exit__(self, ptype, value, trace): - codegen.DelRTFuncTransPair("TVMBackendAllocWorkspace") - codegen.DelRTFuncTransPair("TVMBackendFreeWorkspace") - codegen.DelRTFuncTransPair("TVMAPISetLastError") - codegen.DelRTFuncTransPair("TVMBackendParallelLaunch") - codegen.DelRTFuncTransPair("TVMBackendParallelBarrier") - codegen.DelRTFuncTransPair("TVMBackendRegisterSystemLibSymbol") - codegen.DelRTFuncTransPair("TVMFuncCall") - codegen.DelRTFuncTransPair("TVMBackendGetFuncFromEnv") diff --git a/predict/module/tvm_kernel/lite/src/api/kernel_manager.cc b/predict/module/tvm_kernel/lite/src/api/kernel_manager.cc deleted file mode 100644 index b349ae6019..0000000000 --- a/predict/module/tvm_kernel/lite/src/api/kernel_manager.cc +++ /dev/null @@ -1,1772 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this ${file} except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LITE_RUNTIME_ON - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "common/mslog.h" - -const char *LIB_INFO = "libtvm_kernel version: master (c66c6b28dc991c9d705e1b983aab7385c337128d)"; -namespace km { -class KernelManager { - public: - int CallKernel(const std::string &fid, TVMArgs args) { - tvm::runtime::Module *mod = this->GetModule(); - CHECK(mod != nullptr) << "Failed to get Module!"; - const std::string name = fid; - tvm::runtime::PackedFunc f = mod->GetFunction(name, false); - CHECK(f != nullptr) << "Can't find kernel func " << fid; - TVMRetValue rv; - f.CallPacked(args, &rv); - return 0; - } - - void InitKernelManager(int mode, const std::string &fname) { return this->Init(mode, fname); } - - static KernelManager *Global() { - static KernelManager inst; - return &inst; - } - - tvm::runtime::Module *GetModule() const { return &g_modLib; } - - private: - KernelManager() = default; - - ~KernelManager() = default; - - void Init(int mode, std::string fpath) { - std::call_once(init_flag, &KernelManager::InitLib, mode, fpath); - return; - } - - static void InitLib(int mode, std::string fpath) { - if (mode) { - const PackedFunc *ptr = tvm::runtime::Registry::Get("module._GetSystemLib"); - CHECK(ptr != nullptr) << "Failed to get systemlib"; - g_modLib = (*ptr)(); - } else { - g_modLib = tvm::runtime::Module::LoadFromFile(fpath); - } - } - static tvm::runtime::Module g_modLib; - std::once_flag init_flag; -}; - -tvm::runtime::Module KernelManager::g_modLib; -} // namespace km - -std::function &)> GetKernel(const std::string &fid) { - km::KernelManager *inst = km::KernelManager::Global(); - CHECK(inst != nullptr) << "Failed to get KernelManager instance!"; - tvm::runtime::Module *mod = inst->GetModule(); - CHECK(mod != nullptr) << "Failed to get Module!"; - tvm::runtime::PackedFunc f = mod->GetFunction(fid, false); - if (f == nullptr) { - MS_LOGE("GetFunction return nullptr"); - return nullptr; - } - auto runner = [f](const std::vector &tensors) -> int { - int argLen = tensors.size(); - CHECK(argLen) << "Input tensors num=0 !"; - std::vector values(argLen); - std::vector codes(argLen); - tvm::runtime::TVMArgsSetter setter(values.data(), codes.data()); - for (int i = 0; i < argLen; ++i) { - setter(i, tensors.at(i)); - } - tvm::runtime::TVMArgs targs(values.data(), codes.data(), argLen); - TVMRetValue rv; - f.CallPacked(targs, &rv); - return 0; - }; - return runner; -} - -int CallKernel(const std::string &fid, const std::vector &tensors) { - km::KernelManager *inst = km::KernelManager::Global(); - CHECK(inst != nullptr) << "Failed to get KernelManager instance!"; - int argLen = tensors.size(); - CHECK(argLen) << "Input tensors num=0 !"; - std::vector values(argLen); - std::vector codes(argLen); - tvm::runtime::TVMArgsSetter setter(values.data(), codes.data()); - for (int i = 0; i < argLen; ++i) { - setter(i, tensors.at(i)); - } - tvm::runtime::TVMArgs targs(values.data(), codes.data(), argLen); - inst->CallKernel(fid, targs); - return 0; -} - -int InitKernelManager(int mode, const std::string &fname) { - km::KernelManager *inst = km::KernelManager::Global(); - CHECK(inst != nullptr) << "Failed to get KernelManager instance!"; - inst->InitKernelManager(mode, fname); - return 0; -} - -// just for api compatible, tvm/lite has same api -void ConfigThreadPool(int mode = 1, int nthreads = 0, bool execute_self = true) {} - -#else - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "flatbuffers/flatbuffers.h" -#include "schema/inner/ms_generated.h" -#include "include/securec.h" -#include "src/runtime/runtime_api.h" -#include "common/mslog.h" - -using runnerType = std::function &)>; - -const char *LIB_INFO = "libtvm_kernel version: master (c66c6b28dc991c9d705e1b983aab7385c337128d)"; - -namespace lite { -namespace runtime { -extern "C" { -// Function signature for generated packed function in shared library -typedef int (*BackendPackedCFunc)(const void *args, int *type_codes, int num_args); -} // extern "C" - -class LiteFuncPool { - public: - LiteFuncPool() = default; - - ~LiteFuncPool() = default; - - void GetFunction(const std::string &name, void **func_addr) { - auto it = tbl_.find(name); - if (func_addr == nullptr) { - MS_LOGW("input func_addr is nullptr"); - return; - } - *func_addr = (it != tbl_.end() ? it->second : nullptr); - } - - void RegisterSymbol(const std::string &name, void *ptr) { - std::lock_guard lock(mutex_); - auto it = tbl_.find(name); - if (it != tbl_.end() && ptr != it->second) { - MS_LOGW("Lite symbol %s get overriden to a different address %p->%p", name.c_str(), ptr, it->second); - } - tbl_[name] = ptr; - } - - static LiteFuncPool *Global() { - static LiteFuncPool inst; - return &inst; - } - - private: - // Internal mutex - std::mutex mutex_; - // Internal symbol table - std::unordered_map tbl_; -}; -} // namespace runtime -} // namespace lite - -using LiteFuncPool = lite::runtime::LiteFuncPool; -using BackendPackedCFunc = lite::runtime::BackendPackedCFunc; - -int LiteBackendRegisterSystemLibSymbol(const char *name, void *ptr) { - MS_ASSERT(LiteFuncPool::Global() != nullptr); - LiteFuncPool::Global()->RegisterSymbol(name, ptr); - return 0; -} - -// do nothing, api compatible with TVM_RUNTIME_ON API -void InitKernelManager(int mode, const std::string &fname) { return; } - -static inline void *GetFunction(const std::string &fid) { - void *f = nullptr; - MS_ASSERT(LiteFuncPool::Global() != nullptr); - LiteFuncPool::Global()->GetFunction(fid, &f); - if (f == nullptr) { - return nullptr; - } - return f; -} - -runnerType __attribute__((noinline)) GetKernel(const std::string &fid) { - auto f = GetFunction(fid); - if (f == nullptr) { - return nullptr; - } - auto runner = [f](const std::vector &tensors) -> int { - if (tensors.empty()) { - MS_LOGE("Input tensors num = 0 !"); - return -1; - } - std::vector values(tensors.size()); - std::vector codes(tensors.size()); - tvm::runtime::TVMArgsSetter setter(values.data(), codes.data()); - for (size_t i = 0; i < tensors.size(); ++i) { - setter(i, tensors.at(i)); - } - auto passfunc = reinterpret_cast(f); - return passfunc(values.data(), codes.data(), tensors.size()); - }; - return runner; -} - -namespace auto_tensor { -constexpr int TENSOR_NUM_MAX = 10; -constexpr bool STORE_MODE = true; -constexpr bool RESUME_MODE = false; -const char *NOT_SUPPORT = "NOT SUPPORT"; -const int NCHW_N = 0; -const int NCHW_C = 1; -const int NCHW_H = 2; -const int NCHW_W = 3; -const int tile = 4; - -void store_shape(const std::vector &tensors, int (&ndim)[TENSOR_NUM_MAX], int64_t *(&shape)[TENSOR_NUM_MAX], - int64_t *(&strides)[TENSOR_NUM_MAX], bool mode = STORE_MODE) { - if (mode == STORE_MODE) { - for (size_t i = 0; i < tensors.size(); ++i) { - ndim[i] = tensors[i]->ndim; - shape[i] = tensors[i]->shape; - strides[i] = tensors[i]->strides; - } - } else { - for (size_t i = 0; i < tensors.size(); ++i) { - tensors[i]->ndim = ndim[i]; - tensors[i]->shape = shape[i]; - tensors[i]->strides = strides[i]; - } - } -} - -static std::string get_dtype(const DLTensor &tensor) { - auto dtype = tensor.dtype; - if (dtype.code == kDLFloat) { - if (dtype.bits == 16) - return "float16"; - else if (dtype.bits == 32) - return "float32"; - else if (dtype.bits == 64) - return "float64"; - } else if (dtype.code == kDLInt) { - if (dtype.bits == 8) - return "int8"; - else if (dtype.bits == 16) - return "int16"; - else if (dtype.bits == 32) - return "int32"; - else if (dtype.bits == 64) - return "int64"; - } else if (dtype.code == kDLUInt) { - if (dtype.bits == 8) - return "uint8"; - else if (dtype.bits == 16) - return "uint16"; - else if (dtype.bits == 32) - return "uint32"; - else if (dtype.bits == 64) - return "uint64"; - } - return std::string(NOT_SUPPORT); -} - -struct OpCommonAttr { - std::string optype = ""; - std::string fid = ""; - uint32_t ndim = 0; - std::string dtype = "float32"; - - OpCommonAttr(const mindspore::predict::OpDef &opdef, const std::vector &tensors) { - auto opT = mindspore::predict::EnumNameOpT(opdef.attr_type()); - this->optype = opT; - MS_ASSERT(opdef.name() != nullptr); - this->fid = opdef.name()->str(); - if (!tensors.empty()) { - MS_ASSERT(tensors.front() != nullptr); - ndim = tensors.front()->ndim; - dtype = get_dtype(*tensors.front()); - } - } -}; - -template -static void NCHW2NHWC(DLTensor *src) { - if (src == nullptr) { - MS_LOGW("input src is nullptr"); - return; - } - T *src_data = static_cast(src->data); - std::unique_ptr tmp(new (std::nothrow) - T[src->shape[NCHW_N] * src->shape[NCHW_C] * src->shape[NCHW_H] * src->shape[NCHW_W]]); - if (tmp == nullptr) { - MS_LOGW("new tmp buf failed"); - return; - } - int N = src->shape[NCHW_N]; - int C = src->shape[NCHW_C]; - int H = src->shape[NCHW_H]; - int W = src->shape[NCHW_W]; - - // NCHW -> NHWC - int k = 0; - for (int n = 0; n < N; n++) - for (int h = 0; h < H; h++) - for (int w = 0; w < W; w++) - for (int c = 0; c < C; c++) { - tmp[k++] = src_data[n * C * H * W + c * H * W + h * W + w]; - } - - int sizes = N * C * H * W * sizeof(T); - errno_t ret = memcpy_s(src_data, sizes, tmp.get(), sizes); - if (ret != 0) { - MS_LOGW("memcpy_s failed: %d", ret); - return; - } -} - -static void transpose_shape(DLTensor *tensor, std::vector axis) { - if (tensor == nullptr) { - MS_LOGW("input tensor is nullptr"); - return; - } - int ndim = tensor->ndim; - std::vector origin_shape(tensor->shape, tensor->shape + ndim); - - for (int i = ndim - 1; i >= 0; --i) { - tensor->shape[i] = origin_shape[axis[i]]; - } -} - -static runnerType Pack_NCHW2NHWC(runnerType fun) { - if (fun == nullptr) { - MS_LOGE("input fun is nullptr"); - return nullptr; - } - auto runner = [fun](const std::vector &tensors) -> int { - if (tensors.back() == nullptr) { - MS_LOGE("tensors.back() is nullptr"); - return 1; - } - transpose_shape(tensors.back(), {0, 3, 1, 2}); // NHWC -> NCHW - fun(tensors); - - auto output = tensors.back(); - if (output == nullptr) { - MS_LOGE("tensors.back() after func is nullptr"); - return 1; - } - if (output->dtype.bits == 8) { - NCHW2NHWC(output); - } else if (output->dtype.bits == 16) { - NCHW2NHWC(output); - } else if (output->dtype.bits == 32) { - NCHW2NHWC(output); - } else if (output->dtype.bits == 64) { - NCHW2NHWC(output); - } else { - MS_LOGE("conv NCHW2NHWC output.dtype.bits=%d invalid, only support (8, 16, 32, 64)", output->dtype.bits); - return 1; - } - - if (tensors.back() == nullptr) { - MS_LOGE("tensors.back() is nullptr"); - return 1; - } - transpose_shape(tensors.back(), {0, 2, 3, 1}); // NCHW -> NHWC - return 0; - }; - return runner; -} - -runnerType __attribute__((noinline)) GetKernel_Insert_vector_int32(const std::string &fid, - const std::vector &vec) { - auto f = GetFunction(fid); - if (f == nullptr) { - MS_LOGE("GetFunction return nullptr"); - return nullptr; - } - auto runner = [f, vec](const std::vector &tensors) -> int { - std::vector values(vec.size() + tensors.size()); - std::vector codes(values.size()); - tvm::runtime::TVMArgsSetter setter(values.data(), codes.data()); - for (size_t i = 0; i < vec.size(); ++i) { - setter(i, vec.at(i)); - } - for (size_t i = 0; i < tensors.size(); ++i) { - setter(i + vec.size(), tensors.at(i)); - } - auto passfunc = reinterpret_cast(f); - return passfunc(values.data(), codes.data(), values.size()); - }; - return runner; -} - -runnerType __attribute__((noinline)) GetKernel_Insert_vector_float(const std::string &fid, - const std::vector &vec) { - auto f = GetFunction(fid); - if (f == nullptr) { - MS_LOGE("GetFunction return nullptr"); - return nullptr; - } - auto runner = [f, vec](const std::vector &tensors) -> int { - std::vector values(vec.size() + tensors.size()); - std::vector codes(values.size()); - tvm::runtime::TVMArgsSetter setter(values.data(), codes.data()); - for (size_t i = 0; i < vec.size(); ++i) { - setter(i, vec.at(i)); - } - for (size_t i = 0; i < tensors.size(); ++i) { - setter(i + vec.size(), tensors.at(i)); - } - auto passfunc = reinterpret_cast(f); - return passfunc(values.data(), codes.data(), values.size()); - }; - return runner; -} - -static runnerType GetKernel_Conv(const mindspore::predict::OpDef &opdef, const std::vector &tensors, - const KernelOption &option) { - if (tensors.at(0) == nullptr) { - MS_LOGE("input tensors.at(0) is nullptr"); - return nullptr; - } - int n = tensors.at(0)->shape[NCHW_N]; - int ci = tensors.at(0)->shape[NCHW_C]; - int h = tensors.at(0)->shape[NCHW_H]; - int w = tensors.at(0)->shape[NCHW_W]; - std::vector arg_const{n, ci, h, w}; - const OpCommonAttr opAttr(opdef, tensors); - - std::string fid; - if (opdef.attr_as_Conv2D() != nullptr) { - auto op = opdef.attr_as_Conv2D(); - fid = std::string(mindspore::predict::EnumNameOpT(opdef.attr_type())) + "_ndim" + std::to_string(opAttr.ndim) + - "_" + opAttr.dtype + "_k" + std::to_string(op->kernelH()) + "_s" + std::to_string(op->strideH()) + "_p" + - std::to_string(op->padUp()) + std::to_string(op->padDown()) + std::to_string(op->padLeft()) + - std::to_string(op->padRight()) + "_d" + std::to_string(op->dilateH()) + "_act" + - std::to_string(static_cast(op->activationType())) + "_vc" + std::to_string(1) + "_vh" + - std::to_string(1) + "_vw" + std::to_string(1) + "_hasbias" + std::to_string(op->hasBias()); - if (tensors.at(1) == nullptr) { - MS_LOGE("input tensors.at(1) is nullptr"); - return nullptr; - } - int co = tensors.at(1)->shape[NCHW_N]; - arg_const.push_back(co); - } else if (opdef.attr_as_DepthwiseConv2D() != nullptr) { - auto op = opdef.attr_as_DepthwiseConv2D(); - fid = std::string(mindspore::predict::EnumNameOpT(opdef.attr_type())) + "_ndim" + std::to_string(opAttr.ndim) + - "_" + opAttr.dtype + "_k" + std::to_string(op->kernelH()) + "_s" + std::to_string(op->strideH()) + "_p" + - std::to_string(op->padUp()) + std::to_string(op->padDown()) + std::to_string(op->padLeft()) + - std::to_string(op->padRight()) + "_d" + std::to_string(op->dilateH()) + "_act" + - std::to_string(static_cast(op->activationType())) + "_vc" + std::to_string(1) + "_vh" + - std::to_string(1) + "_vw" + std::to_string(1) + "_hasbias" + std::to_string(op->hasBias()); - int co = tensors.at(0)->shape[NCHW_C] * op->channelMultiplier(); - arg_const.push_back(co); - } else if (opdef.attr_as_DeDepthwiseConv2D() != nullptr) { - auto op = opdef.attr_as_DeDepthwiseConv2D(); - fid = std::string(mindspore::predict::EnumNameOpT(opdef.attr_type())) + "_ndim" + std::to_string(opAttr.ndim) + - "_" + opAttr.dtype + "_k" + std::to_string(op->kernelH()) + "_s" + std::to_string(op->strideH()) + "_p" + - std::to_string(op->padUp()) + std::to_string(op->padDown()) + std::to_string(op->padLeft()) + - std::to_string(op->padRight()) + "_d" + std::to_string(op->dilateH()) + "_act" + - std::to_string(static_cast(op->activationType())) + "_vc" + std::to_string(1) + "_vh" + - std::to_string(1) + "_vw" + std::to_string(1) + "_hasbias" + std::to_string(op->hasBias()); - int co = tensors.at(0)->shape[NCHW_C] * op->channelMultiplier(); - arg_const.push_back(co); - } - auto fun = GetKernel(fid); - if (fun == nullptr) { - MS_LOGE("GetKernel return nullptr"); - return nullptr; - } - - auto f = GetFunction(fid); - if (f == nullptr) { - MS_LOGE("GetFunction return nullptr"); - return nullptr; - } - auto runner = [f, arg_const](const std::vector &tensors) -> int { - int ndim[TENSOR_NUM_MAX]; - int64_t *shapes[TENSOR_NUM_MAX]; - int64_t *strides[TENSOR_NUM_MAX]; - store_shape(tensors, ndim, shapes, strides, STORE_MODE); - - std::vector values(arg_const.size() + tensors.size()); - std::vector codes(values.size()); - tvm::runtime::TVMArgsSetter setter(values.data(), codes.data()); - for (size_t i = 0; i < arg_const.size(); ++i) { - setter(i, arg_const.at(i)); - } - for (size_t i = 0; i < tensors.size(); ++i) { - setter(i + arg_const.size(), tensors.at(i)); - } - auto passfunc = reinterpret_cast(f); - passfunc(values.data(), codes.data(), values.size()); - store_shape(tensors, ndim, shapes, strides, RESUME_MODE); - return 0; - }; - fun = runner; - - if (opdef.isLastConv()) { - return Pack_NCHW2NHWC(fun); - } - return fun; -} - -void update_shape_NC4HW4(const std::vector &tensors, int64_t (&shapeA)[TENSOR_NUM_MAX], - int64_t (&shapeC)[TENSOR_NUM_MAX]) { - auto inputA = tensors.front(); - auto output = tensors.back(); - if (inputA == nullptr) { - MS_LOGW("input tensors.front() is nullptr"); - return; - } - if (output == nullptr) { - MS_LOGW("input tensors.back() is nullptr"); - return; - } - shapeA[inputA->ndim] = tile; - for (int32_t i = 0; i < inputA->ndim; ++i) { - if (i == 1) { - shapeA[i] = inputA->shape[i] >> 2; - } else { - shapeA[i] = inputA->shape[i]; - } - } - { - inputA->ndim = inputA->ndim + 1; - inputA->shape = shapeA; - inputA->strides = nullptr; - } - shapeC[output->ndim] = tile; - for (int32_t i = 0; i < output->ndim; ++i) { - if (i == 1) { - shapeC[i] = output->shape[i] >> 2; - } else { - shapeC[i] = output->shape[i]; - } - } - { - output->ndim = output->ndim + 1; - output->shape = shapeC; - output->strides = nullptr; - } -} - -static runnerType GetKernel_Conv_var(const mindspore::predict::OpDef &opdef, const std::vector &tensors, - const KernelOption &option) { - const OpCommonAttr opAttr(opdef, tensors); - auto fun = GetKernel(opAttr.fid); - if (tensors.at(0) == nullptr) { - MS_LOGE("input tensors.at(0) is nullptr"); - return nullptr; - } - std::string fid = opAttr.fid.substr(0, opAttr.fid.find('_')); - int n = tensors.at(0)->shape[NCHW_N]; - int ci = tensors.at(0)->shape[NCHW_C]; - int h = tensors.at(0)->shape[NCHW_H]; - int w = tensors.at(0)->shape[NCHW_W]; - int co = tensors.at(1)->shape[NCHW_C]; - std::vector arg_const{n, ci >> 2, h, w, co}; - if (fun == nullptr) { - auto fd = [](int h, std::vector &res) { - for (int i = 2; i <= h; i += 2) { - if ((h % i) == 0) res.emplace_back(i); - } - }; - int outidx = tensors.size() - 1; - std::vector vw; - if (tensors.at(outidx) == nullptr) { - MS_LOGE("input tensors.at(%d) is nullptr", outidx); - return nullptr; - } - fd(tensors.at(outidx)->shape[NCHW_W], vw); - - auto op = opdef.attr_as_DeConv2D(); - if (op == nullptr) { - MS_LOGE("opdef.attr_as_DeConv2D() is nullptr"); - return nullptr; - } - std::string fids; - for (auto iter = vw.rbegin(); iter != vw.rend(); iter++) { - fids = fid + "_ndim" + std::to_string(opAttr.ndim + 1) + "_" + opAttr.dtype + "_k" + - std::to_string(op->kernelH()) + "_s" + std::to_string(op->strideH()) + "_p" + std::to_string(op->padUp()) + - std::to_string(op->padDown()) + std::to_string(op->padLeft()) + std::to_string(op->padRight()) + "_d" + - std::to_string(op->dilateH()) + "_act" + std::to_string(static_cast(op->activationType())) + "_vc" + - std::to_string(4) + "_vh" + std::to_string(2) + "_vw" + std::to_string(*iter) + "_hasbias" + - std::to_string(op->hasBias()); - fun = GetKernel(fids); - if (fun != nullptr) { - break; - } - } - fid = fids; - if (fun == nullptr) { - MS_LOGE("fun is nullptr"); - return nullptr; - } - auto f = GetFunction(fid); - if (f == nullptr) { - MS_LOGE("GetFunction return nullptr"); - return nullptr; - } - auto runner = [f, arg_const](const std::vector &tensors) -> int { - int ndim[TENSOR_NUM_MAX]; - int64_t *shapes[TENSOR_NUM_MAX]; - int64_t *strides[TENSOR_NUM_MAX]; - int64_t shapeA[TENSOR_NUM_MAX]; - int64_t shapeC[TENSOR_NUM_MAX]; - store_shape(tensors, ndim, shapes, strides, STORE_MODE); - update_shape_NC4HW4(tensors, shapeA, shapeC); - - std::vector values(arg_const.size() + tensors.size()); - std::vector codes(values.size()); - tvm::runtime::TVMArgsSetter setter(values.data(), codes.data()); - for (size_t i = 0; i < arg_const.size(); ++i) { - setter(i, arg_const.at(i)); - } - for (size_t i = 0; i < tensors.size(); ++i) { - setter(i + arg_const.size(), tensors.at(i)); - } - auto passfunc = reinterpret_cast(f); - passfunc(values.data(), codes.data(), values.size()); - store_shape(tensors, ndim, shapes, strides, RESUME_MODE); - return 0; - }; - fun = runner; - } - - if (opdef.isLastConv()) { - return Pack_NCHW2NHWC(fun); - } - return fun; -} - -enum reahpeCHW_Mode { FusedCHW, ExpandCHW }; - -void update_shape_reahpeCHW(const std::vector &tensors, reahpeCHW_Mode mode, int64_t (&shape)[4], - int64_t (&strides)[4], bool reahpe_output = false) { - auto input = tensors.front(); - auto output = tensors.back(); - if (input == nullptr) { - MS_LOGW("input tensors.front() is nullptr"); - return; - } - if (output == nullptr) { - MS_LOGW("input tensors.back() is nullptr"); - return; - } - int ndim; - if (mode == FusedCHW) { - ndim = 2; - int64_t CHW = 1; - for (int32_t i = 1; i < input->ndim; ++i) { - CHW *= input->shape[i]; - } - shape[NCHW_N] = input->shape[NCHW_N]; - shape[NCHW_C] = CHW; - strides[1] = 1; - strides[0] = CHW; - } else { - ndim = 4; - shape[NCHW_N] = input->shape[NCHW_N]; - shape[NCHW_C] = input->shape[NCHW_C]; - shape[NCHW_H] = 1; - shape[NCHW_W] = 1; - strides[3] = 1; - strides[2] = 1; - strides[1] = 1; - strides[0] = input->shape[NCHW_C]; - } - - input->ndim = ndim; - input->shape = shape; - input->strides = strides; - if (reahpe_output) { - output->ndim = ndim; - output->shape = shape; - output->strides = strides; - } -} - -static runnerType Pack_reahpeCHW(const runnerType &fun, const std::vector &tensors, reahpeCHW_Mode mode, - bool reahpe_output = false) { - if (fun == nullptr) { - MS_LOGE("input fun is nullptr"); - return nullptr; - } - if (tensors.front() == nullptr) { - MS_LOGE("input tensors.front() is nullptr"); - return nullptr; - } - if ((tensors.front()->ndim == 2 && mode == FusedCHW) || (tensors.front()->ndim == 4 && mode == ExpandCHW)) { - return fun; - } - - auto runner = [fun, mode, reahpe_output](const std::vector &tensors) -> int { - int ndim[TENSOR_NUM_MAX]; - int64_t *shape[TENSOR_NUM_MAX]; - int64_t *strides[TENSOR_NUM_MAX]; - int64_t shape_R[4]; - int64_t strides_R[4]; - store_shape(tensors, ndim, shape, strides, STORE_MODE); - update_shape_reahpeCHW(tensors, mode, shape_R, strides_R, reahpe_output); - fun(tensors); - store_shape(tensors, ndim, shape, strides, RESUME_MODE); - return 0; - }; - return runner; -} - -static runnerType GetKernel_BatchNorm(const mindspore::predict::OpDef &opdef, const std::vector &tensors, - const KernelOption &option) { - const OpCommonAttr opAttr(opdef, tensors); - std::string fid; - std::vector epsilon(1, 0.001); - if (opAttr.optype == "BatchNorm") { - fid = "TFBatchNorm_ndim" + std::to_string(opAttr.ndim) + "_" + opAttr.dtype + "_axis1"; - epsilon.front() = opdef.attr_as_FusedBatchNorm()->epsilon(); - return GetKernel_Insert_vector_float(fid, epsilon); - } else if (opAttr.optype == "CaffeBatchNorm") { - fid = "CaffeBatchNorm_ndim4_" + opAttr.dtype + "_axis1"; - epsilon.front() = opdef.attr_as_CaffeBatchNorm()->epsilon(); - auto fun = GetKernel_Insert_vector_float(fid, epsilon); - if (fun == nullptr) { - MS_LOGE("GetKernel_Insert_vector_float return nullptr"); - return nullptr; - } - bool reahpe_output = true; - return Pack_reahpeCHW(fun, tensors, ExpandCHW, reahpe_output); - } else if (opAttr.optype == "BiasAdd") { - auto op = opdef.attr_as_BiasAdd(); - fid = "TFBiasAdd_ndim" + std::to_string(opAttr.ndim) + "_" + opAttr.dtype + "_axis" + - std::to_string(op->axis()->Get(0)); - return GetKernel(fid); - } else if (opAttr.optype == "Scale") { - fid = "CaffeScale_ndim" + std::to_string(opAttr.ndim) + "_" + opAttr.dtype + "_axis1"; - return GetKernel(fid); - } - return nullptr; -} - -void update_shape_flatten(const std::vector &tensors, int64_t *shape, int64_t *strides) { - auto inputA = tensors.back(); - if (inputA == nullptr) { - MS_LOGW("input tensors.back() is nullptr"); - return; - } - for (int32_t i = 0; i < inputA->ndim; ++i) { - *shape *= inputA->shape[i]; - } - for (size_t i = 0; i < tensors.size(); ++i) { - tensors[i]->ndim = 1; - tensors[i]->shape = shape; - tensors[i]->strides = strides; - } -} - -std::string GetEltwiseMode(const OpCommonAttr &opAttr, const mindspore::predict::OpDef &opdef) { - const auto optype = opAttr.optype; - std::string mode = "add"; - if (optype == "Eltwise") { - auto op_mode = opdef.attr_as_Eltwise()->mode(); - if (mindspore::predict::EltwiseMode_PROD == op_mode) { - mode = "multiply"; - } else if (mindspore::predict::EltwiseMode_SUM == op_mode) { - mode = "add"; - } else if (mindspore::predict::EltwiseMode_MAXIMUM == op_mode) { - mode = "maximum"; - } - } else { - if ("Add" == optype) { - mode = "add"; - } else if ("Sub" == optype) { - mode = "subtract"; - } else if ("Mul" == optype) { - mode = "multiply"; - } else if ("RealDiv" == optype) { - mode = "divide"; - } else if ("Maximum" == optype) { - mode = "maximum"; - } - } - return mode; -} - -bool IsSwap(const std::vector &tensors) { - auto CalShape = [](DLTensor *tensor) -> int { - int res = 1; - if (tensor == nullptr) { - MS_LOGE("input DLTensor is nullptr"); - return -1; - } - for (int i = 0; i < tensor->ndim; ++i) { - res *= tensor->shape[i]; - } - return res; - }; - - MS_ASSERT(tensors[0] != nullptr); - MS_ASSERT(tensors[1] != nullptr); - auto ndimA = tensors[0]->ndim; - auto ndimB = tensors[1]->ndim; - bool isSwap = false; - - if (ndimA <= ndimB) { - auto AShape = CalShape(tensors[0]); - auto BShape = CalShape(tensors[1]); - if (AShape < BShape) { - isSwap = true; - } - } - return isSwap; -} - -static runnerType GetKernel_Eltwise(const mindspore::predict::OpDef &opdef, const std::vector &tensors, - const KernelOption &option) { - const OpCommonAttr opAttr(opdef, tensors); - std::string mode = GetEltwiseMode(opAttr, opdef); - - // make fid - int indexA = 0; - int indexB = 1; - MS_ASSERT(tensors[0] != nullptr); - MS_ASSERT(tensors[1] != nullptr); - auto ndimA = tensors[0]->ndim; - auto ndimB = tensors[1]->ndim; - - bool isSwap = IsSwap(tensors); - if (isSwap) { - std::swap(ndimA, ndimB); - std::swap(indexA, indexB); - } - - MS_ASSERT(tensors[indexA] != nullptr); - MS_ASSERT(tensors[indexB] != nullptr); - if (ndimA == 1 && tensors[indexA]->shape[NCHW_N] == 1) { - ndimA = 0; - } - if (ndimB == 1 && tensors[indexB]->shape[NCHW_N] == 1) { - ndimB = 0; - } - bool is_same = ndimA == ndimB && ndimA > 1; - for (int i = 0; i < tensors[indexB]->ndim && is_same; ++i) { - if (tensors[indexB]->shape[i] != tensors[indexA]->shape[i]) { - is_same = false; - } - } - for (int i = 0; i < tensors[indexB]->ndim && ndimB > 1 && is_same == false; ++i) { - if (tensors[indexB]->shape[i] == 1) { - ndimB--; - } - } - - if (ndimA == ndimB && ndimA >= 1) { - std::string fid = "Eltwise_" + mode + "_ndimA1_ndimB1" + "_" + opAttr.dtype; - auto fun = GetKernel(fid); - if (fun == nullptr) { - MS_LOGE("GetKernel return nullptr"); - return nullptr; - } - auto runner = [fun, isSwap](const std::vector &tensors) -> int { - std::vector tensorsCopy(tensors); - if (isSwap) { - iter_swap(tensorsCopy.begin(), tensorsCopy.begin() + 1); - } - int ndim[TENSOR_NUM_MAX]; - int64_t *shapes[TENSOR_NUM_MAX]; - int64_t *strides[TENSOR_NUM_MAX]; - int64_t shape = 1; - int64_t stride = 1; - - store_shape(tensorsCopy, ndim, shapes, strides, STORE_MODE); - update_shape_flatten(tensorsCopy, &shape, &stride); - fun(tensorsCopy); - store_shape(tensorsCopy, ndim, shapes, strides, RESUME_MODE); - return 0; - }; - return runner; - } else { - std::string fid = - "Eltwise_" + mode + "_ndimA" + std::to_string(ndimA) + "_ndimB" + std::to_string(ndimB) + "_" + opAttr.dtype; - auto fun = GetKernel(fid); - if (fun == nullptr) { - MS_LOGE("GetKernel return nullptr"); - return nullptr; - } - auto runner = [fun, isSwap](const std::vector &tensors) -> int { - std::vector tensorsCopy(tensors); - if (isSwap) { - iter_swap(tensorsCopy.begin(), tensorsCopy.begin() + 1); - } - - fun(tensorsCopy); - return 0; - }; - return runner; - } -} - -static runnerType GetKernel_Resize(const mindspore::predict::OpDef &opdef, const std::vector &tensors, - const KernelOption &option) { - if (tensors.size() != 2) { - MS_LOGE("Input tensors num should be 2 !"); - return nullptr; - } - const OpCommonAttr opAttr(opdef, tensors); - auto op = opdef.attr_as_Resize(); - if (op == nullptr) { - MS_LOGE("opdef.attr_as_Resize() is nullptr"); - return nullptr; - } - std::string fid = "Resize_ndim" + std::to_string(opAttr.ndim) + "_" + opAttr.dtype; - if (op->method() == mindspore::predict::ResizeMethod::ResizeMethod_NEAREST_NEIGHBOR) { - fid += "_nearest_neighbor"; - } else if (op->method() == mindspore::predict::ResizeMethod::ResizeMethod_BILINEAR) { - fid += "_bilinear"; - } - fid += (op->alignCorners()) ? "_Align" : "_NotAlign"; - std::vector HeightWidth = { - static_cast(op->newHeight()), - static_cast(op->newWidth()), - }; - return GetKernel_Insert_vector_int32(fid, HeightWidth); -} - -static runnerType GetKernel_DataCarry(const mindspore::predict::OpDef &opdef, const std::vector &tensors, - const KernelOption &option) { - auto runner = [](const std::vector &tensors) -> int { - auto input = tensors.front(); - auto output = tensors.back(); - if (input == nullptr) { - MS_LOGE("input tensors.front() is nullptr"); - return 1; - } - if (output == nullptr) { - MS_LOGE("input tensors.back() is nullptr"); - return 1; - } - uint64_t input_num = 1; - for (int i = 0; i < input->ndim; ++i) { - input_num *= input->shape[i]; - } - uint64_t input_byte_num = input_num * input->dtype.lanes * input->dtype.bits / 8; - - uint64_t output_num = 1; - for (int i = 0; i < output->ndim; ++i) { - output_num *= output->shape[i]; - } - uint64_t output_byte_num = output_num * output->dtype.lanes * output->dtype.bits / 8; - - errno_t ret = memcpy_s(output->data, output_byte_num, input->data, input_byte_num); - if (ret != 0) { - MS_LOGE("memset_s failed."); - return ret; - } - return 0; - }; - return runner; -} - -static runnerType GetKernel_Shape(const mindspore::predict::OpDef &opdef, const std::vector &tensors, - const KernelOption &option) { - auto runner = [](const std::vector &tensors) -> int { - auto input = tensors.front(); - auto output = tensors.back(); - if (input == nullptr) { - MS_LOGE("input tensors.front() is nullptr"); - return 1; - } - if (output == nullptr) { - MS_LOGE("input tensors.back() is nullptr"); - return 1; - } - for (int i = 0; i < input->ndim; ++i) { - reinterpret_cast(output->data)[i] = static_cast(input->shape[i]); - } - return 0; - }; - return runner; -} - -void StridedSliceArgs(const std::vector &input_shape, std::vector *begin, std::vector *end, - std::vector *stride, uint32_t begin_mask, uint32_t end_mask, uint32_t ellipsis_mask, - uint32_t new_axis_mask, uint32_t shrink_axis_mask) { - MS_ASSERT(begin != nullptr); - MS_ASSERT(end != nullptr); - MS_ASSERT(stride != nullptr); - constexpr int support_dims = 8; - std::bitset begin_list(begin_mask); - std::bitset end_list(end_mask); - std::bitset ellipsis_list(ellipsis_mask); - std::bitset new_axis_list(new_axis_mask); - std::bitset shrink_list(shrink_axis_mask); - - std::string begin_list_s = begin_list.to_string().substr(support_dims - begin->size()); - reverse(begin_list_s.begin(), begin_list_s.end()); - - std::string end_list_s = end_list.to_string().substr(support_dims - end->size()); - reverse(end_list_s.begin(), end_list_s.end()); - - std::string ellipsis_list_s = ellipsis_list.to_string().substr(support_dims - end->size()); - reverse(ellipsis_list_s.begin(), ellipsis_list_s.end()); - - std::string new_axis_list_s = new_axis_list.to_string().substr(support_dims - end->size()); - reverse(new_axis_list_s.begin(), new_axis_list_s.end()); - - std::string shrink_list_s = shrink_list.to_string().substr(support_dims - end->size()); - reverse(shrink_list_s.begin(), shrink_list_s.end()); - - int new_axis_count = new_axis_list.count(); - if (ellipsis_list.any()) { - auto idx = 0; // ellipsis_list._Find_first(); - // the 1 is ellipsis - int ellipsis_length = input_shape.size() - (begin->size() - 1 - new_axis_count); - begin->erase(begin->begin() + idx); - end->erase(end->begin() + idx); - stride->erase(stride->begin() + idx); - - begin_list_s.erase(idx, 1); - end_list_s.erase(idx, 1); - ellipsis_list_s.erase(idx, 1); - new_axis_list_s.erase(idx, 1); - shrink_list_s.erase(idx, 1); - - if (ellipsis_length > 0) { - begin->insert(begin->begin() + idx, ellipsis_length, 0); - end->insert(end->begin() + idx, ellipsis_length, 0); - stride->insert(stride->begin() + idx, ellipsis_length, 1); - begin_list_s.insert(idx, ellipsis_length, '1'); - end_list_s.insert(idx, ellipsis_length, '1'); - ellipsis_list_s.insert(idx, ellipsis_length, '0'); - new_axis_list_s.insert(idx, ellipsis_length, '0'); - shrink_list_s.insert(idx, ellipsis_length, '0'); - } - } - - if (new_axis_count) { - for (int i = static_cast(new_axis_list_s.size()) - 1; i >= 0; i--) { - if (new_axis_list_s[i] == '1') { - begin->erase(begin->begin() + i); - end->erase(end->begin() + i); - stride->erase(stride->begin() + i); - begin_list_s.erase(i, 1); - end_list_s.erase(i, 1); - shrink_list_s.erase(i, 1); - } - } - } - - unsigned int size = begin->size(); - for (unsigned int i = 0; i < size; i++) { - if (shrink_list_s[i] == '1') { - auto beginItr = (begin->begin() + i); - auto endItr = (end->begin() + i); - auto strideItr = (stride->begin() + i); - *endItr = *beginItr + 1; - *strideItr = 1; - continue; - } - if (begin_list_s[i] == '1') { - auto beginItr = (begin->begin() + i); - *beginItr = 0; - } - if (end_list_s[i] == '1') { - auto endItr = (end->begin() + i); - *endItr = input_shape[i]; - } - } -} - -#define MAXDIMS 10 -template -int StridedSlice(const std::vector &input_shape, T *input, T *output, int *start, int *end, int *stride, - const int &output_size) { - MS_ASSERT(input != nullptr); - MS_ASSERT(output != nullptr); - MS_ASSERT(start != nullptr); - MS_ASSERT(end != nullptr); - MS_ASSERT(stride != nullptr); - int dimension = input_shape.size(); - if (dimension == 1) { - if (*stride == 1) { - int sizes = (*end - *start) * sizeof(T); - errno_t ret = memcpy_s(output, output_size * sizeof(T), input + *start, sizes); - if (ret != 0) { - MS_LOGE("memset_s failed: %d", ret); - return ret; - } - return 0; - } - for (int j = *start, i = 0; j < *end; j += (*stride), i++) { - output[i] = input[j]; - } - return 0; - } - - // adapt higher dimension - int dimensionArray[MAXDIMS]; - int factorArray[MAXDIMS]; - int totalElement = 0; - - for (int i = 0; i < dimension; i++) { - dimensionArray[i] = input_shape[i]; - factorArray[i] = i ? factorArray[i - 1] * dimensionArray[i] : dimensionArray[i]; - totalElement = i ? totalElement * dimensionArray[i] : dimensionArray[i]; - } - - int j = 0; - for (int k = 0; k < totalElement; k++) { - bool isValid = true; - for (int i = 0; i < dimension; i++) { - int tmp = (k / (totalElement / factorArray[i])) % dimensionArray[i]; - if (tmp < start[i] || tmp >= end[i]) { - isValid = false; - break; - } - isValid = isValid && ((tmp - start[i]) % stride[i] == 0); - } - if (isValid) { - output[j++] = input[k]; - } - } - - return 0; -} - -static runnerType GetKernel_StridedSlice(const mindspore::predict::OpDef &opdef, const std::vector &tensors, - const KernelOption &option) { - const OpCommonAttr opAttr(opdef, tensors); - auto ndim = opAttr.ndim; - - auto op = opdef.attr_as_StridedSlice(); - if (op == nullptr) { - MS_LOGE("op is nullptr"); - return nullptr; - } - uint32_t begin_mask = op->beginMask(); - uint32_t end_mask = op->endMask(); - uint32_t ellipsis_mask = op->ellipsisMask(); - uint32_t new_axis_mask = op->newAxisMask(); - uint32_t shrink_axis_mask = op->shrinkAxisMask(); - std::vector begin; - std::vector end; - std::vector stride; - for (uint32_t i = 0; i < ndim; ++i) { - begin.push_back(op->begin()->Get(i)); - end.push_back(op->end()->Get(i)); - stride.push_back(op->stride()->Get(i)); - } - - auto runner = [begin_mask, end_mask, ellipsis_mask, new_axis_mask, shrink_axis_mask, begin, end, - stride](const std::vector &tensors) mutable -> int { - auto input = tensors.front(); - auto output = tensors.back(); - std::vector input_shape; - for (int i = 0; i < input->ndim; ++i) { - input_shape.push_back(input->shape[i]); - } - - int output_size = 1; - for (int i = 0; i < output->ndim; ++i) { - output_size *= output->shape[i]; - } - - StridedSliceArgs(input_shape, &begin, &end, &stride, begin_mask, end_mask, ellipsis_mask, new_axis_mask, - shrink_axis_mask); - - if (input->dtype.lanes != 1) { - MS_LOGE("StridedSlice input.dtype.lanes=%d invalid, only support 1", input->dtype.lanes); - return 1; - } - - if (input->dtype.bits == 16) { - StridedSlice(input_shape, reinterpret_cast(input->data), reinterpret_cast(output->data), - begin.data(), end.data(), stride.data(), output_size); - } else if (input->dtype.bits == 32) { - StridedSlice(input_shape, reinterpret_cast(input->data), reinterpret_cast(output->data), - begin.data(), end.data(), stride.data(), output_size); - } else { - MS_LOGE("StridedSlice input.dtype.bits=%d invalid, only support (16, 32)", input->dtype.bits); - return 1; - } - return 0; - }; - return runner; -} - -template -static void Permute4d(DLTensor *src, DLTensor *dst, const std::vector &shape, - const std::vector &strides) { - MS_ASSERT(src != nullptr); - MS_ASSERT(dst != nullptr); - int64_t N = shape[NCHW_N]; - int64_t C = shape[NCHW_C]; - int64_t H = shape[NCHW_H]; - int64_t W = shape[NCHW_W]; - auto src_data = reinterpret_cast(src->data); - auto dst_data = reinterpret_cast(dst->data); - int k = 0; - for (int n = 0; n < N; n++) - for (int c = 0; c < C; c++) - for (int h = 0; h < H; h++) - for (int w = 0; w < W; w++) { - dst_data[k++] = src_data[n * strides[0] + c * strides[1] + h * strides[2] + w * strides[3]]; - } -} - -static runnerType GetKernel_CaffeCrop(const mindspore::predict::OpDef &opdef, const std::vector &tensors, - const KernelOption &option) { - const OpCommonAttr opAttr(opdef, tensors); - auto op = opdef.attr_as_CaffeCrop(); - if (op == nullptr) { - MS_LOGE("opdef.attr_as_CaffeCrop() is nullptr"); - return nullptr; - } - std::string fid = - "CaffeCrop_ndim" + std::to_string(opAttr.ndim) + "_" + opAttr.dtype + "_axis" + std::to_string(op->axis()); - - std::vector offsets(op->offsets()->size()); - for (size_t i = 0; i < offsets.size(); ++i) { - offsets[i] = op->offsets()->Get(i); - } - return GetKernel_Insert_vector_int32(fid, offsets); -} - -static runnerType GetKernel_CaffePReLU(const mindspore::predict::OpDef &opdef, const std::vector &tensors, - const KernelOption &option) { - const OpCommonAttr opAttr(opdef, tensors); - auto op = opdef.attr_as_CaffePReLU(); - if (op == nullptr) { - MS_LOGE("opdef.attr_as_CaffePReLU() is nullptr"); - return nullptr; - } - std::string fid = "CaffePReLU_ndim4_" + opAttr.dtype; - fid += (op->channelShared()) ? "_channelShared" : "_channelNotShared"; - auto fun = GetKernel(fid); - if (fun == nullptr) { - return nullptr; - } - bool reahpe_output = true; - return Pack_reahpeCHW(fun, tensors, ExpandCHW, reahpe_output); -} - -static runnerType GetKernel_FullConnection(const mindspore::predict::OpDef &opdef, - const std::vector &tensors, const KernelOption &option) { - const OpCommonAttr opAttr(opdef, tensors); - auto op = opdef.attr_as_FullConnection(); - if (op == nullptr) { - MS_LOGE("opdef.attr_as_FullConnection() is nullptr"); - return nullptr; - } - std::string fid = "FullConnection_ndimA2_" + opAttr.dtype; - fid += (op->hasBias()) ? "_hasBias" : "_notHasBias"; - auto fun = GetKernel(fid); - if (fun == nullptr) { - return nullptr; - } - bool reahpe_output = false; - return Pack_reahpeCHW(fun, tensors, FusedCHW, reahpe_output); -} - -static runnerType GetKernel_Power(const mindspore::predict::OpDef &opdef, const std::vector &tensors, - const KernelOption &option) { - const OpCommonAttr opAttr(opdef, tensors); - auto op = opdef.attr_as_Power(); - if (op == nullptr) { - MS_LOGE("opdef.attr_as_Power() is nullptr"); - return nullptr; - } - std::string fid = "Power_ndim" + std::to_string(opAttr.ndim) + "_" + opAttr.dtype; - std::vector pss; - pss.push_back(op->power()); - pss.push_back(op->scale()); - pss.push_back(op->shift()); - return GetKernel_Insert_vector_float(fid, pss); -} - -static runnerType GetKernel_ArgMax(const mindspore::predict::OpDef &opdef, const std::vector &tensors, - const KernelOption &option) { - const OpCommonAttr opAttr(opdef, tensors); - auto op = opdef.attr_as_ArgMax(); - if (op == nullptr) { - MS_LOGE("opdef.attr_as_ArgMax() is nullptr"); - return nullptr; - } - std::string fid = - "ArgMax_ndim" + std::to_string(opAttr.ndim) + "_" + opAttr.dtype + "_axis" + std::to_string(op->axis()); - fid += (op->keepDims()) ? "_keepDims" : "_notKeepDims"; - fid += "_top1"; - if (tensors.back() == nullptr) { - MS_LOGE("tensors.back() is nullptr"); - return nullptr; - } - fid += "_" + get_dtype(*tensors.back()); - return GetKernel(fid); -} - -void update_shape_Concat(const std::vector &tensors, int32_t axis, int64_t (&shape)[TENSOR_NUM_MAX][3], - int64_t (&strides)[TENSOR_NUM_MAX][3]) { - int64_t shape_low_dim = 1; - int64_t shape_high_dim = 1; - auto output = tensors.back(); - if (output == nullptr) { - MS_LOGW("tensors.back() is nullptr"); - return; - } - for (int32_t i = 0; i < axis; ++i) { - shape_high_dim *= output->shape[i]; - } - for (int32_t i = axis + 1; i < output->ndim; ++i) { - shape_low_dim *= output->shape[i]; - } - for (size_t i = 0; i < tensors.size(); ++i) { - shape[i][0] = shape_high_dim; - shape[i][1] = tensors[i]->shape[axis]; - shape[i][2] = shape_low_dim; - - strides[i][2] = 1; - strides[i][1] = shape[i][2]; - strides[i][0] = shape[i][2] * shape[i][1]; - - tensors[i]->ndim = 3; - tensors[i]->shape = shape[i]; - tensors[i]->strides = strides[i]; - } -} - -static runnerType GetKernel_Concat(const mindspore::predict::OpDef &opdef, const std::vector &tensors, - const KernelOption &option) { - const OpCommonAttr opAttr(opdef, tensors); - if (tensors.size() < 2) { - MS_LOGE("Concat should have at least two tensors"); - return nullptr; - } - if (tensors.at(0) == nullptr) { - MS_LOGE("0th tensors of Concat is nullptr"); - return nullptr; - } - auto ndim = tensors.at(0)->ndim; - if (opdef.attr_as_Concat() == nullptr) { - MS_LOGE("opdef.attr_as_Concat() is nullptr"); - return nullptr; - } - auto axis = opdef.attr_as_Concat()->axis(); - if (axis < 0) { - axis += ndim; - } - std::string fid = - "Concat_ndim3_" + opAttr.dtype + "_input_num" + std::to_string(static_cast(tensors.size()) - 1) + "_axis1"; - auto fun = GetKernel(fid); - if (fun == nullptr) { - MS_LOGE("GetKernel return nullptr"); - return nullptr; - } - auto runner = [fun, axis](const std::vector &tensors) -> int { - int ndim[TENSOR_NUM_MAX]; - int64_t *shape[TENSOR_NUM_MAX]; - int64_t *strides[TENSOR_NUM_MAX]; - int64_t shape_C[TENSOR_NUM_MAX][3]; - int64_t strides_C[TENSOR_NUM_MAX][3]; - store_shape(tensors, ndim, shape, strides, STORE_MODE); - update_shape_Concat(tensors, axis, shape_C, strides_C); - fun(tensors); - store_shape(tensors, ndim, shape, strides, RESUME_MODE); - return 0; - }; - return runner; -} - -template -void Stack_ScaleNumber(const std::vector &tensors) { - if (tensors.empty()) { - MS_LOGW("input tensors is nullptr"); - return; - } - auto output = tensors.back(); - if (output != nullptr) { - MS_LOGW("tensors.back() is nullptr"); - return; - } - for (int i = 0; i < static_cast(tensors.size()) - 1; i++) { - reinterpret_cast(output->data)[i] = reinterpret_cast(tensors.at(i)->data)[0]; - } -} - -static runnerType GetKernel_Stack(const mindspore::predict::OpDef &opdef, const std::vector &tensors, - const KernelOption &option) { - const OpCommonAttr opAttr(opdef, tensors); - auto op = opdef.attr_as_Stack(); - if (op == nullptr) { - MS_LOGE("opdef.attr_as_Stack() is nullptr"); - return nullptr; - } - if (op->isScale()->Get(0)) { - auto runner = [](const std::vector &tensors) -> int { - auto input = tensors.front(); - if (input->dtype.bits == 8) { - Stack_ScaleNumber(tensors); - } else if (input->dtype.bits == 16) { - Stack_ScaleNumber(tensors); - } else if (input->dtype.bits == 32) { - Stack_ScaleNumber(tensors); - } else if (input->dtype.bits == 64) { - Stack_ScaleNumber(tensors); - } else { - MS_LOGE("StridedSlice input.dtype.bits=%d invalid, only support (8, 16, 32, 64)", input->dtype.bits); - return 1; - } - return 0; - }; - return runner; - } - std::string fid = "Stack_ndim" + std::to_string(opAttr.ndim) + "_" + opAttr.dtype + "_input_num" + - std::to_string(static_cast(tensors.size()) - 1) + "_axis" + std::to_string(op->axis()); - return GetKernel(fid); -} - -static runnerType GetKernel_Pad(const mindspore::predict::OpDef &opdef, const std::vector &tensors, - const KernelOption &option) { - const OpCommonAttr opAttr(opdef, tensors); - auto op = opdef.attr_as_Pad(); - if (op == nullptr) { - MS_LOGE("opdef.attr_as_Pad() is nullptr"); - return nullptr; - } - std::string fid = "Pad_ndim" + std::to_string(opAttr.ndim) + "_" + opAttr.dtype + "_" + - mindspore::predict::EnumNamePaddingMode(op->paddingmode()); - std::vector paddings(op->paddings()->size()); - for (size_t i = 0; i < paddings.size(); ++i) { - paddings[i] = op->paddings()->Get(i); - } - return GetKernel_Insert_vector_int32(fid, paddings); -} - -static runnerType GetKernel_Pooling(const mindspore::predict::OpDef &opdef, const std::vector &tensors, - const KernelOption &option) { - const OpCommonAttr opAttr(opdef, tensors); - auto op = opdef.attr_as_Pooling(); - if (op == nullptr) { - MS_LOGE("opdef.attr_as_Pooling() is nullptr"); - return nullptr; - } - auto H = tensors.front()->shape[NCHW_H]; - auto W = tensors.front()->shape[NCHW_W]; - auto padUp = op->padUp(); - auto padDown = op->padDown(); - auto padLeft = op->padLeft(); - auto padRight = op->padRight(); - bool useGlobal = false; - if (H == op->windowH() && W == op->windowW()) { - useGlobal = true; - } - if (op->padMode() != mindspore::predict::PadMode_VALID) { - int64_t outputHeight = tensors.back()->shape[NCHW_H]; - int64_t outputWidth = tensors.back()->shape[NCHW_W]; - - int64_t dHeight = (outputHeight - 1) * op->strideH() + op->windowH() - H; - int64_t dWidth = (outputWidth - 1) * op->strideW() + op->windowW() - W; - padUp = dHeight / 2; - padDown = dHeight - dHeight / 2; - padLeft = dWidth / 2; - padRight = dWidth - dWidth / 2; - if (padDown < 0) { - padDown = 0; - } - if (padRight < 0) { - padRight = 0; - } - } - std::string poolingMode = mindspore::predict::EnumNamesPoolMode()[op->poolingMode()]; - if (poolingMode != "MAX_POOLING" && poolingMode != "MEAN_POOLING") { - MS_LOGE("Pooling op not support poolingMode=%s", poolingMode.c_str()); - return nullptr; - } - - std::string fid; - fid += useGlobal ? "GlobalPooling" : "Pooling"; - fid += "_ndim" + std::to_string(opAttr.ndim) + "_" + opAttr.dtype; - fid += (poolingMode == "MAX_POOLING") ? "_max" : "_avg"; - if (!useGlobal) { - fid += "_kernel" + std::to_string(op->windowH()) + std::to_string(op->windowW()); - fid += "_stride" + std::to_string(op->strideH()) + std::to_string(op->strideW()); - fid += - "_pad" + std::to_string(padUp) + std::to_string(padDown) + std::to_string(padLeft) + std::to_string(padRight); - if (op->caffeMode() && (padUp || padDown || padLeft || padRight)) fid += "_caffe"; - } - auto fun = GetKernel(fid); - if (fun == nullptr) { - MS_LOGE("GetKernel return nullptr"); - return nullptr; - } - return (opdef.isLastConv()) ? Pack_NCHW2NHWC(fun) : fun; -} - -static runnerType GetKernel_Mean(const mindspore::predict::OpDef &opdef, const std::vector &tensors, - const KernelOption &option) { - const OpCommonAttr opAttr(opdef, tensors); - auto op = opdef.attr_as_Mean(); - if (op == nullptr) { - MS_LOGE("opdef.attr_as_Mean() is nullptr"); - return nullptr; - } - std::string axis_str = ""; - for (uint32_t i = 0; i < op->axis()->size(); ++i) { - axis_str += std::to_string(op->axis()->Get(i)); - } - std::string fid = "Mean_ndim" + std::to_string(opAttr.ndim) + "_" + opAttr.dtype + "_axis" + axis_str; - fid += (op->keepDims()) ? "_keepDims" : "_notkeepDims"; - return GetKernel(fid); -} - -static runnerType GetKernel_MatMul(const mindspore::predict::OpDef &opdef, const std::vector &tensors, - const KernelOption &option) { - const OpCommonAttr opAttr(opdef, tensors); - auto op = opdef.attr_as_MatMul(); - if (op == nullptr) { - MS_LOGE("opdef.attr_as_MatMul() is nullptr"); - return nullptr; - } - std::string fid = "MatMul_ndimA2_ndimB2_" + opAttr.dtype; - fid += (op->transposeA()) ? "_1" : "_0"; - fid += (op->transposeB()) ? "_1" : "_0"; - return GetKernel(fid); -} - -static runnerType GetKernel_Softmax(const mindspore::predict::OpDef &opdef, const std::vector &tensors, - const KernelOption &option) { - const OpCommonAttr opAttr(opdef, tensors); - auto op = opdef.attr_as_SoftMax(); - if (op == nullptr) { - MS_LOGE("opdef.attr_as_SoftMax() is nullptr"); - return nullptr; - } - std::string fid = - "Softmax_ndim" + std::to_string(opAttr.ndim) + "_" + opAttr.dtype + "_axis" + std::to_string(op->axis()->Get(0)); - return GetKernel(fid); -} - -void update_shape_Activation(const std::vector &tensors, int64_t *shape, int64_t *strides) { - auto input = tensors.front(); - MS_ASSERT(input != nullptr); - for (int32_t i = 0; i < input->ndim; ++i) { - *shape *= input->shape[i]; - } - for (size_t i = 0; i < tensors.size(); ++i) { - MS_ASSERT(tensors[i] != nullptr); - tensors[i]->ndim = 1; - tensors[i]->shape = shape; - tensors[i]->strides = strides; - } -} - -static runnerType GetKernel_Activation(const mindspore::predict::OpDef &opdef, const std::vector &tensors, - const KernelOption &option) { - const OpCommonAttr opAttr(opdef, tensors); - auto op = opdef.attr_as_Activation(); - if (op == nullptr) { - MS_LOGE("opdef.attr_as_Activation() is nullptr"); - return nullptr; - } - std::string fid = - "Activation_ndim1_" + opAttr.dtype + "_" + std::string(mindspore::predict::EnumNameActivationType(op->type())); - - auto fun = GetKernel(fid); - if (fun == nullptr) { - MS_LOGE("GetKernel return nullptr"); - return nullptr; - } - auto runner = [fun](const std::vector &tensors) -> int { - int ndim[TENSOR_NUM_MAX]; - int64_t *shapes[TENSOR_NUM_MAX]; - int64_t *strides[TENSOR_NUM_MAX]; - int64_t shape = 1; - int64_t stride = 1; - - store_shape(tensors, ndim, shapes, strides, STORE_MODE); - update_shape_Activation(tensors, &shape, &stride); - fun(tensors); - store_shape(tensors, ndim, shapes, strides, RESUME_MODE); - return 0; - }; - return runner; -} - -static runnerType GetKernel_Exp(const mindspore::predict::OpDef &opdef, const std::vector &tensors, - const KernelOption &option) { - const OpCommonAttr opAttr(opdef, tensors); - std::string fid = "Exp_ndim" + std::to_string(opAttr.ndim) + "_" + opAttr.dtype; - return GetKernel(fid); -} - -static runnerType GetKernel_Cast(const mindspore::predict::OpDef &opdef, const std::vector &tensors, - const KernelOption &option) { - MS_ASSERT(tensors.front() != nullptr); - MS_ASSERT(tensors.back() != nullptr); - auto src_dtype = get_dtype(*tensors.front()); - auto dst_dtype = get_dtype(*tensors.back()); - std::string fid = "Cast_ndim" + std::to_string(tensors.front()->ndim) + "_" + src_dtype + "_" + dst_dtype; - return GetKernel(fid); -} - -static runnerType GetKernel_ExpandDims(const mindspore::predict::OpDef &opdef, const std::vector &tensors, - const KernelOption &option) { - const OpCommonAttr opAttr(opdef, tensors); - auto op = opdef.attr_as_ExpandDims(); - if (op == nullptr) { - MS_LOGE("opdef.attr_as_ExpandDims() is nullptr"); - return nullptr; - } - std::string fid = - "ExpandDims_ndim" + std::to_string(opAttr.ndim) + "_" + opAttr.dtype + "_axis" + std::to_string(op->dim()); - return GetKernel(fid); -} - -static runnerType GetKernel_Tile(const mindspore::predict::OpDef &opdef, const std::vector &tensors, - const KernelOption &option) { - const OpCommonAttr opAttr(opdef, tensors); - auto op = opdef.attr_as_Tile(); - if (op == nullptr) { - MS_LOGE("opdef.attr_as_Tile() is nullptr"); - return nullptr; - } - std::string fid = "Tile_ndim" + std::to_string(opAttr.ndim) + "_" + opAttr.dtype; - std::vector multiples; - for (size_t i = 0; i < op->multiples()->size(); ++i) { - multiples.push_back(op->multiples()->Get(i)); - } - return GetKernel_Insert_vector_int32(fid, multiples); -} - -static runnerType GetKernel_Range(const mindspore::predict::OpDef &opdef, const std::vector &tensors, - const KernelOption &option) { - const OpCommonAttr opAttr(opdef, tensors); - auto op = opdef.attr_as_Range(); - if (op == nullptr) { - MS_LOGE("opdef.attr_as_Range() is nullptr"); - return nullptr; - } - std::string fid = "Range_ndim_" + opAttr.dtype; - std::vector vec = {static_cast(op->start()), static_cast(op->delta())}; - - auto f = GetFunction(fid); - if (f == nullptr) { - MS_LOGE("GetFunction returu nullptr"); - return nullptr; - } - auto runner = [f, vec](const std::vector &tensors_origin) -> int { - // remove 3 input, only remain output - const std::vector tensors = {tensors_origin.back()}; - std::vector values(vec.size() + tensors.size()); - std::vector codes(values.size()); - tvm::runtime::TVMArgsSetter setter(values.data(), codes.data()); - for (size_t i = 0; i < vec.size(); ++i) { - setter(i, vec.at(i)); - } - for (size_t i = 0; i < tensors.size(); ++i) { - setter(i + vec.size(), tensors.at(i)); - } - auto passfunc = reinterpret_cast(f); - return passfunc(values.data(), codes.data(), values.size()); - }; - return runner; -} - -using GetKernelFunType = std::function &tensors, const KernelOption &option)>; - -static const std::unordered_map g_kernel_op_list = { - {"Conv2D", GetKernel_Conv}, - {"DepthwiseConv2D", GetKernel_Conv}, - {"DeDepthwiseConv2D", GetKernel_Conv}, - {"DeConv2D", GetKernel_Conv_var}, - {"BatchNorm", GetKernel_BatchNorm}, - {"CaffeBatchNorm", GetKernel_BatchNorm}, - {"BiasAdd", GetKernel_BatchNorm}, - {"Scale", GetKernel_BatchNorm}, - {"Eltwise", GetKernel_Eltwise}, - {"Add", GetKernel_Eltwise}, - {"Sub", GetKernel_Eltwise}, - {"Mul", GetKernel_Eltwise}, - {"RealDiv", GetKernel_Eltwise}, - {"Maximum", GetKernel_Eltwise}, - {"ResizeBilinear", GetKernel_Resize}, - {"ResizeNearestNeighbor", GetKernel_Resize}, - {"Squeeze", GetKernel_DataCarry}, - {"Reshape", GetKernel_DataCarry}, - {"Shape", GetKernel_Shape}, - {"StridedSlice", GetKernel_StridedSlice}, - {"CaffeCrop", GetKernel_CaffeCrop}, - {"CaffePReLU", GetKernel_CaffePReLU}, - {"FullConnection", GetKernel_FullConnection}, - {"Power", GetKernel_Power}, - {"ArgMax", GetKernel_ArgMax}, - {"Concat", GetKernel_Concat}, - {"Stack", GetKernel_Stack}, - {"Pad", GetKernel_Pad}, - {"Pooling", GetKernel_Pooling}, - {"Mean", GetKernel_Mean}, - {"MatMul", GetKernel_MatMul}, - {"SoftMax", GetKernel_Softmax}, - {"Activation", GetKernel_Activation}, - {"Exp", GetKernel_Exp}, - {"Cast", GetKernel_Cast}, - {"ExpandDims", GetKernel_ExpandDims}, - {"Tile", GetKernel_Tile}, - {"Range", GetKernel_Range}, -}; - -GetKernelFunType Get_GetKernelFun(const std::string &optype) { - auto it = g_kernel_op_list.find(optype); - return (it != g_kernel_op_list.end() ? it->second : nullptr); -} -} // namespace auto_tensor - -runnerType GetKernel(const mindspore::predict::OpDef &opdef, const std::vector &tensors, - const KernelOption &option) { - std::string optype = mindspore::predict::EnumNameOpT(opdef.attr_type()); - auto GetKernelFun = auto_tensor::Get_GetKernelFun(optype); - if (GetKernelFun != nullptr) { - return GetKernelFun(opdef, tensors, option); - } else { - return nullptr; - } -} - -int CallKernel(const std::string &fid, const std::vector &tensors) { - auto runner = GetKernel(fid); - return runner(tensors); -} - -#endif // LITE_RUNTIME_ON diff --git a/predict/module/tvm_kernel/lite/src/api/tvm_op_module.cc b/predict/module/tvm_kernel/lite/src/api/tvm_op_module.cc deleted file mode 100644 index c6a6fc93f5..0000000000 --- a/predict/module/tvm_kernel/lite/src/api/tvm_op_module.cc +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this ${file} except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include -#include "src/api/tvm_op_module.h" -#include "common/op_utils.h" -#include "lite/api/km_api.h" -#include "src/op.h" -namespace mindspore { -namespace predict { -using OpFunc = std::function &)>; - -class TVMOperator : public OpBase { - public: - explicit TVMOperator(OpFunc func) : opfunc(std::move(func)) {} - ~TVMOperator() override = default; - int Init(const std::vector &inputs, const std::vector &outputs) override { return 0; } - int Execute(const std::vector &inputs, const std::vector &outputs) override { - std::vector dlT; - for (auto input : inputs) { - MS_ASSERT(input != nullptr); - dlT.push_back(input->GetDLTensor()); - } - - for (auto output : outputs) { - MS_ASSERT(output != nullptr); - dlT.push_back(output->GetDLTensor()); - } - return opfunc(dlT); - } - - static OpBase *CreateOp(const std::vector &inputs, const std::vector &outputs, const OpDef &opDef, - const Context &ctx, const OpDesc &desc) { - std::vector dlT; - for (auto input : inputs) { - MS_ASSERT(input != nullptr); - dlT.push_back(input->GetDLTensor()); - } - - for (auto output : outputs) { - MS_ASSERT(output != nullptr); - dlT.push_back(output->GetDLTensor()); - } - - KernelOption option; - option.numThreads = ctx.threadNum; - OpFunc opFunc = GetKernel(opDef, dlT, option); - if (opFunc != nullptr) { - auto op = std::unique_ptr(new (std::nothrow) TVMOperator(opFunc)); - if (op == nullptr) { - MS_LOGE("new TVMOperator failed"); - } - return op.release(); - } - return nullptr; - } - - private: - OpFunc opfunc; - std::vector dltensors; -}; - -TVMOpRegistry::TVMOpRegistry() = default; - -mindspore::predict::OpCreator TVMOpRegistry::GetOpCreator(const mindspore::predict::OpDesc &desc) { - return TVMOperator::CreateOp; -} - -OpRegistry *TVMOpModule::GetInstance() { - static TVMOpRegistry tvmOpRegistry; - return &tvmOpRegistry; -} - -static TVMOpModule tvmOpModule; - -static ModuleRegistrar> g_tvmOpReg(MODULE_REG_NAME_OP_REGISTRY, tvmOpModule); -} // namespace predict -} // namespace mindspore diff --git a/predict/module/tvm_kernel/lite/src/api/tvm_op_module.h b/predict/module/tvm_kernel/lite/src/api/tvm_op_module.h deleted file mode 100644 index 3f0f33142d..0000000000 --- a/predict/module/tvm_kernel/lite/src/api/tvm_op_module.h +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this ${file} except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef PREDICT_MODULE_TVM_KERNEL_LITE_SRC_API_TVM_OP_MODULE_H_ -#define PREDICT_MODULE_TVM_KERNEL_LITE_SRC_API_TVM_OP_MODULE_H_ - -#include "src/op_registry.h" -namespace mindspore { -namespace predict { -class TVMOpRegistry : public OpRegistry { - public: - TVMOpRegistry(); - OpCreator GetOpCreator(const OpDesc &desc) override; -}; - -class TVMOpModule : public Module { - public: - OpRegistry *GetInstance() override; -}; -} // namespace predict -} // namespace mindspore -#endif // PREDICT_MODULE_TVM_KERNEL_LITE_SRC_API_TVM_OP_MODULE_H_ diff --git a/predict/module/tvm_kernel/lite/src/codegen/llvm/lite_rtfunc_reset.cc b/predict/module/tvm_kernel/lite/src/codegen/llvm/lite_rtfunc_reset.cc deleted file mode 100644 index df19676b40..0000000000 --- a/predict/module/tvm_kernel/lite/src/codegen/llvm/lite_rtfunc_reset.cc +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this ${file} except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include "tvm/api_registry.h" - -namespace tvm { -namespace codegen { -class LiteRTFuncReseter { - public: - LiteRTFuncReseter() {} - ~LiteRTFuncReseter() {} - int InsertFuncPair(std::string sfunc, std::string dfunc) { - CHECK_NE(sfunc.size(), 0); - CHECK_NE(dfunc.size(), 0); - func_map_[sfunc] = dfunc; - return 0; - } - - /* - * the llvm::Function::Create need a longe life scope const char* as input - * so here not return block life scopte tmp std::string. - */ - const char* GetResetFunc(std::string sfunc) { - CHECK_NE(sfunc.size(), 0); - auto it_dfunc = func_map_.find(sfunc); - if (it_dfunc != func_map_.end()) { - return it_dfunc->second.c_str(); - } else { - func_map_[sfunc] = sfunc; - return func_map_[sfunc].c_str(); - } - } - - /* - * not real delete item paire, just set orig function pair - */ - int DeleteFuncPair(std::string sfunc) { - CHECK_NE(sfunc.size(), 0); - func_map_[sfunc] = sfunc; - return 0; - } - static LiteRTFuncReseter* GetRTFuncReseter() { - static LiteRTFuncReseter inst; - return &inst; - } - - private: - std::map func_map_; -}; - -TVM_REGISTER_API("codegen.SetRTFuncTransPair").set_body([](const TVMArgs& targs, TVMRetValue* rv) { - *rv = LiteRTFuncReseter::GetRTFuncReseter()->InsertFuncPair(targs[0], targs[1]); -}); - -TVM_REGISTER_API("codegen.DelRTFuncTransPair").set_body([](const TVMArgs& targs, TVMRetValue* rv) { - *rv = LiteRTFuncReseter::GetRTFuncReseter()->DeleteFuncPair(targs[0]); -}); - -/* - * now no operator=(const char* ) provide for TVMRetValue - * here using explicit operator call function to make sure not using operator=(int) - */ -TVM_REGISTER_API("codegen.GetTransRTFunc").set_body([](const TVMArgs& targs, TVMRetValue* rv) { - (*rv).operator=( - reinterpret_cast(const_cast(LiteRTFuncReseter::GetRTFuncReseter()->GetResetFunc(targs[0])))); -}); -} // namespace codegen -} // namespace tvm diff --git a/predict/schema/inner/README b/predict/schema/inner/README deleted file mode 100644 index f397efe4c5..0000000000 --- a/predict/schema/inner/README +++ /dev/null @@ -1 +0,0 @@ -for flatbuf auto generated file diff --git a/predict/schema/ms.fbs b/predict/schema/ms.fbs deleted file mode 100644 index f66abf8a86..0000000000 --- a/predict/schema/ms.fbs +++ /dev/null @@ -1,153 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -include "op.fbs"; - -namespace mindspore.predict; - -enum DataType : int { - DT_FLOAT = 0, - DT_FLOAT16 = 1, - DT_INT8 = 2, - DT_INT32 = 3, - DT_UINT8 = 4, - DT_UINT32 = 8, - DT_UNDEFINED = 16 -} - -enum Format : int { - NCHW = 0, - NHWC, - NC4HW4 = 100, - NUM_OF_FORMAT -} - -enum MSConst: int { - WEIGHT_REFCOUNT = 999 -} - -table QuantizationDef { - // Quantized value q, corresponding float value r: - // r = scale * (q - zero_point), where scale = (rmax - rmin) / (qmax - qmin) - min: [float]; - max: [float]; - scale: [float]; - zero_point: [long]; - - // Tensor shape of the specifies dimension. - dimension: int; -} - -table TensorDef { - // data type - dataType: DataType; - // shape - dims: [int]; - format: Format; - refCount: int; - offset: int; - data: [ubyte]; - quantization: QuantizationDef; -} - -union OpT { - Concat, - SoftMax, - Activation, - Conv2D, - FusedBatchNorm, - CaffeBatchNorm, - Squeeze, - BiasAdd, - Pooling, - DepthwiseConv2D, - DeDepthwiseConv2D, - Resize, - DetectionPostProcess, - FullConnection, - Mean, - DeConv2D, - Scale, - Reshape, - Eltwise, - NetOutput, - Add, - MatMul, - StridedSlice, - Power, - Slice, - Stack, - Mul, - Pad, - Maximum, - CaffePReLU, - ArgMax, - Exp, - CaffeCrop, - Range, - ExpandDims, - Tile, - Cast -// Split -} - -enum QuantType: int { - QUANT_NONE, - QUANT_INT8 -} - -table OpDef { - name: string; - attr: OpT; - inputIndex: [uint]; - outputIndex: [uint]; - isLastConv: bool; - quantType: QuantType = QUANT_NONE; -} - - -enum FmkType: int { - TF, - CAFFE -} - -table NodeDef { - fmkType: FmkType; - opDef: OpDef; -} - - -table SubGraphDef { - name: string; - inputIndex: [uint]; - outputIndex: [uint]; - mempoolSize: uint; - nodes: [NodeDef]; - allTensors: [TensorDef]; // weight + input + output -} - -table MempoolCfg { - size: uint; - shiftFactor: uint; -} - -table GraphDef { - name: string; - mempoolCfg: MempoolCfg; - subgraphs: [SubGraphDef]; -} - -root_type GraphDef; diff --git a/predict/schema/op.fbs b/predict/schema/op.fbs deleted file mode 100755 index 4d01bb9c3b..0000000000 --- a/predict/schema/op.fbs +++ /dev/null @@ -1,351 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -namespace mindspore.predict; - -enum ResizeMethod: byte { - UNKNOW = -1, - BILINEAR = 0, - NEAREST_NEIGHBOR = 1 -} - -enum DataFormatType : byte { - UNKNOW = -1, - NCHW = 0, - NHWC = 1, - HWC = 2, // for input image or resize - CHW = 3, // for input image or resize -} - -enum ActivationType : byte { - NO_ACTIVATION = 0, - RELU = 1, - SIGMOID = 2, - RELU6 = 3, - ELU = 4, - LEAKY_RELU = 5, - ABS = 6, - RELU1 = 7, - SOFTSIGN = 8, - SOFTPLUS = 9, - TANH = 10, - UNKNOW = 11 -} - -enum PoolMode : byte { - MAX_POOLING = 0, - MEAN_POOLING = 1, - GLOBAL_POOING = 2 -} - -enum EltwiseMode : byte { - PROD = 0, - SUM = 1, - MAXIMUM = 2 -} - -enum PadMode : byte { - NOTSET=0, - SAME=1, - VALID=2, - CAFFE_CEIL_NEW=4 -} - -enum PaddingMode : byte { - CONSTANT = 0, - REFLECT = 1, - SYMMETRIC = 2, - MODE_RESERVED = 3 -} - -table Pad { - paddingmode: PaddingMode; - paddings: [int]; -} - -table Maximum { - format: DataFormatType = 0; -} - -table Concat { - axis: int; - n: int; -} - -table SoftMax { - axis: [int]; -} - -table Activation { - type: ActivationType = 0; -} - -table Conv2D { - format: DataFormatType = 0; - group: int; - channelIn: int; - channelOut: int; - kernelW: int; - kernelH: int; - strideW: int; - strideH: int; - padMode: PadMode; - padUp: int; - padDown: int; - padLeft: int; - padRight: int; - dilateW: int; - dilateH: int; - hasBias: bool = false; - activationType: ActivationType = 0; -} - -table FusedBatchNorm { - epsilon: float; // eg. epsilon=0.001 -} - -table CaffeBatchNorm { - epsilon: float; // eg. epsilon=0.001 -} - -table Squeeze { - axis: [int]; -} - -table BiasAdd { - axis: [int]; -} - -table Pooling { - format: DataFormatType = 0; - poolingMode: PoolMode; - windowW: int; - windowH: int; - strideW: int; - strideH: int; - padMode: PadMode; - padUp: int; - padDown: int; - padLeft: int; - padRight: int; - caffeMode: bool = false; -} - -table DepthwiseConv2D { - format: DataFormatType = 0; - channelIn: int; - channelMultiplier: int; - kernelW: int; - kernelH: int; - strideW: int; - strideH: int; - padMode: PadMode; - padUp: int; - padDown: int; - padLeft: int; - padRight: int; - dilateW: int; - dilateH: int; - hasBias: bool = false; - activationType: ActivationType = 0; -} - -table DeDepthwiseConv2D { - format: DataFormatType = 0; - channelIn: int; - channelMultiplier: int; - kernelW: int; - kernelH: int; - strideW: int; - strideH: int; - padMode: PadMode; - padUp: int; - padDown: int; - padLeft: int; - padRight: int; - dilateW: int; - dilateH: int; - hasBias: bool = false; - activationType: ActivationType = 0; -} - - -table Resize { - format: DataFormatType = 0; - method: ResizeMethod; - newHeight: long; - newWidth: long; - alignCorners: bool = false; - preserveAspectRatio: bool = false; -} - -table DetectionPostProcess { - format: DataFormatType = 0; - inputSize: int; - hScale: float; - wScale: float; - xScale: float; - yScale: float; - NmsIouThreshold: float; - NmsScoreThreshold: float; - MaxDetections: long; - DetectionsPreClass: long; - MaxClassesPreDetection: long; - NumClasses: long; - UseRegularNms: bool; -} - -table FullConnection { - format: DataFormatType = 0; - hasBias: bool; - axis: int; -} - -// Mean(input_tensor, axis, keep_dims) -table Mean { - axis: [int]; - keepDims: bool = false; -} - -table DeConv2D { - format: DataFormatType = 0; - group: int; - channelIn: int; - channelOut: int; - kernelW: int; - kernelH: int; - strideW: int; - strideH: int; - padMode: PadMode; - padUp: int; - padDown: int; - padLeft: int; - padRight: int; - dilateW: int; - dilateH: int; - hasBias: bool = false; - activationType: ActivationType = 0; -} - -table Scale { - format: DataFormatType = 0; -} - -table Eltwise { - format: DataFormatType = 0; - mode: EltwiseMode; -} - -table Add { - format: DataFormatType = 0; -} - -table Slice { - format: DataFormatType = 0; - begin: [int]; - end: [int]; - stride: [int]; -} - -table Mul { -} - -table Exp { -} - -table Reshape { - format: DataFormatType = 0; - shape: [long]; -} - -table Power { - power: float; - scale: float; - shift: float; -} - -table ArgMax { - axis: int; - outMaxValue: bool; - topK: int; - keepDims: bool; - axisType: int; -} - -table NetOutput { - format: DataFormatType = 0; -} - -table MatMul { - transposeA : bool = false; - transposeB : bool = false; -} - -table CaffePReLU { - channelShared : bool = false; -} - -table StridedSlice { - beginMask: int; - endMask: int; - ellipsisMask: int; - newAxisMask: int; - shrinkAxisMask: int; - begin: [int]; - end: [int]; - stride: [int]; - isScale: [int]; -} - -table Stack { - axis: int; - n: int; - isScale: [int]; -} - -table Range { - start: int; - limit: int; - delta: int; -} - -table ExpandDims { - dim: int; -} - -table Tile { - multiples: [int]; -} - -table Cast { - srcT: int; - dstT: int; -} - -table Split { - numberSplit: int; - sizeSplits: [int]; - splitDim: int; -} - -table CaffeCrop { - axis : long; - offsets : [long]; -} - -table Permute { - order: [long]; -} diff --git a/predict/src/CMakeLists.txt b/predict/src/CMakeLists.txt deleted file mode 100644 index 92c45473d7..0000000000 --- a/predict/src/CMakeLists.txt +++ /dev/null @@ -1,69 +0,0 @@ -cmake_minimum_required(VERSION 3.12) -project(mspredict) - -set(CMAKE_CXX_STANDARD 11) - -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../module/tvm_kernel/incubator-tvm/3rdparty/dlpack/include/) - -link_directories(${CMAKE_CURRENT_SOURCE_DIR}/../output/lib/) - -if (ENABLE_ASAN) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -o0 -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined") -endif() - -set(MSPREDICT_SRC - runtime/allocator.cc - runtime/allocator.h - runtime/thread_pool.cc - runtime/thread_pool.h - runtime/workspace_pool.cc - runtime/workspace_pool.h - runtime/runtime_api.cc - runtime/runtime_api.h - context.cc - graph.cc - graph.h - graph_execution.cc - graph_execution.h - node.cc - node.h - op.cc - op.h - op_factory.cc - op_factory.h - op_registry.cc - op_registry.h - session.cc - tensor.cc - ${CMAKE_CURRENT_SOURCE_DIR}/operator/cpu/common/op_func_comm.cc) - -set(MSPREDICT_SRC ${MSPREDICT_SRC} - ${CMAKE_CURRENT_SOURCE_DIR}/../common/graph_util.cc - ${CMAKE_CURRENT_SOURCE_DIR}/../common/utils.cc - ${CMAKE_CURRENT_SOURCE_DIR}/../common/mslog.cc - ${CMAKE_CURRENT_SOURCE_DIR}/../common/module_registry.cc) - -add_library(mspredict SHARED ${MSPREDICT_SRC}) - -if(ENABLE_PREDICT_ARM64 OR ENABLE_PREDICT_ARM32) - target_link_libraries(mspredict android log tvm_kernel libsecurec.a) -else() - target_link_libraries(mspredict pthread tvm_kernel libsecurec.a) -endif() - -add_dependencies(mspredict tvm_kernel) -add_dependencies(mspredict securec) -add_dependencies(mspredict gtest) - -add_custom_command(TARGET mspredict POST_BUILD - COMMAND mkdir -pv ${PREDICT_DIR}/output/lib - COMMAND cp ${PREDICT_BUILD_DIR}/src/libmspredict.so ${PREDICT_DIR}/output/lib/ - COMMAND cp ${PREDICT_BUILD_DIR}/module/tvm_kernel/lite/libtvm_kernel.so ${PREDICT_DIR}/output/lib/ - COMMAND mkdir -pv ${PREDICT_DIR}/output/include - COMMAND cp -r ${PREDICT_DIR}/include/* ${PREDICT_DIR}/output/include - COMMAND mkdir -pv ${PREDICT_DIR}/output/include/schema/inner - COMMAND cp ${PREDICT_DIR}/schema/ms_generated.h ${PREDICT_DIR}/output/include/schema/inner - COMMAND cp ${PREDICT_DIR}/schema/op_generated.h ${PREDICT_DIR}/output/include/schema/inner - COMMAND mkdir -pv ${PREDICT_DIR}/output/include/dlpack/ - COMMAND cp ${PREDICT_DIR}/module/tvm_kernel/incubator-tvm/3rdparty/dlpack/include/dlpack/dlpack.h ${PREDICT_DIR}/output/include/dlpack/) diff --git a/predict/src/context.cc b/predict/src/context.cc deleted file mode 100644 index 45ec719db6..0000000000 --- a/predict/src/context.cc +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "include/context.h" -#include "include/tensor.h" -#include "src/runtime/allocator.h" - -namespace mindspore { -namespace predict { -Context::Context() { allocator = Allocator::Create(); } - -Context::~Context() {} - -Context::Context(int threadNum, std::shared_ptr allocator, DLContext deviceCtx) { - this->allocator = allocator; - this->threadNum = threadNum; - this->deviceCtx = deviceCtx; -} -} // namespace predict -} // namespace mindspore diff --git a/predict/src/graph.cc b/predict/src/graph.cc deleted file mode 100644 index 5e1aab6a56..0000000000 --- a/predict/src/graph.cc +++ /dev/null @@ -1,378 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "src/graph.h" -#include -#include -#include -#include -#include "schema/ms_generated.h" -#include "common/graph_util.h" -#include "common/mslog.h" -#include "include/errorcode.h" -#include "src/graph_execution.h" - -namespace mindspore { -namespace predict { -static const uint32_t G_MAX_OP_COUNT = 10000; - -Graph *Graph::CreateFromBuf(const char *buf, size_t size, const Context &ctx) { - if (buf == nullptr) { - MS_LOGE("the input buffer is nullptr"); - return nullptr; - } - - flatbuffers::Verifier verify((const uint8_t *)buf, size); - if (!VerifyGraphDefBuffer(verify)) { - MS_LOGE("the buffer is invalid and fail to create graph"); - return nullptr; - } - - auto graphDef = GetGraphDef(buf); - std::unique_ptr graph(new (std::nothrow) Graph()); - if (graph == nullptr) { - MS_LOGE("graph malloc fail"); - return nullptr; - } - auto ret = graph->Build(*graphDef, ctx); - if (ret != RET_OK) { - MS_LOGE("build graph fail"); - return nullptr; - } - return graph.release(); -} - -Graph::Graph() = default; - -Graph::~Graph() { - for (auto &subgraph : subgraphs) { - delete subgraph; - } - subgraphs.clear(); -} - -int Graph::Build(const GraphDef &graphDef, const Context &ctx) { - MS_ASSERT(graphDef.subgraphs() != nullptr); - for (size_t i = 0; i < graphDef.subgraphs()->size(); i++) { - MS_ASSERT(graphDef.subgraphs()->GetAs(i) != nullptr); - SubGraph *subGraph = SubGraph::CreateSubGraph(*(graphDef.subgraphs()->GetAs(i)), ctx); - if (subGraph == nullptr) { - MS_LOGE("converter subgraph failed"); - return RET_ERROR; - } - subgraphs.push_back(subGraph); - auto subDepends = subGraph->GetDepends(); - depends.insert(subDepends.begin(), subDepends.end()); - } - - auto iter = depends.begin(); - while (iter != depends.end()) { - if (iter->second.empty()) { - readyQue.push_back(iter->first); - iter = depends.erase(iter); - } else { - iter++; - } - } - - return RET_OK; -} - -std::vector Graph::GetInputs() { - MS_ASSERT(subgraphs.front() != nullptr); - return subgraphs.front()->GetInputs(); -} - -std::vector Graph::GetOutputs() { - MS_ASSERT(subgraphs.back() != nullptr); - return subgraphs.back()->GetOutputs(); -} - -std::map> &Graph::GetOutputsMap() { - MS_ASSERT(subgraphs.back() != nullptr); - return subgraphs.back()->GetOutputsMap(); -} - -void Graph::FreeAllTensors() { - for (auto iter : subgraphs) { - iter->FreeAllTensors(); - } -} - -std::vector *Graph::Subgraphs() { return &subgraphs; } - -SubGraph::SubGraph() = default; - -SubGraph::~SubGraph() { - for (auto iter = nodes.begin(); iter != nodes.end();) { - if (iter->second != nullptr) { - delete iter->second; - } - iter = nodes.erase(iter); - } - nodes.clear(); - - for (auto &allTensor : allTensors) { - if (allTensor != nullptr) { - delete allTensor; - } - } - allTensors.clear(); -} - -SubGraph *SubGraph::CreateSubGraph(const SubGraphDef &subGraphDef, const Context &ctx) { - std::unique_ptr subGraph(new (std::nothrow) SubGraph()); - if (subGraph == nullptr) { - MS_LOGE("subGraph malloc fail"); - return nullptr; - } - - auto ret = subGraph->Build(subGraphDef, ctx); - if (ret != RET_OK) { - MS_LOGE("subGraph Build fail"); - return nullptr; - } - - return subGraph.release(); -} - -int SubGraph::Build(const SubGraphDef &subGraphDef, const Context &ctx) { - int ret; - MS_ASSERT(subGraphDef.inputIndex() != nullptr); - ret = ConverterIndex(*(subGraphDef.inputIndex()), &inputIndices); - if (ret != RET_OK) { - MS_LOGE("ConverterIndex failed: %d", ret); - return ret; - } - MS_LOGD("converter inputIndex succ"); - - MS_ASSERT(subGraphDef.outputIndex() != nullptr); - ret = ConverterIndex(*(subGraphDef.outputIndex()), &outputIndices); - if (ret != RET_OK) { - MS_LOGE("ConverterIndex failed: %d", ret); - return ret; - } - MS_LOGD("converter outputIndex succ"); - MS_ASSERT(subGraphDef.allTensors() != nullptr); - ret = ConverterAllTensor(*(subGraphDef.allTensors())); - if (ret != RET_OK) { - MS_LOGE("ConverterAllTensor failed: %d", ret); - return ret; - } - MS_LOGD("converter AllTensor succ"); - MS_ASSERT(subGraphDef.nodes() != nullptr); - ret = ConverterNodes(*(subGraphDef.nodes()), ctx); - if (ret != RET_OK) { - MS_LOGE("ConverterNodes failed: %d", ret); - return ret; - } - MS_LOGD("converter nodes succ"); - - ret = ConverterEdges(subGraphDef); - if (ret != RET_OK) { - MS_LOGE("ConverterEdges failed: %d", ret); - return ret; - } - MS_LOGD("converter edges succ"); - - ret = InitOutputsMap(); - if (ret != RET_OK) { - MS_LOGE("InitOutputsMap failed: %d", ret); - return ret; - } - MS_LOGD("init outputs map succ"); - - MS_LOGD("build graph succ"); - return RET_OK; -} - -int SubGraph::ConverterIndex(const flatbuffers::Vector &srcIndex, std::vector *dstIndex) { - if (dstIndex == nullptr) { - MS_LOGE("input dstIndex is nullptr"); - return RET_PARAM_INVALID; - } - dstIndex->resize(srcIndex.size()); - std::copy(srcIndex.begin(), srcIndex.end(), dstIndex->begin()); - return RET_OK; -} - -int SubGraph::ConverterAllTensor(const flatbuffers::Vector> &srcTensors) { - uint32_t tensorsSize = srcTensors.size(); - - allTensors.clear(); - allTensors.reserve(tensorsSize); - for (uint32_t i = 0; i < tensorsSize; i++) { - auto tensorDef = srcTensors.GetAs(i); - if (tensorDef == nullptr) { - MS_LOGE("%ud th tensordef is null", i); - return RET_ERROR; - } - auto tensor = Tensor::CopyFromTensorDef(*tensorDef); - if (tensor == nullptr) { - return RET_ERROR; - } - allTensors.push_back(tensor); - } - - return RET_OK; -} - -int SubGraph::ConverterNodes(const flatbuffers::Vector> &nodeDefs, const Context &ctx) { - uint32_t opCount = nodeDefs.size(); - // for dfx - if (opCount > G_MAX_OP_COUNT) { - MS_LOGE("opCount(%u) bigger than maxOpCount(%u)", opCount, G_MAX_OP_COUNT); - return RET_ERROR; - } - - nodes.clear(); - - for (uint32_t i = 0; i < opCount; i++) { - auto nodeDef = nodeDefs.GetAs(i); - MS_ASSERT(nodeDef != nullptr); - auto node = std::unique_ptr(new (std::nothrow) Node(nodeDef)); - if (node == nullptr) { - MS_LOGE("new node failed"); - return RET_NULL_PTR; - } - - node->SetTensors(*nodeDef, allTensors); - - auto ret = node->InitOp(*(nodeDef->opDef()), ctx); - if (ret != RET_OK) { - MS_LOGE("node (%s) InitOP failed. ret:%d", node->ID().c_str(), ret); - return ret; - } - - auto nodeId = node->ID(); - nodes[nodeId] = node.release(); - MS_LOGD("add node succ, id:%s", nodeId.c_str()); - } - - return RET_OK; -} - -int SubGraph::ConverterEdges(const SubGraphDef &subGraphDef) { - auto opGraph = OpGraph::Build(subGraphDef); - if (opGraph == nullptr) { - MS_LOGE("opGraph Build fail"); - return RET_ERROR; - } - - for (auto nodeIter : nodes) { - auto node = opGraph->GetNode(nodeIter.first); - if (node == nullptr) { - MS_LOGI("node %s not found", nodeIter.first.c_str()); - continue; - } - for (const auto &edge : node->GetAllInEdge()) { - MS_ASSERT(nodeIter.second != nullptr); - nodeIter.second->AddInEdge(GetNode(edge)); - } - for (const auto &edge : node->GetAllOutEdge()) { - MS_ASSERT(nodeIter.second != nullptr); - nodeIter.second->AddOutEdge(GetNode(edge)); - } - } - delete opGraph; - return RET_OK; -} - -int SubGraph::InitOutputsMap() { - if (nodes.empty()) { - MS_LOGE("nodes are empty"); - return RET_ERROR; - } - for (auto node : nodes) { - NODE_ID realNodeName = node.second->ID(); - MS_ASSERT(node.second != nullptr); - if (node.second->GetAllOutEdges().empty()) { - auto nodeType = node.second->Type(); - if (nodeType == "Nhwc2Nchw" || nodeType == "Nchw2Nhwc") { - auto dependNode = *(this->GetDepends().at(this->GetNode(realNodeName)).begin()); - realNodeName = dependNode->ID(); - } - this->outputsMap.emplace( - std::pair>(realNodeName, node.second->GetOutputTensors())); - } - } - return RET_OK; -} - -std::unordered_map> SubGraph::GetDepends() { - std::unordered_map> depends; - for (auto nodeIter : nodes) { - MS_ASSERT(nodeIter.second != nullptr); - depends[nodeIter.second] = nodeIter.second->GetAllInEdges(); - } - return depends; -} - -Node *SubGraph::GetNode(const NODE_ID &id) { - auto node = nodes.find(id); - if (node == nodes.end()) { - return nullptr; - } - return node->second; -} - -std::vector SubGraph::GetInputs() { - std::vector inputTensor; - inputTensor.resize(inputIndices.size()); - std::transform(inputIndices.begin(), inputIndices.end(), inputTensor.begin(), - [this](int i) { return this->allTensors[i]; }); - - return inputTensor; -} - -std::vector SubGraph::GetOutputs() { - std::vector outputTensor; - outputTensor.resize(outputIndices.size()); - std::transform(outputIndices.begin(), outputIndices.end(), outputTensor.begin(), - [this](int i) { return this->allTensors[i]; }); - - return outputTensor; -} - -std::map> &SubGraph::GetOutputsMap() { return outputsMap; } - -void SubGraph::FreeAllTensors() { - for (auto &allTensor : allTensors) { - if (allTensor != nullptr) { - auto refcount = allTensor->RefCount(); - if (refcount != MSConst_WEIGHT_REFCOUNT) { - allTensor->DefRef(refcount); - allTensor->FreeData(); - } - } - } -} - -const std::vector *SubGraph::GetInputIndices() const { return &inputIndices; } - -const std::vector *SubGraph::GetOutputIndices() const { return &outputIndices; } - -bool SubGraph::IsInputIndex(uint32_t i) { - auto iter = std::find(inputIndices.begin(), inputIndices.end(), i); - return !(iter == inputIndices.end()); -} - -bool SubGraph::IsOutputIndex(uint32_t i) { - auto iter = std::find(outputIndices.begin(), outputIndices.end(), i); - return !(iter == outputIndices.end()); -} -} // namespace predict -} // namespace mindspore diff --git a/predict/src/graph.h b/predict/src/graph.h deleted file mode 100755 index f02c46f94e..0000000000 --- a/predict/src/graph.h +++ /dev/null @@ -1,101 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PREDICT_SRC_GRAPH_H_ -#define PREDICT_SRC_GRAPH_H_ - -#include -#include -#include -#include -#include -#include -#include "common/utils.h" -#include "common/graph_util.h" -#include "include/tensor.h" -#include "src/node.h" - -#define MSPREDICT_API __attribute__((visibility("default"))) - -namespace mindspore { -namespace predict { -class SubGraph { - public: - SubGraph(); - ~SubGraph(); - static SubGraph *CreateSubGraph(const SubGraphDef &subGraphDef, const Context &ctx); - int Build(const SubGraphDef &subGraphDef, const Context &ctx); - bool IsInputIndex(uint32_t i); - bool IsOutputIndex(uint32_t i); - - const std::vector *GetInputIndices() const; - const std::vector *GetOutputIndices() const; - - std::vector GetInputs(); - std::vector GetOutputs(); - std::map> &GetOutputsMap(); - void FreeAllTensors(); - - Node *GetNode(const NODE_ID &id); - - std::unordered_map> GetDepends(); - - private: - int ConverterIndex(const flatbuffers::Vector &srcIndex, std::vector *dstIndex); - - int ConverterAllTensor(const flatbuffers::Vector> &srcTensors); - - int ConverterNodes(const flatbuffers::Vector> &opDefs, const Context &ctx); - - int ConverterEdges(const SubGraphDef &subGraphDef); - - int InitOutputsMap(); - - protected: - std::unordered_map nodes; - std::vector inputIndices; - std::vector outputIndices; - std::vector allTensors; // weight + input + output - std::map> outputsMap; -}; - -class MSPREDICT_API Graph { - public: - Graph(); - ~Graph(); - static Graph *CreateFromBuf(const char *buf, size_t size, const Context &ctx); - - std::vector GetInputs(); - std::vector GetOutputs(); - - std::map> &GetOutputsMap(); - - void FreeAllTensors(); - - int Build(const GraphDef &def, const Context &ctx); - std::vector *Subgraphs(); - - protected: - friend class GraphExecution; - - std::vector subgraphs; - std::unordered_map> depends; // records the dependencies - std::deque readyQue; // the nodes which can execute without any dependencies -}; -} // namespace predict -} // namespace mindspore - -#endif // PREDICT_SRC_GRAPH_H_ diff --git a/predict/src/graph_execution.cc b/predict/src/graph_execution.cc deleted file mode 100644 index 4a71d4359a..0000000000 --- a/predict/src/graph_execution.cc +++ /dev/null @@ -1,293 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "src/graph_execution.h" -#include -#include -#include - -namespace mindspore { -namespace predict { -GraphExecution::GraphExecution(const Context &ctx) : graph(nullptr), _ctx(ctx) {} -GraphExecution::GraphExecution(const Context &ctx, Graph *staticGraph) : _ctx(ctx) { - graph = staticGraph; - if (graph != nullptr) { - depends = graph->depends; - readyQue = graph->readyQue; - outputTensors = graph->GetOutputs(); - inputTensors = graph->GetInputs(); - } -} - -GraphExecution::~GraphExecution() = default; - -int GraphExecution::TransInputDataToNc4hw4(const Tensor &src, Tensor *dst) { - MS_ASSERT(dst != nullptr); - if (dst->GetData() == nullptr) { - auto ret = dst->MallocData(nullptr, MSConst_WEIGHT_REFCOUNT); - if (ret != RET_OK) { - MS_LOGE("Malloc inputTensors failed: %d", ret); - return ret; - } - } - auto ret = NchwToNc4hw4(&src, dst); - if (ret != RET_OK) { - MS_LOGE("NchwToNc4hw4 failed"); - return ret; - } - return RET_OK; -} - -int GraphExecution::SetInputTensors(const std::vector &inputs) { - size_t num = inputs.size(); - if (num != inputTensors.size()) { - MS_LOGE("input num %zu != model input num %zu", num, inputTensors.size()); - return RET_INPUT_TENSOR_ERROR; - } - - for (size_t i = 0; i < num; i++) { - MS_ASSERT(inputs[i] != nullptr); - // The input Tensor desc must be equivalent with the model tensor - if (inputs[i]->GetData() == nullptr) { - MS_LOGE("input tensor data is null!"); - return RET_INPUT_TENSOR_ERROR; - } - if (inputTensors[i] == nullptr) { - MS_LOGE("inputTensors[%zu] is nullptr", i); - return RET_ERROR; - } - - if (!inputs[i]->CompareShape(*inputTensors[i])) { - MS_LOGE("tensor shape in graph and executor are different!"); - return RET_INPUT_TENSOR_ERROR; - } - - if (inputs[i]->GetDataType() != inputTensors[i]->GetDataType()) { - MS_LOGE("tensor datatype in graph and executor are different!"); - return RET_INPUT_TENSOR_ERROR; - } - - if (inputs[i]->GetFormat() != Format_NCHW) { - MS_LOGE("input format not support. only nchw is supported now"); - return RET_INPUT_TENSOR_ERROR; - } - - if (inputs[i]->GetFormat() == inputTensors[i]->GetFormat()) { - auto data = inputs[i]->GetData(); - if (data == nullptr) { - MS_LOGE("data of input tensor is null!"); - return RET_INPUT_TENSOR_ERROR; - } - inputTensors[i]->SetData(data); - } else if (inputTensors[i]->GetFormat() == Format_NC4HW4) { - auto ret = TransInputDataToNc4hw4(*inputs[i], inputTensors[i]); - if (ret != RET_OK) { - MS_LOGE("TransInputDataToNc4hw4 failed"); - return ret; - } - } else { - MS_LOGE("graphDef inputTensors format is invalid: %d", inputTensors[i]->GetFormat()); - return RET_ERROR; - } - } - return RET_OK; -} - -int GraphExecution::MallocOutput() { - for (auto tensor : outputTensors) { - auto ret = tensor->MallocData(); - if (ret != RET_OK) { - MS_LOGE("malloc output data failed"); - return RET_ERROR; - } - } - return RET_OK; -} - -void GraphExecution::FreeTensors(std::vector *tensors) { - for (auto &tensor : (*tensors)) { - delete tensor; - } - tensors->clear(); -} - -void GraphExecution::FreeOutputMap(std::map> *map) { - MS_ASSERT(map != nullptr); - for (auto &m : *map) { - FreeTensors(&(m.second)); - } - map->clear(); -} - -int GraphExecution::CopyOutputTensors(const std::vector &refOutputs, std::vector *outputs) { - for (auto tensor : refOutputs) { - if (tensor == nullptr) { - MS_LOGE("tensor in refOutputs is nullptr"); - return RET_INPUT_TENSOR_ERROR; - } - std::unique_ptr t(new Tensor(*tensor)); - if (t == nullptr) { - MS_LOGE("new Tensor failed."); - if (outputs != nullptr) { - FreeTensors(outputs); - } - return RET_ERROR; - } - - if (tensor->GetFormat() == Format_NC4HW4) { - t->SetFormat(Format_NCHW); - auto ret = t->MallocData(); - if (ret != RET_OK) { - MS_LOGE("malloc data failed.") - FreeTensors(outputs); - return ret; - } - - ret = Nc4hw4ToNchw(tensor, t.get()); - if (ret != RET_OK) { - MS_LOGE("Nc4hw4ToNchw failed"); - return ret; - } - tensor->FreeData(); - } else { - t->SetData(tensor->GetData()); - tensor->SetData(nullptr); - } - outputs->push_back(t.release()); - } - return RET_OK; -} - -std::map> GraphExecution::GetAllOutput() { - std::map> outputs{}; - for (auto &outputNode : graph->GetOutputsMap()) { - std::vector outputNodeTensors{}; - auto ret = this->CopyOutputTensors(outputNode.second, &outputNodeTensors); - if (ret != RET_OK) { - MS_LOGE("copy output failed."); - FreeOutputMap(&outputs); - return outputs; - } - outputs.emplace(std::pair>(outputNode.first, outputNodeTensors)); - } - return outputs; -} - -std::vector GraphExecution::GetOutput(const NODE_ID &nodeName) { - std::vector outputNodeTensors{}; - auto iter = graph->GetOutputsMap().find(nodeName); - if (iter == graph->GetOutputsMap().end()) { - MS_LOGE("node name is not in output."); - return outputNodeTensors; - } - auto ret = this->CopyOutputTensors(iter->second, &outputNodeTensors); - if (ret != RET_OK) { - MS_LOGE("copy output failed."); - } - return outputNodeTensors; -} - -std::vector GraphExecution::GetInput() { - std::vector inputs{}; - for (auto refInput : graph->GetInputs()) { - if (refInput == nullptr) { - MS_LOGE("tensor from graph->GetInputs() is nullptr"); - return inputs; - } - std::unique_ptr t(new Tensor(refInput->GetDataType(), refInput->GetDims(), Format_NCHW, nullptr)); - if (t == nullptr) { - MS_LOGE("new Tensor failed.") - FreeTensors(&inputs); - return inputs; - } - inputs.push_back(t.release()); - } - return inputs; -} - -void GraphExecution::ResetInputData() { - for (auto tensor : inputTensors) { - if (tensor == nullptr) { - MS_LOGW("tensor in inputTensors is nullptr"); - continue; - } - if (tensor->GetFormat() == Format_NC4HW4) { - if (tensor->GetData() != nullptr) { - free(tensor->GetData()); - tensor->SetData(nullptr); - } - continue; - } - tensor->SetData(nullptr); - } -} - -void GraphExecution::FreeAllTensors() { graph->FreeAllTensors(); } - -int GraphExecution::Run(const std::vector &inputs) { - if (inputs.empty()) { - MS_LOGE("input is empty"); - return RET_ERROR; - } - - int ret; - - if (readyQue.empty()) { - MS_LOGE("readyQue is empty"); - return RET_ERROR; - } - - ret = SetInputTensors(inputs); - if (ret != RET_OK) { - MS_LOGE("SetInputTensors failed: %d", ret); - ResetInputData(); - return ret; - } - ret = MallocOutput(); - if (ret != RET_OK) { - MS_LOGE("MallocOutput failed: %d", ret); - ResetInputData(); - return ret; - } - - while (!readyQue.empty()) { - auto *node = readyQue.front(); - readyQue.pop_front(); - - ret = node->Run(_ctx); - if (ret != RET_OK) { - MS_LOGE("node (%s) failed to run op (%s). error code:%d", node->ID().c_str(), node->Type().c_str(), ret); - ResetInputData(); - FreeAllTensors(); - return ret; - } - - for (auto outNode : node->GetAllOutEdges()) { - auto nodeDepend = depends.find(outNode); - nodeDepend->second.erase(node); - if (nodeDepend->second.empty()) { - depends.erase(nodeDepend); - readyQue.push_back(outNode); - } - } - } - - ResetInputData(); - - return RET_OK; -} -} // namespace predict -} // namespace mindspore diff --git a/predict/src/graph_execution.h b/predict/src/graph_execution.h deleted file mode 100644 index 022be21865..0000000000 --- a/predict/src/graph_execution.h +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PREDICT_SRC_GRAPH_EXECUTION_H_ -#define PREDICT_SRC_GRAPH_EXECUTION_H_ - -#include -#include -#include -#include -#include -#include -#include -#include "common/mslog.h" -#include "src/graph.h" -#include "include/errorcode.h" -#include "schema/inner/ms_generated.h" -#include "src/operator/cpu/include/op_func_comm.h" -#include "src/node.h" - -namespace mindspore { -namespace predict { -class GraphExecution { - public: - explicit GraphExecution(const Context &ctx); - GraphExecution(const Context &ctx, Graph *staticGraph); - virtual ~GraphExecution(); - - virtual std::vector GetInput(); - virtual int SetInputTensors(const std::vector &inputs); - - virtual int Run(const std::vector &inputs); - - virtual std::map> GetAllOutput(); - virtual std::vector GetOutput(const NODE_ID &nodeName); - - private: - void ResetInputData(); - int MallocOutput(); - void FreeTensors(std::vector *tensors); - int TransInputDataToNc4hw4(const Tensor &src, Tensor *dst); - int CopyOutputTensors(const std::vector &refOutputs, std::vector *outputs); - void FreeOutputMap(std::map> *map); - void FreeAllTensors(); - - protected: - Graph *graph; - const Context &_ctx; - std::vector inputTensors; - std::vector outputTensors; - std::unordered_map> depends; // records the dependencies - std::deque readyQue; // the nodes which can execute without any dependencies -}; -} // namespace predict -} // namespace mindspore - -#endif // PREDICT_SRC_GRAPH_EXECUTION_H_ diff --git a/predict/src/node.cc b/predict/src/node.cc deleted file mode 100644 index 7128dde209..0000000000 --- a/predict/src/node.cc +++ /dev/null @@ -1,148 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "src/node.h" -#include -#include -#include -#include -#include -#include "schema/inner/ms_generated.h" -#include "common/mslog.h" -#include "common/op_utils.h" -#include "include/errorcode.h" -#include "src/op_factory.h" - -namespace mindspore { -namespace predict { -Node::Node(const NodeDef *nodeDef) - : id(std::string(nodeDef->opDef()->name()->c_str())), type(GetOpTypeName(*nodeDef)) {} - -Node::~Node() { - if (op != nullptr) { - delete op; - } -} - -NODE_ID Node::ID() { return id; } - -std::string Node::Type() { return type; } - -void Node::SetTensors(const NodeDef &nodeDef, const std::vector &allTensors) { - if (nodeDef.opDef() == nullptr) { - MS_LOGE("nodeDef is null"); - return; - } - - auto inputIndex = nodeDef.opDef()->inputIndex(); - MS_ASSERT(inputIndex != nullptr); - inputs.resize(inputIndex->size()); - std::transform(inputIndex->begin(), inputIndex->end(), inputs.begin(), [allTensors](int i) { return allTensors[i]; }); - - auto outputIndex = nodeDef.opDef()->outputIndex(); - MS_ASSERT(outputIndex != nullptr); - outputs.resize(outputIndex->size()); - std::transform(outputIndex->begin(), outputIndex->end(), outputs.begin(), - [allTensors](int i) { return allTensors[i]; }); -} - -void Node::SetDepends(const std::unordered_set &deps) { depends = deps; } - -std::unordered_set Node::GetDepends() { return depends; } - -void Node::AddInEdge(Node *node) { - if (node == nullptr) { - MS_LOGE("node is null"); - return; - } - inEdges.insert(node); -} - -void Node::AddOutEdge(Node *node) { - if (node == nullptr) { - MS_LOGE("node is null"); - return; - } - outEdges.insert(node); -} - -std::unordered_set &Node::GetAllInEdges() { return inEdges; } - -std::unordered_set &Node::GetAllOutEdges() { return outEdges; } - -std::vector &Node::GetOutputTensors() { return outputs; } -std::vector &Node::GetInputTensors() { return inputs; } - -int Node::InitOp(const OpDef &opDef, const Context &ctx) { - OpDesc dst; - dst.type = GetOpType(opDef); - dst.arch = X86_FP32; - MS_ASSERT(OpFactory::GetInstance() != nullptr); - op = OpFactory::GetInstance()->GetOp(inputs, outputs, opDef, ctx, dst); - if (op == nullptr) { - MS_LOGE("Can't find opName: %s, type: %s ", id.c_str(), type.c_str()); - return RET_ERROR; - } - return RET_OK; -} - -int Node::Run(const Context &ctx) { - MS_LOGD("%s run start", id.c_str()); - auto ret = MallocOutput(ctx); - if (ret != RET_OK) { - MS_LOGE("MallocOutput failed: %d", ret); - return ret; - } - if (op == nullptr) { - MS_LOGE("op is nullptr."); - return RET_ERROR; - } - ret = op->Execute(inputs, outputs); - if (ret != RET_OK) { - return ret; - } - FreeInput(); - return RET_OK; -} - -int Node::MallocOutput(const Context &ctx) { - size_t refCount = outEdges.size(); - for (auto tensor : outputs) { - if (tensor == nullptr) { - MS_LOGE("tensor in outputs is nullptr"); - return RET_ERROR; - } - auto ret = tensor->MallocData(ctx.allocator, refCount); - if (ret != RET_OK) { - return ret; - } - } - return RET_OK; -} - -void Node::FreeInput() { - for (auto tensor : inputs) { - if (tensor == nullptr) { - MS_LOGW("tensor in inputs is nullptr"); - return; - } - if (tensor->RefCount() != MSConst_WEIGHT_REFCOUNT) { - tensor->FreeData(); - } - } -} -} // namespace predict -} // namespace mindspore diff --git a/predict/src/node.h b/predict/src/node.h deleted file mode 100644 index eebb1b4321..0000000000 --- a/predict/src/node.h +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PREDICT_SRC_NODE_H_ -#define PREDICT_SRC_NODE_H_ - -#include -#include -#include -#include "include/session.h" -#include "src/op.h" - -namespace mindspore { -namespace predict { -using NODE_ID = std::string; - -class Node { - public: - Node() = default; - explicit Node(const NodeDef *nodeDef); - virtual ~Node(); - NODE_ID ID(); - std::string Type(); - void SetTensors(const NodeDef &nodeDef, const std::vector &allTensors); - void SetDepends(const std::unordered_set &deps); - std::unordered_set GetDepends(); - - void AddInEdge(Node *node); - void AddOutEdge(Node *node); - std::unordered_set &GetAllOutEdges(); - std::unordered_set &GetAllInEdges(); - - std::vector &GetOutputTensors(); - std::vector &GetInputTensors(); - - int InitOp(const OpDef &opDef, const Context &ctx); - int Run(const Context &ctx); - int MallocOutput(const Context &ctx); - void FreeInput(); - - protected: - friend class GraphExecution; - NODE_ID id; - std::string type; - OpBase *op{}; - std::vector inputs; - std::vector outputs; - std::unordered_set depends; - std::unordered_set inEdges; - std::unordered_set outEdges; -}; -} // namespace predict -} // namespace mindspore - -#endif // PREDICT_SRC_NODE_H_ diff --git a/predict/src/op.cc b/predict/src/op.cc deleted file mode 100644 index ca99b7fdff..0000000000 --- a/predict/src/op.cc +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "src/op.h" - -namespace mindspore { -namespace predict { -OpBase::OpBase() : desc(nullptr) {} - -OpBase::~OpBase() = default; -} // namespace predict -} // namespace mindspore diff --git a/predict/src/op.h b/predict/src/op.h deleted file mode 100644 index a07ce21952..0000000000 --- a/predict/src/op.h +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PREDICT_SRC_OP_H_ -#define PREDICT_SRC_OP_H_ - -#include -#include -#include "include/context.h" -#include "include/tensor.h" -#include "include/errorcode.h" -#include "schema/inner/ms_generated.h" - -#define MSPREDICT_API __attribute__((visibility("default"))) - -namespace mindspore { -namespace predict { -enum OP_ARCH { X86_FP32, X86_INT8, ARM_FP32, ARM_FP16, ARM_INT8, GPU }; - -struct MSPREDICT_API OpDesc { - OP_ARCH arch; - OpT type; - - bool operator<(const OpDesc &dst) const { return (arch < dst.arch) || (type < dst.type); } -}; - -class MSPREDICT_API OpBase { - public: - OpBase(); - virtual ~OpBase(); - - virtual int Execute(const std::vector &inputs, const std::vector &outputs) = 0; - virtual int Init(const std::vector &inputs, const std::vector &outputs) = 0; - - protected: - const OpDesc *desc; - std::string name; -}; - -typedef OpBase *(*OpCreator)(const std::vector &inputs, const std::vector &outputs, - const OpDef &opDef, const Context &ctx, const OpDesc &desc); -} // namespace predict -} // namespace mindspore - -#endif // PREDICT_SRC_OP_H_ diff --git a/predict/src/op_common.h b/predict/src/op_common.h deleted file mode 100644 index c5eb69bd57..0000000000 --- a/predict/src/op_common.h +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PREDICT_SRC_OP_COMMON_H_ -#define PREDICT_SRC_OP_COMMON_H_ -#include - -namespace mindspore { -namespace predict { -static inline size_t AlignSize(size_t size, size_t align) { return (size + align - 1) & -align; } - -template -inline void Nchw2Nhwc(const Tsrc *in, Tdst *out, size_t h, size_t w, size_t c) { - MS_ASSERT(in != nullptr && out != nullptr); - const size_t sz = w * h; - - for (size_t cc = 0; cc < c; ++cc) { - auto pi = in + sz * cc; - - for (size_t el = 0; el < sz; ++el) { - out[cc + el * c] = (Tdst)pi[el]; - } - } -} - -template -inline void Nhwc2Nchw(const Tsrc *in, Tdst *out, size_t h, size_t w, size_t c) { - MS_ASSERT(in != nullptr && out != nullptr); - const size_t sz = w * h; - - for (auto cc = 0; cc < c; ++cc) { - auto po = out + sz * cc; - - for (size_t el = 0; el < sz; ++el) { - po[el] = (Tdst)in[cc + el * c]; - } - } -} - -template -inline void InverseQuantization(const Tsrc *srcdata, Tdst *dstdata, size_t datanum, float *parms) { - MS_ASSERT(srcdata != nullptr && dstdata != nullptr); - float scale = parms[2]; - float zeroPoint = parms[3]; - for (size_t i = 0; i < datanum; ++i) { - dstdata = (scale == 0) ? (0) : (Tdst)((srcdata[i] - zeroPoint) * scale); - } -} - -template -inline void Astype(const Tsrc *srcdata, Tdst *dstdata, size_t datanum) { - MS_ASSERT(srcdata != nullptr && dstdata != nullptr); - for (size_t i = 0; i < datanum; ++i) { - dstdata[i] = (Tdst)srcdata[i]; - } -} -#define MSMIN(x, y) ((x) < (y) ? (x) : (y)) -#define MSMAX(x, y) ((x) > (y) ? (x) : (y)) - -#define UP_DIV(x, y) (((x) + (y) - (1)) / (y)) -#define DOWN_DIV(x, y) (((x) - (y) + (1)) / (y)) -#define ROUND_UP(x, y) (((x) + (y) - (1)) / (y) * (y)) -#define ALIGN_UP4(x) ROUND_UP((x), 4) -#define ALIGN_UP8(x) ROUND_UP((x), 8) - -#define MAX_MALLOC_SIZE 100 * 1024 * 1024 -} // namespace predict -} // namespace mindspore - -#endif // PREDICT_SRC_OP_COMMON_H_ diff --git a/predict/src/op_factory.cc b/predict/src/op_factory.cc deleted file mode 100644 index 2506db68d8..0000000000 --- a/predict/src/op_factory.cc +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "src/op_factory.h" - -namespace mindspore { -namespace predict { -OpFactory::OpFactory() { InitKernelManager(0, ""); } - -OpFactory::~OpFactory() = default; - -OpFactory *OpFactory::GetInstance() { - static OpFactory instance; - return &instance; -} - -OpBase *OpFactory::GetOp(const std::vector &inputs, const std::vector &outputs, const OpDef &opDef, - const Context &ctx, const OpDesc &desc) { - MS_ASSERT(GetRegistryInstance() != nullptr); - auto *reg = GetRegistryInstance()->GetInstance(MODULE_REG_NAME_OP_REGISTRY); - if (reg != nullptr) { - auto creator = reg->GetOpCreator(desc); - if (creator) { - return creator(inputs, outputs, opDef, ctx, desc); - } - } - MS_ASSERT(OpRegistry::GetInstance() != nullptr); - auto creator = OpRegistry::GetInstance()->GetOpCreator(desc); - if (creator) { - return creator(inputs, outputs, opDef, ctx, desc); - } - return nullptr; -} -} // namespace predict -} // namespace mindspore diff --git a/predict/src/op_factory.h b/predict/src/op_factory.h deleted file mode 100644 index 583a605d8c..0000000000 --- a/predict/src/op_factory.h +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PREDICT_SRC_OP_FACTORY_H_ -#define PREDICT_SRC_OP_FACTORY_H_ - -#include -#include "lite/api/km_api.h" -#include "src/op.h" -#include "src/op_registry.h" - -namespace mindspore { -namespace predict { -class OpFactory { - public: - OpFactory(); - virtual ~OpFactory(); - - static OpFactory *GetInstance(); - OpBase *GetOp(const std::vector &inputs, const std::vector &outputs, const OpDef &opDef, - const Context &ctx, const OpDesc &desc); -}; -} // namespace predict -} // namespace mindspore - -#endif // PREDICT_SRC_OP_FACTORY_H_ diff --git a/predict/src/op_registry.cc b/predict/src/op_registry.cc deleted file mode 100644 index 14cd810d54..0000000000 --- a/predict/src/op_registry.cc +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "src/op_registry.h" - -namespace mindspore { -namespace predict { -OpRegistry::OpRegistry() = default; - -OpRegistry::~OpRegistry() = default; - -OpRegistry *OpRegistry::GetInstance() { - static OpRegistry instance; - return &instance; -} - -OpCreator OpRegistry::GetOpCreator(const OpDesc &desc) { - auto it = creators.find(desc); - if (it != creators.end()) { - return it->second; - } - return nullptr; -} - -void OpRegistry::RegOp(const OpDesc desc, OpCreator creator) { creators[desc] = creator; } - -void OpRegistry::RegOp(const OP_ARCH arch, const OpT type, OpCreator creator) { - OpDesc desc = {arch, type}; - creators[desc] = creator; -} - -bool OpRegistry::Merge(const std::unordered_map &newCreators) { return false; } - -const std::map &OpRegistry::GetOpCreators() { return creators; } -} // namespace predict -} // namespace mindspore diff --git a/predict/src/op_registry.h b/predict/src/op_registry.h deleted file mode 100644 index bb1d957fec..0000000000 --- a/predict/src/op_registry.h +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PREDICT_SRC_OP_REGISTRY_H_ -#define PREDICT_SRC_OP_REGISTRY_H_ - -#include -#include -#include -#include "common/mslog.h" -#include "common/module_registry.h" -#include "src/op.h" - -#define MSPREDICT_API __attribute__((visibility("default"))) - -namespace mindspore { -namespace predict { -class MSPREDICT_API OpRegistry { - public: - OpRegistry(); - virtual ~OpRegistry(); - - static OpRegistry *GetInstance(); - virtual OpCreator GetOpCreator(const OpDesc &desc); - - const std::map &GetOpCreators(); - - void RegOp(OpDesc desc, OpCreator creator); - void RegOp(OP_ARCH arch, OpT type, OpCreator creator); - static bool Merge(const std::unordered_map &newCreators); - - protected: - std::map creators; -}; - -template <> -class Module : public ModuleBase { - public: - virtual OpRegistry *GetInstance() = 0; -}; - -const char MODULE_REG_NAME_OP_REGISTRY[] = "op_registry"; - -class OpRegistrar { - public: - OpRegistrar(const OpDesc &desc, OpCreator creator) { OpRegistry::GetInstance()->RegOp(desc, creator); } - - OpRegistrar(const OP_ARCH arch, const OpT type, OpCreator creator) { - MS_ASSERT(OpRegistry::GetInstance() != nullptr); - OpRegistry::GetInstance()->RegOp(arch, type, creator); - } -}; - -#define REG_OP(arch, type, opCreater) static OpRegistrar g_##arch##type##OpReg(arch, type, opCreater); -} // namespace predict -} // namespace mindspore - -#endif // PREDICT_SRC_OP_REGISTRY_H_ diff --git a/predict/src/operator/cpu/common/op_func_comm.cc b/predict/src/operator/cpu/common/op_func_comm.cc deleted file mode 100755 index f05e224ec3..0000000000 --- a/predict/src/operator/cpu/common/op_func_comm.cc +++ /dev/null @@ -1,422 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "src/operator/cpu/include/op_func_comm.h" -#include "include/errorcode.h" -#include "include/tensor.h" -#include "common/mslog.h" -#include "securec/include/securec.h" - -namespace mindspore { -namespace predict { -#ifndef MS_USE_NEON -#ifndef MS_USE_SSE - -void MSAddBias(float *srcPtr, const float *bias, size_t unitSize, size_t count) { - if (srcPtr == nullptr || bias == nullptr) { - MS_LOGW("srcPtr or bias is nullptr"); - return; - } - for (size_t stepU = 0; stepU < count; stepU++) { - float *tmpPtr = srcPtr + unitSize * CAL_STEP * stepU; - const float *biasPtr = bias + CAL_STEP * stepU; - for (size_t step = 0; step < unitSize; step++) { - float *dstPtr = tmpPtr + CAL_STEP * step; - for (int i = 0; i < CAL_STEP; i++) { - dstPtr[i] += biasPtr[i]; - } - } - } -} - -void MSAddBiasRelu(float *srcPtr, const float *bias, size_t unitSize, size_t count) { - if (srcPtr == nullptr || bias == nullptr) { - MS_LOGW("srcPtr or bias is nullptr"); - return; - } - for (size_t stepU = 0; stepU < count; stepU++) { - float *tmpPtr = srcPtr + unitSize * CAL_STEP * stepU; - const float *biasPtr = bias + CAL_STEP * stepU; - for (size_t step = 0; step < unitSize; step++) { - float *dstPtr = tmpPtr + CAL_STEP * step; - for (int i = 0; i < CAL_STEP; i++) { - dstPtr[i] += biasPtr[i]; - dstPtr[i] = (dstPtr[i] < 0) ? 0 : dstPtr[i]; - } - } - } -} - -void MSAddBiasRelu6(float *srcPtr, const float *bias, size_t unitSize, size_t count) { - if (srcPtr == nullptr || bias == nullptr) { - MS_LOGW("srcPtr or bias is nullptr"); - return; - } - for (size_t stepU = 0; stepU < count; stepU++) { - float *tmpPtr = srcPtr + unitSize * CAL_STEP * stepU; - const float *biasPtr = bias + CAL_STEP * stepU; - for (size_t step = 0; step < unitSize; step++) { - float *dstPtr = tmpPtr + CAL_STEP * step; - for (int i = 0; i < CAL_STEP; i++) { - dstPtr[i] += biasPtr[i]; - dstPtr[i] = (dstPtr[i] < 0) ? 0 : dstPtr[i]; - dstPtr[i] = (dstPtr[i] > 6.0f) ? 6.0f : dstPtr[i]; - } - } - } -} - -void MSCopyC4WithStride(const float *srcPtr, float *dstPtr, size_t srcStride, size_t dstStride, size_t count) { - if (srcPtr == nullptr || dstPtr == nullptr) { - MS_LOGW("srcPtr or dstPtr is nullptr"); - return; - } - for (size_t stepU = 0; stepU < count; stepU++) { - auto sPtr = srcPtr + stepU * srcStride; - auto dPtr = dstPtr + stepU * dstStride; - int tmpC = 0; - while (tmpC < CAL_STEP) { - dPtr[tmpC] = sPtr[tmpC]; - tmpC++; - } - } -} -#endif // MS_USE_SSE - -int MSPackC4(float *dstPtr, const float *srcPtr, size_t area, size_t depth) { - if (dstPtr == nullptr || srcPtr == nullptr) { - MS_LOGE("srcPtr or dstPtr is nullptr"); - return RET_ERROR; - } - int cur = 0; - size_t size = area * UP_DIV(depth, CAL_STEP) * CAL_STEP * sizeof(float); - auto ret = memset_s(dstPtr, size, 0, size); - if (ret != EOK) { - MS_LOGE("memset_s failed!"); - return RET_ERROR; - } - for (size_t step = 0; step < depth; step++) { - auto plane = step / CAL_STEP; - auto offset = step % CAL_STEP; - auto dstPlane = plane * area * CAL_STEP + dstPtr; - for (size_t i = 0; i < area; i++) { - dstPlane[CAL_STEP * i + offset] = srcPtr[cur++]; - } - } - return RET_OK; -} - -void MSUnpackC4(float *dstPtr, const float *srcPtr, size_t area, size_t depth) { - if (dstPtr == nullptr || srcPtr == nullptr) { - MS_LOGW("srcPtr or dstPtr is nullptr"); - return; - } - int cur = 0; - for (size_t step = 0; step < depth; step++) { - auto plane = step / CAL_STEP; - auto offset = step % CAL_STEP; - auto srcPlane = plane * area * CAL_STEP + srcPtr; - for (size_t i = 0; i < area; i++) { - dstPtr[cur++] = srcPlane[CAL_STEP * i + offset]; - } - } -} - -void MSUInt8ToInt16WithOffsetC4Common(int16_t *dstPtr, const uint8_t *srcPtr, size_t zeroPoint, size_t sizeQuad, - size_t dstStride, size_t srcStride) { - if (dstPtr == nullptr || srcPtr == nullptr) { - MS_LOGW("srcPtr or dstPtr is nullptr"); - return; - } - for (size_t step = 0; step < sizeQuad; step++) { - auto dstZ = dstPtr + (dstStride / sizeof(int16_t)) * step; - auto srcZ = srcPtr + (srcStride / sizeof(uint8_t)) * step; - for (int i = 0; i < CAL_STEP; i++) { - dstZ[i] = (int16_t)((int32_t)srcZ[i] - (int32_t)zeroPoint); - } - } -} - -void MSUInt8ToInt16WithOffsetC4Fast(int16_t *colAddr, const uint8_t *srcStart, size_t zeroPoint, size_t sizeQuad, - size_t depthQuad, size_t dstZStep, size_t srcZStep) { - if (colAddr == nullptr || srcStart == nullptr) { - MS_LOGW("colAddr or srcStart is nullptr"); - return; - } - for (size_t step = 0; step < depthQuad; step++) { - auto dstZ = colAddr + (dstZStep / sizeof(int16_t)) * step; - auto srcZ = srcStart + (srcZStep / sizeof(uint8_t)) * step; - MSUInt8ToInt16WithOffsetC4Common(dstZ, srcZ, zeroPoint, sizeQuad, CAL_STEP * sizeof(int16_t), - CAL_STEP * sizeof(uint8_t)); - } -} -#endif - -void MSPackC4Uint8(uint8_t *dstPtr, const uint8_t *srcPtr, size_t area, size_t depth) { - if (dstPtr == nullptr || srcPtr == nullptr) { - MS_LOGW("srcPtr or dstPtr is nullptr"); - return; - } - int cur = 0; - size_t size = area * UP_DIV(depth, CAL_STEP) * CAL_STEP * sizeof(uint8_t); - auto ret = memset_s(dstPtr, size, 0, size); - if (ret != EOK) { - MS_LOGE("memset_s failed!"); - return; - } - for (size_t step = 0; step < depth; step++) { - auto plane = step / CAL_STEP; - auto offset = step % CAL_STEP; - auto dstPlane = plane * area * CAL_STEP + dstPtr; - for (size_t x = 0; x < area; ++x) { - dstPlane[CAL_STEP * x + offset] = srcPtr[cur++]; - } - } -} - -void MSUnpackC4Uint8(uint8_t *dstPtr, const uint8_t *srcPtr, size_t area, size_t depth) { - if (dstPtr == nullptr || srcPtr == nullptr) { - MS_LOGW("srcPtr or dstPtr is nullptr"); - return; - } - int cur = 0; - for (size_t step = 0; step < depth; step++) { - auto srcPlane = (step / CAL_STEP) * area * CAL_STEP + srcPtr; - for (size_t i = 0; i < area; i++) { - dstPtr[cur++] = srcPlane[CAL_STEP * i + (step % CAL_STEP)]; - } - } -} - -#ifdef MS_USE_NEON -static void MSTensorConvertNCHWToNC4HW4Depth(float *dst, const float *src, size_t area, size_t depth) { - if (dstPtr == nullptr || srcPtr == nullptr) { - MS_LOGW("srcPtr or dstPtr is nullptr"); - return; - } - if (1 == depth) { - auto zeroValue = vmovq_n_f32(0.0f); - int areaC4 = static_cast(area / CAL_STEP); - int remain = areaC4 * CAL_STEP; - for (int i = 0; i < areaC4; ++i) { - auto srcCur = src + CAL_STEP * i; - auto dstCur = dst + CAL_STEP * CAL_STEP * i; - auto srcValue = vld1q_f32(srcCur); - float32x4x4_t dstValue; - dstValue.val[0] = srcValue; - dstValue.val[1] = zeroValue; - dstValue.val[2] = zeroValue; - dstValue.val[3] = zeroValue; - vst4q_f32(dstCur, dstValue); - } - for (int i = remain; i < area; ++i) { - dst[CAL_STEP * i + 0] = src[i]; - dst[CAL_STEP * i + 1] = 0.0f; - dst[CAL_STEP * i + 2] = 0.0f; - dst[CAL_STEP * i + 3] = 0.0f; - } - } else if (3 == depth) { - auto zeroValue = vmovq_n_f32(0.0f); - int areaC4 = static_cast(area / CAL_STEP); - int remain = areaC4 * CAL_STEP; - for (int i = 0; i < areaC4; ++i) { - auto srcCur = src + 12 * i; - auto dstCur = dst + 16 * i; - auto srcValue = vld3q_f32(srcCur); - float32x4x4_t dstValue; - dstValue.val[0] = srcValue.val[0]; - dstValue.val[1] = srcValue.val[1]; - dstValue.val[2] = srcValue.val[2]; - dstValue.val[3] = zeroValue; - vst4q_f32(dstCur, dstValue); - } - for (int i = remain; i < area; ++i) { - dst[CAL_STEP * i + 0] = src[3 * i + 0]; - dst[CAL_STEP * i + 1] = src[3 * i + 1]; - dst[CAL_STEP * i + 2] = src[3 * i + 2]; - dst[CAL_STEP * i + 3] = 0.0f; - } - } -} -#endif - -void MSTensorConvertNHWCToNC4HW4(float *dst, const float *src, size_t area, size_t depth) { - if (dst == nullptr || src == nullptr) { - MS_LOGW("srcPtr or dstPtr is nullptr"); - return; - } -#ifdef MS_USE_NEON - MSTensorConvertNCHWToNC4HW4Depth(dst, src, area, depth); - return; -#endif - int c = static_cast(depth); - int cDiv4 = c / CAL_STEP; - int cMod4 = c % CAL_STEP; - int cAlign = cDiv4 * CAL_STEP; - for (int hi = 0; hi < area; ++hi) { - auto srcHeight = src + hi * c; - auto dstHeight = dst + hi * CAL_STEP; - for (int ci = 0; ci < cDiv4; ++ci) { -#ifdef MS_USE_NEON - vst1q_f32(dstHeight + CAL_STEP * ci * area, vld1q_f32(srcHeight + CAL_STEP * ci)); -#else - for (int i = 0; i < CAL_STEP; ++i) { - dstHeight[ci * area * CAL_STEP + i] = srcHeight[CAL_STEP * ci + i]; - } -#endif - } - } - - if (cMod4 == 0) { - MS_LOGW("depth should be multiple of four"); - return; - } - - auto srcAlign = src + cAlign; - auto dstAlign = dst + area * cAlign; - -#ifdef MS_USE_NEON - auto zeroVector = vdupq_n_f32(0.0f); -#endif - - for (int hi = 0; hi < area; ++hi) { - auto srcHeight = srcAlign + hi * c; - auto dstHeight = dstAlign + hi * CAL_STEP; -#ifdef MS_USE_NEON - vst1q_f32(dstHeight, zeroVector); -#else - for (int i = 0; i < CAL_STEP; ++i) { - dstHeight[i] = 0; - } -#endif - for (int ci = 0; ci < cMod4; ++ci) { - dstHeight[ci] = srcHeight[ci]; - } - } -} - -void MSTensorConvertNC4HW4ToNHWC(float *dst, const float *src, size_t area, size_t depth) { - if (dst == nullptr || src == nullptr) { - MS_LOGW("srcPtr or dstPtr is nullptr"); - return; - } - int c = static_cast(depth); - int cDiv4 = c / CAL_STEP; - int cMod4 = c % CAL_STEP; - int cAlign = cDiv4 * CAL_STEP; - for (int hi = 0; hi < area; ++hi) { - auto srcHeight = src + hi * CAL_STEP; - auto dstHeight = dst + hi * c; - for (int ci = 0; ci < cDiv4; ++ci) { -#ifdef MS_USE_NEON - vst1q_f32(dstHeight + CAL_STEP * ci, vld1q_f32(srcHeight + CAL_STEP * ci * area)); -#else - for (int i = 0; i < CAL_STEP; ++i) { - dstHeight[ci * CAL_STEP + i] = srcHeight[CAL_STEP * ci * area + i]; - } -#endif - } - } - - if (cMod4 == 0) { - MS_LOGW("depth should be multiple of four"); - return; - } - - auto srcAlign = src + area * cAlign; - auto dstAlign = dst + cAlign; - - for (int hi = 0; hi < area; ++hi) { - auto srcHeight = srcAlign + hi * CAL_STEP; - auto dstHeight = dstAlign + hi * c; - - for (int ci = 0; ci < cMod4; ++ci) { - dstHeight[ci] = srcHeight[ci]; - } - } -} - -int NchwToNc4hw4(const Tensor *input, Tensor *output) { - if (input == nullptr || output == nullptr) { - MS_LOGE("input or output is nullptr"); - return RET_ERROR; - } - int batch = static_cast(input->Batch()); - int channel = static_cast(input->Channel()); - MS_ASSERT(batch > 0); - MS_ASSERT(channel > 0); - int area = static_cast(input->Width()) * static_cast(input->Height()); - int inputStride = input->GetElementSize() / batch; - int outputStride = output->GetElementSize() / batch; - DataType dt = input->GetDataType(); - - MS_ASSERT(input->GetData()); - MS_ASSERT(output->GetData()); - - if (dt == DataType_DT_FLOAT) { - for (int i = 0; i < batch; ++i) { - auto ret = MSPackC4(reinterpret_cast(output->GetData()) + outputStride * i, - (const float *)input->GetData() + inputStride * i, area, channel); - if (ret != RET_OK) { - MS_LOGE("MSPackC4 failed: %d", ret); - return RET_ERROR; - } - } - } else if (dt == DataType_DT_UINT8) { - for (int i = 0; i < batch; ++i) { - MSPackC4Uint8(reinterpret_cast(output->GetData()) + outputStride * i, - (const uint8_t *)input->GetData() + inputStride * i, area, channel); - } - } else { - MS_LOGE("Unsupported dataType: %d", dt); - return RET_ERROR; - } - return RET_OK; -} - -int Nc4hw4ToNchw(const Tensor *input, Tensor *output) { - if (input == nullptr || output == nullptr) { - MS_LOGE("input tensor or output tensor is nullptr"); - return RET_ERROR; - } - - int batch = static_cast(input->Batch()); - int channel = static_cast(input->Channel()); - MS_ASSERT(batch > 0); - MS_ASSERT(channel > 0); - int area = static_cast(input->Width()) * static_cast(input->Height()); - int inputStride = input->GetElementSize() / batch; - int outputStride = output->GetElementSize() / batch; - DataType dt = input->GetDataType(); - if (dt == DataType_DT_FLOAT) { - for (int i = 0; i < batch; ++i) { - MSUnpackC4(reinterpret_cast(output->GetData()) + outputStride * i, - (const float *)input->GetData() + inputStride * i, area, channel); - } - } else if (dt == DataType_DT_UINT8) { - for (int i = 0; i < batch; ++i) { - MSUnpackC4Uint8(reinterpret_cast(output->GetData()) + outputStride * i, - (const uint8_t *)input->GetData() + inputStride * i, area, channel); - } - } else { - MS_LOGE("Unsupported dataType: %d", dt); - return RET_ERROR; - } - - return RET_OK; -} -} // namespace predict -} // namespace mindspore diff --git a/predict/src/operator/cpu/include/op_func_comm.h b/predict/src/operator/cpu/include/op_func_comm.h deleted file mode 100644 index 884803d669..0000000000 --- a/predict/src/operator/cpu/include/op_func_comm.h +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PREDICT_SRC_OPERATOR_CPU_INCLUDE_OP_FUNC_COMM_H_ -#define PREDICT_SRC_OPERATOR_CPU_INCLUDE_OP_FUNC_COMM_H_ - -#include -#include -#include -#include -#include -#include "src/op_common.h" -#include "include/tensor.h" - -#ifdef MS_USE_NEON -#include -#endif // MS_USE_NEON - -namespace mindspore { -namespace predict { -#ifdef __cplusplus -extern "C" { -#endif -#define CAL_STEP 4 -void MSAddBias(float *dst, const float *bias, size_t planeNumber, size_t biasNumber); -void MSAddBiasRelu(float *dst, const float *bias, size_t planeNumber, size_t biasNumber); -void MSAddBiasRelu6(float *dst, const float *bias, size_t planeNumber, size_t biasNumber); -void MSPackC4Uint8(uint8_t *dst, const uint8_t *src, size_t area, size_t depth); -void MSUnpackC4(float *dst, const float *src, size_t area, size_t depth); -void MSUnpackC4Uint8(uint8_t *dst, const uint8_t *src, size_t area, size_t depth); -void MSTensorConvertNHWCToNC4HW4(float *dst, const float *src, size_t area, size_t depth); -void MSTensorConvertNC4HW4ToNHWC(float *dst, const float *src, size_t area, size_t depth); -void MSUnpackC4(float *dst, const float *src, size_t area, size_t depth); -void MSCopyC4WithStride(const float *source, float *dest, size_t srcStride, size_t dstStride, size_t count); -void MSUInt8ToInt16WithOffsetC4Common(int16_t *dst, const uint8_t *src, size_t zeroPoint, size_t sizeQuad, - size_t dstStride, size_t srcStride); -void MSUInt8ToInt16WithOffsetC4Fast(int16_t *dst, const uint8_t *src, size_t zeroPoint, size_t sizeQuad, - size_t depthQuad, size_t dstZStep, size_t srcZStep); - -int MSPackC4(float *dst, const float *src, size_t area, size_t depth); -int NchwToNc4hw4(const Tensor *input, Tensor *output); -int Nc4hw4ToNchw(const Tensor *input, Tensor *output); -#ifdef __cplusplus -} -#endif -} // namespace predict -} // namespace mindspore - -#endif // PREDICT_SRC_OPERATOR_CPU_INCLUDE_OP_FUNC_COMM_H_ diff --git a/predict/src/runtime/allocator.cc b/predict/src/runtime/allocator.cc deleted file mode 100644 index cb94af5df9..0000000000 --- a/predict/src/runtime/allocator.cc +++ /dev/null @@ -1,135 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "src/runtime/allocator.h" -#include "common/module_registry.h" -#include "src/op_common.h" - -namespace mindspore { -namespace predict { -std::shared_ptr Allocator::Create() { - auto alloc = GetRegistryInstance()->Create(MODULE_REG_NAME_ALLOCATOR); - if (alloc != nullptr) { - return alloc; - } - - // default allocator - return std::shared_ptr(new DefaultAllocator()); -} - -DefaultAllocator::DefaultAllocator() = default; - -DefaultAllocator::~DefaultAllocator() { Clear(); } - -void DefaultAllocator::SetContext(const AllocatorContext &ctx) { - lockFlag = ctx.lockFlag; - shiftFactor = ctx.shiftFactor; -} - -void DefaultAllocator::Lock() { - if (lockFlag) { - lock.lock(); - } -} - -void DefaultAllocator::UnLock() { - if (lockFlag) { - lock.unlock(); - } -} - -void *DefaultAllocator::Malloc(size_t size) { - if (size > MAX_MALLOC_SIZE) { - return nullptr; - } - Lock(); - auto it = freeList.begin(); - for (; it != freeList.end(); it++) { - auto membuf = *it; - - if ((membuf->size >= size) && (membuf->size < (size << shiftFactor))) { - freeList.erase(it); - allocatedList.push_back(membuf); - UnLock(); - return membuf->buf; - } - } - std::unique_ptr membuf(reinterpret_cast(malloc(sizeof(MemBuf) + size))); - if (membuf == nullptr) { - UnLock(); - return nullptr; - } - membuf->size = size; - membuf->buf = reinterpret_cast(membuf.get()) + sizeof(MemBuf); - auto bufPtr = membuf->buf; - allocatedList.push_back(membuf.release()); - UnLock(); - return bufPtr; -} - -void DefaultAllocator::Free(void *buf) { - if (buf == nullptr) { - return; - } - Lock(); - auto it = allocatedList.begin(); - for (; it != allocatedList.end(); it++) { - auto membuf = *it; - - if (membuf->buf == buf) { - allocatedList.erase(it); - freeList.push_back(membuf); - UnLock(); - return; - } - } - UnLock(); - free(buf); -} - -size_t DefaultAllocator::GetTotalSize() { - Lock(); - size_t totalSize = 0; - auto it = allocatedList.begin(); - for (; it != allocatedList.end(); it++) { - auto membuf = *it; - totalSize += membuf->size; - } - it = freeList.begin(); - for (; it != freeList.end(); it++) { - auto membuf = *it; - totalSize += membuf->size; - } - UnLock(); - return totalSize; -} - -void DefaultAllocator::Clear() { - Lock(); - auto it = allocatedList.begin(); - for (; it != allocatedList.end(); it++) { - free(*it); - } - allocatedList.clear(); - it = freeList.begin(); - for (; it != freeList.end(); it++) { - free(*it); - } - freeList.clear(); - UnLock(); -} -} // namespace predict -} // namespace mindspore diff --git a/predict/src/runtime/allocator.h b/predict/src/runtime/allocator.h deleted file mode 100644 index a9d72fbc9d..0000000000 --- a/predict/src/runtime/allocator.h +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PREDICT_SRC_RUNTIME_ALLOCATOR_H_ -#define PREDICT_SRC_RUNTIME_ALLOCATOR_H_ - -#include -#include -#include -#include -#include "common/module_registry.h" - -namespace mindspore { -namespace predict { -struct AllocatorContext { - int shiftFactor; - bool lockFlag; -}; - -class Allocator { - public: - Allocator() : name("default") {} - virtual ~Allocator() {} - virtual void *Malloc(size_t size) = 0; - virtual void Free(void *ptr) = 0; - virtual void SetContext(const AllocatorContext &ctx) {} - virtual size_t GetTotalSize() { return 0; } - virtual void Clear() {} - static std::shared_ptr Create(); - std::string name; -}; - -class DefaultAllocator : public Allocator { - public: - DefaultAllocator(); - ~DefaultAllocator() override; - void SetContext(const AllocatorContext &ctx) override; - void *Malloc(size_t size) override; - void Free(void *ptr) override; - size_t GetTotalSize() override; - void Clear() override; - - private: - void Lock(); - void UnLock(); - struct MemBuf { - size_t size; - void *buf; - }; - - std::mutex lock; - std::vector allocatedList; - std::vector freeList; - int shiftFactor = 0; - bool lockFlag = false; -}; - -// these declaration are for module integration, refer to sample_allocator -const char MODULE_REG_NAME_ALLOCATOR[] = "allocator"; - -template <> class Module : public ModuleBase { - public: - virtual std::shared_ptr Create() = 0; -}; -} // namespace predict -} // namespace mindspore - -#endif // PREDICT_SRC_RUNTIME_ALLOCATOR_H_ diff --git a/predict/src/runtime/runtime_api.cc b/predict/src/runtime/runtime_api.cc deleted file mode 100644 index 2091c808ff..0000000000 --- a/predict/src/runtime/runtime_api.cc +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "src/runtime/runtime_api.h" -#include -#include -#include "src/runtime/workspace_pool.h" -#include "src/runtime/thread_pool.h" -#include "common/mslog.h" - -static std::mutex gWorkspaceMutex; -#ifdef __cplusplus -extern "C" { -#endif -void LiteAPISetLastError(const char *msg) { MS_LOGE("The lite api set last error is %s.", msg); } - -void *LiteBackendAllocWorkspace(int deviceType, int deviceId, uint64_t size, int dtypeCode, int dtypeBits) { - std::lock_guard lock(gWorkspaceMutex); - auto p = mindspore::predict::WorkspacePool::GetInstance(); - if (p == nullptr) { - MS_LOGE("get ThreadPool install failed"); - return nullptr; - } - return p->AllocWorkSpaceMem(size); -} - -int LiteBackendFreeWorkspace(int deviceType, int deviceId, void *ptr) { - std::lock_guard lock(gWorkspaceMutex); - auto p = mindspore::predict::WorkspacePool::GetInstance(); - if (p == nullptr) { - MS_LOGE("get ThreadPool install failed"); - return -1; - } - p->FreeWorkSpaceMem(ptr); - return 0; -} - -void ConfigThreadPool(int mode, int nthreads) { - auto p = mindspore::predict::ThreadPool::GetInstance(); - if (p == nullptr) { - MS_LOGE("get ThreadPool install failed"); - return; - } - p->ConfigThreadPool(mode, nthreads); -} - -int LiteBackendParallelLaunch(FTVMParallelLambda flambda, void *cdata, int num_task) { - auto p = mindspore::predict::ThreadPool::GetInstance(); - if (p == nullptr) { - MS_LOGE("get ThreadPool install failed"); - return -1; - } - if (!p->LaunchThreadPoolTask()) { - MS_LOGE("get ThreadPool or thread bind failed"); - return -1; - } - if (!p->AddTask(flambda, cdata, num_task)) { - MS_LOGE("AddTask failed"); - return -1; - } - return 0; -} - -#ifdef __cplusplus -} -#endif diff --git a/predict/src/runtime/runtime_api.h b/predict/src/runtime/runtime_api.h deleted file mode 100644 index 01aa782cf8..0000000000 --- a/predict/src/runtime/runtime_api.h +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef PREDICT_SRC_RUNTIME_RUNTIME_API_H_ -#define PREDICT_SRC_RUNTIME_RUNTIME_API_H_ -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - void *sync_handle; - int32_t num_task; -} TVMParallelGroupEnv; -typedef int (*FTVMParallelLambda)(int task_id, TVMParallelGroupEnv *penv, void *cdata); -void LiteAPISetLastError(const char *msg); -void *LiteBackendAllocWorkspace(int deviceType, int deviceId, uint64_t size, int dtypeCode, int dtypeBits); -int LiteBackendFreeWorkspace(int deviceType, int deviceId, void *ptr); -void ConfigThreadPool(int mode, int nthreads); -int LiteBackendParallelLaunch(FTVMParallelLambda flambda, void *cdata, int num_task); -int LiteBackendRegisterSystemLibSymbol(const char *name, void *ptr); - -#ifdef __cplusplus -} -#endif -#endif // PREDICT_SRC_RUNTIME_RUNTIME_API_H_ diff --git a/predict/src/runtime/thread_pool.cc b/predict/src/runtime/thread_pool.cc deleted file mode 100644 index 6018927a18..0000000000 --- a/predict/src/runtime/thread_pool.cc +++ /dev/null @@ -1,447 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "src/runtime/thread_pool.h" -#include -#include "common/mslog.h" - -namespace mindspore { -namespace predict { -static constexpr int kThreadPoolMaxThreads = 8; -static const int kCoreNumThr = 4; -static const int kMidCoreNum = 2; -static const int kBigCoreNum = 2; -bool LiteQueue::Enqueue(const ThreadPoolTask &task) { - const int tailIndex = tail.load(std::memory_order_relaxed); - // queue full - auto next = (tailIndex + 1) % kSingleThreadMaxTask; - if (next == head.load(std::memory_order_acquire)) { - return false; - } - buffer[tailIndex] = task; - tail.store(next, std::memory_order_release); - taskSize.fetch_add(1); - return true; -} - -bool LiteQueue::Dequeue(ThreadPoolTask *out) { - if (out == nullptr) { - MS_LOGE("ThreadPoolTask is nullptr"); - return false; - } - if (taskSize.load() == 0) { - return false; - } - // queue empty - const int headIndex = head.load(std::memory_order_relaxed); - if (headIndex == tail.load(std::memory_order_acquire)) { - return false; - } - *out = buffer[headIndex]; - head.store((headIndex + 1) % kSingleThreadMaxTask, std::memory_order_release); - return true; -} - -bool LiteThreadBind::Bind(int numThreads, int mode) { - InitSortedCpuId(); - if (numThreads > static_cast(sortedCpuIds.size())) { - MS_LOGE("thread num %d is larger than cores %lu in the system", numThreads, sortedCpuIds.size()); - return false; - } - threadNums = numThreads + 1; - bindModel = static_cast(mode); - if (bindModel == NO_BIND) { - if (!BindAllThread(false)) { - MS_LOGE("unbind %d threads failed", threadNums); - return false; - } - MS_LOGD("unbind %d threads successful", threadNums); - } else { - if (!BindAllThread(true)) { - MS_LOGE("bind %d threads failed", threadNums); - return false; - } - MS_LOGD("bind %d threads successful", threadNums); - } - return true; -} - -void LiteThreadBind::InitSortedCpuId() { - int numCores = static_cast(std::thread::hardware_concurrency()); - if (numCores < kCoreNumThr) { - bigCore = 0; - midCore = numCores; - } else { - bigCore = kBigCoreNum; - midCore = kMidCoreNum; - } - if (numCores > kCoreNumThr) { - numCores = bigCore + midCore; - } - sortedCpuIds.resize(numCores); - sortedCpuIds.clear(); - for (int i = numCores - 1; i >= 0; --i) { - sortedCpuIds.emplace_back(i); - } -} - -bool LiteThreadBind::BindAllThread(bool bindFlag) { - if (threadNums <= 0) { - MS_LOGE("no thread pool find, current threadNums %d", threadNums); - return false; - } - if (!BindThreads(bindFlag)) { - MS_LOGE("bind threads failed"); - return false; - } - return true; -} - -bool LiteThreadBind::BindMasterThread(bool bindFlag, int mode) { - std::vector cpu; - cpu.resize(sortedCpuIds.size()); - cpu.clear(); - if (bindFlag) { - int cpuIndex = (mode == MID_CORE) ? (threadNums - 1) : 0; - auto materCpuId = sortedCpuIds.at(cpuIndex); - cpu.emplace_back(materCpuId); - } else { - // unbind master - cpu.assign(sortedCpuIds.begin(), sortedCpuIds.end()); - } - cpu_set_t cpuSet; - CPU_ZERO(&cpuSet); - for (auto coreId : cpu) { - CPU_SET(coreId, &cpuSet); - } - if (!SetCPUBind(pthread_self(), cpuSet)) { - MS_LOGE("do master bind failed. mode: %d", mode); - return false; - } - return true; -} - -bool LiteThreadBind::BindThreads(bool bindFlag) { - if (bindFlag) { - if (bindModel != NO_BIND) { - size_t bindNums = std::min(sortedCpuIds.size(), threadIdList.size()); - size_t coreIndex; - cpu_set_t cpuSet; - for (size_t i = 0; i < bindNums; ++i) { - if (bindModel == MID_CORE) { - coreIndex = sortedCpuIds.size() - i - 1; - } else { - coreIndex = i; - } - CPU_ZERO(&cpuSet); - CPU_SET(sortedCpuIds[coreIndex], &cpuSet); - if (!threadIdList[i].second) { - MS_LOGD("threadIdList[%lu]=%lu, sortedCpuIds[%lu]=%d", i, threadIdList[i].first, coreIndex, - sortedCpuIds[coreIndex]); - if (!SetCPUBind(threadIdList[i].first, cpuSet)) { - MS_LOGE("do SetCPUBind failed"); - return false; - } - } - threadIdList[i].second = true; - } - } - } else { - // unbind - size_t bindNums = std::min(sortedCpuIds.size(), threadIdList.size()); - cpu_set_t cpuSet; - CPU_ZERO(&cpuSet); - for (auto coreId : sortedCpuIds) { - CPU_SET(coreId, &cpuSet); - } - for (size_t i = 0; i < bindNums; ++i) { - if (!SetCPUBind(threadIdList[i].first, cpuSet)) { - MS_LOGE("do SetCPUBind failed"); - return false; - } - threadIdList[i].second = false; - } - } - return true; -} - -bool LiteThreadBind::SetCPUBind(pthread_t threadId, const cpu_set_t &cpuSet) { -#if defined(__ANDROID__) -#if __ANDROID_API__ >= 21 - int ret = sched_setaffinity(pthread_gettid_np(threadId), sizeof(cpu_set_t), &cpuSet); - if (ret != 0) { - MS_LOGE("bind thread %ld to cpu failed.ERROR %d", threadId, ret); - } -#endif -#else - int ret = pthread_setaffinity_np(threadId, sizeof(cpu_set_t), &cpuSet); - if (ret != 0) { - MS_LOGE("bind thread %ld to cpu failed.ERROR %d", threadId, ret); - return false; - } -#endif - return true; -} - -LiteThreadPool::LiteThreadPool(int numThreads) { - queueList.resize(kThreadPoolMaxThreads); - queueList.clear(); - AddNewThread(numThreads); -} - -void LiteThreadPool::AddNewThread(int newNums) { - for (int i = curThreadNums, j = 0; j < newNums; ++j, ++i) { - queueList.push_back(std::unique_ptr(new LiteQueue())); - threadList.emplace_back([this, i]() { - ThreadPoolTask task; - while (!destroy) { - while (running != 0) { - MS_LOGD("i = %d, thread id = %lu, taskSize = %d", i, pthread_self(), queueList[i]->taskSize.load()); - while (queueList[i]->taskSize.load() > 0 && queueList[i]->Dequeue(&task)) { - auto ret = task.first(task.second.taskId, task.second.tvmParam, task.second.cdata); - if (ret != 0) { - errorInfo.emplace_back(std::make_pair(task.second.taskId, std::make_pair(false, ret))); - } - queueList[i]->taskSize.fetch_sub(1); - } - std::this_thread::yield(); - } - std::unique_lock queueLock(tMutex); - queueReady.wait(queueLock, [this] { return destroy || running != 0; }); - } - }); - } - MS_LOGI("%d new thread create", newNums); - curThreadNums += newNums; -} - -bool LiteThreadPool::DistributeTask(ThreadPoolTask task, int numTask) { - // wake up - errorInfo.clear(); - if (!AddRunReference()) { - MS_LOGE("add reference failed"); - return false; - } - bool kSuccFlag; - for (int i = 1; i < numTask; ++i) { - task.second.taskId = i; - do { - kSuccFlag = false; - for (auto &queue : queueList) { - MS_ASSERT(queue != nullptr); - if (queue->Enqueue(task)) { - kSuccFlag = true; - break; - } - } - std::this_thread::yield(); - } while (!kSuccFlag); - } - MS_LOGI("add %d task successful", numTask); - // master thread - int ret = task.first(0, task.second.tvmParam, task.second.cdata); - if (ret != 0) { - errorInfo.emplace_back(std::make_pair(0, std::make_pair(false, ret))); - } - kSuccFlag = false; - while (!kSuccFlag) { - kSuccFlag = true; - for (auto iter = queueList.begin(); iter != queueList.end(); ++iter) { - if ((*iter)->taskSize.load() != 0) { - kSuccFlag = false; - break; - } - } - std::this_thread::yield(); - } - // hibernate - if (!SubRunReference()) { - MS_LOGE("sub reference failed"); - return false; - } - MS_LOGI("finish %d task successful", numTask); - return CheckResult(); -} - -bool LiteThreadPool::AddRunReference() { - running.fetch_add(1); - std::lock_guard queueLock(tMutex); - queueReady.notify_all(); - return true; -} - -bool LiteThreadPool::SubRunReference() { - running.fetch_sub(1); - return true; -} - -bool LiteThreadPool::CheckResult() { - bool kSuccFlag = true; - for (auto result : errorInfo) { - if (result.second.first) { - MS_LOGE("task %d failed, error code is %d", result.first, result.second.second); - kSuccFlag = false; - } - } - return kSuccFlag; -} - -int ThreadPool::GetThreadNum(int numThreads) { - if (numThreads <= 0 || numThreads > kThreadPoolMaxThreads) { - MS_LOGE("numThreads %d, must be greater than 0 or less than or equal to %d", numThreads, kThreadPoolMaxThreads); - return -1; - } else { - if (numThreads > totalThreadNum) { - return (numThreads - totalThreadNum); - } else { - MS_LOGD("%d threads have been already created", numThreads); - return 0; - } - } -} - -void ThreadPool::GetThreadIdList() { - if (gThreadPool != nullptr) { - for (int i = 0; i < totalThreadNum; ++i) { - bool kSuccFlag = false; - pthread_t threadHandle; - do { - kSuccFlag = false; - threadHandle = gThreadPool->threadList[i].native_handle(); - if (threadHandle != 0) { - kSuccFlag = true; - } - std::this_thread::yield(); - } while (!kSuccFlag); - - auto iter = std::find_if(std::begin(gThreadBind->threadIdList), std::end(gThreadBind->threadIdList), - [threadHandle](std::pair id) { return id.first == threadHandle; }); - if (iter == std::end(gThreadBind->threadIdList)) { - gThreadBind->threadIdList.emplace_back(std::make_pair(threadHandle, false)); - } - } - } - MS_ASSERT(gThreadBind != nullptr); - gThreadBind->threadIdList.emplace_back(std::make_pair(pthread_self(), false)); -} - -bool ThreadPool::SetThreadCpulBind(int mode) { - if (totalThreadNum <= 0) { - MS_LOGE("no threads need to be bind, totalThreadNum : %d", totalThreadNum); - return false; - } - std::lock_guard bMutex(gPoolMutex); - if (gThreadBind == nullptr) { - gThreadBind = std::unique_ptr(new (std::nothrow) LiteThreadBind()); - if (gThreadBind == nullptr) { - MS_LOGE("new LiteThreadBind failed"); - return false; - } - gThreadBind->threadIdList.resize(kThreadPoolMaxThreads + 1); - gThreadBind->threadIdList.clear(); - } - GetThreadIdList(); - - if (!gThreadBind->Bind(totalThreadNum, mode)) { - MS_LOGE("BindCore failed"); - return false; - } - return true; -} - -bool ThreadPool::SetThreadPool(int numThreads) { - std::lock_guard Lock(gPoolMutex); - int realNums = GetThreadNum(numThreads); - if (realNums < -1) { - return false; - } - if (realNums == 0) { - return true; - } - if (gThreadPool == nullptr) { - gThreadPool = std::unique_ptr(new (std::nothrow) LiteThreadPool(realNums)); - if (gThreadPool == nullptr) { - MS_LOGE("%d threads create failed", realNums); - return false; - } - } else { - gThreadPool->AddNewThread(realNums); - } - MS_LOGD("%d threads create successful", realNums); - return true; -} - -ThreadPool *ThreadPool::GetInstance() { - static ThreadPool instance; - return &instance; -} - -void ThreadPool::ConfigThreadPool(int mode, int numThreads) { - bindMode = mode; - totalThreadNum = numThreads; -} - -bool ThreadPool::LaunchThreadPoolTask() { - if (gThreadPool == nullptr) { - if (!SetThreadPool(totalThreadNum)) { - MS_LOGE("create %d threads failed", totalThreadNum); - return false; - } - } - - if (gThreadBind == nullptr) { - if (!SetThreadCpulBind(bindMode)) { - MS_LOGE("create bind mode %d failed", bindMode); - return false; - } - } - return true; -} - -bool ThreadPool::AddTask(const WorkFun &worker, void *cdata, int numTask) { - if (numTask <= 0) { - numTask = totalThreadNum; - } - // single task, run master thread - if (numTask <= 1) { - TvmEnv env{}; - env.num_task = numTask; - int ret = worker(0, &env, cdata); - if (ret != 0) { - MS_LOGE("task 0 failed, error code is %d", ret); - return false; - } - MS_LOGD("task 0 successful"); - return true; - } - ThreadPoolTask task; - task.first = worker; - task.second.cdata = cdata; - return gThreadPool->DistributeTask(task, numTask); -} - -LiteThreadPool::~LiteThreadPool() { - destroy.store(true); - running.store(0); - queueReady.notify_all(); - for (auto &thread : threadList) { - if (thread.joinable()) { - thread.join(); - } - } -} -} // namespace predict -} // namespace mindspore diff --git a/predict/src/runtime/thread_pool.h b/predict/src/runtime/thread_pool.h deleted file mode 100644 index 53e4c1ec88..0000000000 --- a/predict/src/runtime/thread_pool.h +++ /dev/null @@ -1,129 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PREDICT_SRC_RUNTIME_THREAD_POOL_H_ -#define PREDICT_SRC_RUNTIME_THREAD_POOL_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "src/runtime/runtime_api.h" - -namespace mindspore { -namespace predict { -constexpr int kSingleThreadMaxTask = 4; -using TvmEnv = TVMParallelGroupEnv; -using WorkFun = FTVMParallelLambda; -using TaskParam = struct Param { - void *cdata; - int32_t taskId; - TvmEnv *tvmParam; -}; -using ThreadPoolTask = std::pair; - -class LiteQueue { - public: - LiteQueue() = default; - ~LiteQueue() = default; - - bool Enqueue(const ThreadPoolTask &task); - bool Dequeue(ThreadPoolTask *out); - std::atomic taskSize{0}; - - private: - std::atomic head{0}; - std::atomic tail{0}; - ThreadPoolTask buffer[kSingleThreadMaxTask]{}; -}; - -class LiteThreadBind { - public: - LiteThreadBind() = default; - ~LiteThreadBind() = default; - bool Bind(int numThreads, int mode); - std::vector> threadIdList; - - private: - enum AffinityMode : int { BIG_CORE = 1, MID_CORE = -1, NO_BIND = 0 }; - void InitSortedCpuId(); - bool BindAllThread(bool bindFlag); - bool BindMasterThread(bool bindFlag, int mode = MID_CORE); - bool BindThreads(bool bindFlag); - bool SetCPUBind(pthread_t threadId, const cpu_set_t &cpuSet); - int bigCore{0}; - int midCore{0}; - int threadNums{0}; - std::vector sortedCpuIds{}; - AffinityMode bindModel{MID_CORE}; -}; - -class LiteThreadPool { - public: - LiteThreadPool() = default; - explicit LiteThreadPool(int numThreads); - ~LiteThreadPool(); - - void AddNewThread(int newNums); - bool DistributeTask(ThreadPoolTask task, int numTask); - std::vector threadList{}; - - private: - using errCode = std::pair; - bool AddRunReference(); - bool SubRunReference(); - bool CheckResult(); - int curThreadNums{0}; - std::vector> queueList; - std::atomic_int running{0}; - std::mutex tMutex; - std::condition_variable queueReady; - std::atomic destroy = {false}; - std::vector> errorInfo{}; -}; - -class ThreadPool { - public: - static ThreadPool *GetInstance(); - void ConfigThreadPool(int mode, int numThreads); - bool LaunchThreadPoolTask(); - bool AddTask(const WorkFun &worker, void *cdata, int numTask); - - ThreadPool(const ThreadPool &) = delete; - ThreadPool &operator=(const ThreadPool &) = delete; - - private: - ThreadPool() = default; - ~ThreadPool() = default; - int GetThreadNum(int numThreads); - void GetThreadIdList(); - bool SetThreadPool(int numThreads = 1); - bool SetThreadCpulBind(int mode); - std::unique_ptr gThreadPool{nullptr}; - std::unique_ptr gThreadBind{nullptr}; - std::mutex gPoolMutex; - int totalThreadNum{1}; - int bindMode{-1}; -}; -} // namespace predict -} // namespace mindspore - -#endif // PREDICT_SRC_RUNTIME_THREAD_POOL_H_ diff --git a/predict/src/runtime/workspace_pool.cc b/predict/src/runtime/workspace_pool.cc deleted file mode 100644 index 6cafe7482e..0000000000 --- a/predict/src/runtime/workspace_pool.cc +++ /dev/null @@ -1,113 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "src/runtime/workspace_pool.h" -#include -#include -#include "common/mslog.h" - -namespace mindspore { -namespace predict { -static constexpr size_t kWorkspacePageSize = 4096; -static constexpr int kTempAllocaAlignment = 64; -WorkspacePool *WorkspacePool::GetInstance() { - static WorkspacePool instance; - return &instance; -} - -void *WorkspacePool::AllocWorkSpaceMem(size_t size) { - size_t nbytes = (size + (kWorkspacePageSize - 1)) / kWorkspacePageSize * kWorkspacePageSize; - if (nbytes == 0) { - nbytes = kWorkspacePageSize; - } - std::pair alloc; - // fist alloc - if (freeList.empty()) { - alloc.first = nbytes; - alloc.second = memalign(kTempAllocaAlignment, nbytes); - } else if (freeList.size() == 1) { // one element - alloc = *(freeList.begin()); - freeList.erase(freeList.begin()); - if (alloc.first < nbytes) { - free(alloc.second); - alloc.first = nbytes; - alloc.second = memalign(kTempAllocaAlignment, nbytes); - } - } else { - if ((*(freeList.begin())).first >= nbytes) { - auto iter = freeList.begin(); - for (; iter != freeList.end(); ++iter) { - if ((*iter).first < size) { - alloc = *(--iter); - freeList.erase(iter); - break; - } - } - if (iter == freeList.end()) { - alloc = *(freeList.rbegin()); - freeList.erase(--freeList.end()); - } - } else { - alloc = *(freeList.begin()); - freeList.erase(freeList.begin()); - free(alloc.second); - alloc.first = nbytes; - alloc.second = memalign(kTempAllocaAlignment, nbytes); - } - } - allocList.emplace_back(alloc); - return alloc.second; -} - -void WorkspacePool::FreeWorkSpaceMem(void *ptr) { - if (ptr == nullptr) { - return; - } - std::pair alloc; - if (allocList.empty()) { - MS_LOGE("no mem have been alloc"); - return; - } else if (allocList.back().second == ptr) { - alloc = allocList.back(); - allocList.pop_back(); - } else { - auto iter = allocList.begin(); - for (; iter != allocList.end(); ++iter) { - if ((*iter).second == ptr) { - alloc = *iter; - allocList.erase(iter); - break; - } - } - if (iter == allocList.end()) { - MS_LOGE("no value ptr have been alloc"); - return; - } - } - freeList.insert(alloc); -} - -WorkspacePool::~WorkspacePool() { - for (auto &a : allocList) { - free(a.second); - } - allocList.clear(); - for (auto &f : freeList) { - free(f.second); - } - freeList.clear(); -} -} // namespace predict -} // namespace mindspore diff --git a/predict/src/runtime/workspace_pool.h b/predict/src/runtime/workspace_pool.h deleted file mode 100644 index ce8a5ca3ab..0000000000 --- a/predict/src/runtime/workspace_pool.h +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef PREDICT_SRC_RUNTIME_WORKSPACE_POOL_H_ -#define PREDICT_SRC_RUNTIME_WORKSPACE_POOL_H_ -#include -#include -#include -#include -#include -#include - -namespace mindspore { -namespace predict { -class WorkspacePool { - public: - WorkspacePool() = default; - ~WorkspacePool(); - WorkspacePool(const WorkspacePool &) = delete; - WorkspacePool &operator=(const WorkspacePool &) = delete; - static WorkspacePool *GetInstance(); - void *AllocWorkSpaceMem(size_t size); - void FreeWorkSpaceMem(void *ptr); - - private: - std::vector> allocList{}; - std::set, std::greater>> freeList{}; -}; -} // namespace predict -} // namespace mindspore -#endif // PREDICT_SRC_RUNTIME_WORKSPACE_POOL_H_ diff --git a/predict/src/session.cc b/predict/src/session.cc deleted file mode 100644 index b808ec7c6b..0000000000 --- a/predict/src/session.cc +++ /dev/null @@ -1,154 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "include/session.h" -#include -#include -#include "include/errorcode.h" -#include "common/mslog.h" -#include "src/graph.h" -#include "src/graph_execution.h" - -namespace mindspore { -namespace predict { -Context m_ctx; -bool m_isConfig = false; - -// In 32bits, this evaluates to 2GB - 1 -static constexpr auto MAX_BUFFER_SIZE = ((1ULL << (sizeof(int32_t) * 8 - 1)) - 1); - -std::shared_ptr CreateSession(const char *graphBuf, size_t size, const Context &ctx) { - if (graphBuf == nullptr) { - MS_LOGE("the graphBuf is nullptr"); - return nullptr; - } - if (size > MAX_BUFFER_SIZE) { - MS_LOGE("the size is invalid"); - return nullptr; - } - auto session = std::make_shared(ctx); - MS_ASSERT(session != nullptr); - auto ret = session->Init(graphBuf, size); - if (ret != RET_OK) { - MS_LOGE("Init session failed."); - return nullptr; - } - return session; -} -Session::Session(const Context &ctx) : _ctx(ctx) { - Context cfgCtx; - cfgCtx = ctx; - if (cfgCtx.threadNum > m_ctx.threadNum) { - cfgCtx.threadNum = m_ctx.threadNum; - } -} - -int Session::Init(const char *graphBuf, size_t size) { - _graph = Graph::CreateFromBuf(graphBuf, size, _ctx); - if (_graph == nullptr) { - MS_LOGE("Graph create from buf failed."); - return RET_NULL_PTR; - } - - auto ret = this->InitExecutor(); - if (ret != RET_OK) { - MS_LOGE("Init Executor failed"); - return ret; - } - return ret; -} - -int Session::InitExecutor() { - if (_executor != nullptr) { - delete _executor; - _executor = nullptr; - } - if (_graph != nullptr) { - _executor = new (std::nothrow) GraphExecution(_ctx, _graph); - if (_executor == nullptr) { - MS_LOGE("new GraphExecution fail"); - return RET_ERROR; - } - return RET_OK; - } else { - MS_LOGE("the graph is nullptr"); - return RET_ERROR; - } -} - -Session::~Session() { - if (_executor != nullptr) { - delete _executor; - } - if (_graph != nullptr) { - delete _graph; - } -} - -int Session::Run(const std::vector &inputs) { - auto ret = RET_OK; - if (reinitExecutor) { - ret = this->InitExecutor(); - if (ret != RET_OK) { - MS_LOGE("Init Executor failed"); - return ret; - } - } - if (_executor == nullptr) { - MS_LOGE("_executor is nullptr"); - return ret; - } - ret = _executor->Run(inputs); - return ret; -} - -std::vector Session::GetInput() { - if (_executor == nullptr) { - MS_LOGE("_executor is nullptr"); - return std::vector{}; - } - auto inputs = _executor->GetInput(); - if (inputs.empty()) { - MS_LOGI("output is empty."); - } - return inputs; -} - -std::vector Session::GetOutput(const std::string &nodeName) { - if (_executor == nullptr) { - MS_LOGE("graph's executor is nullptr."); - return std::vector{}; - } - auto outputs = _executor->GetOutput(nodeName); - if (outputs.empty()) { - MS_LOGI("output is empty."); - } - return outputs; -} - -std::map> Session::GetAllOutput() { - if (_executor == nullptr) { - MS_LOGE("graph's executor is nullptr."); - return std::map>{}; - } - auto outputs = _executor->GetAllOutput(); - if (outputs.empty()) { - MS_LOGI("outputs is empty."); - } - return outputs; -} -} // namespace predict -} // namespace mindspore diff --git a/predict/src/tensor.cc b/predict/src/tensor.cc deleted file mode 100644 index de758f3407..0000000000 --- a/predict/src/tensor.cc +++ /dev/null @@ -1,517 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "include/tensor.h" -#include "common/mslog.h" -#include "src/op_common.h" -#include "include/errorcode.h" -#include "securec/include/securec.h" -#include "common/common.h" -#include "src/runtime/allocator.h" - -namespace mindspore { -namespace predict { -Tensor *Tensor::CopyFromTensorDef(const TensorDef &tensorDef) { - std::vector dims; - - if (tensorDef.dims() == nullptr) { - MS_LOGD("tensorDef->dims is nullptr"); - } else { - MS_ASSERT(tensorDef.dims()->data() != nullptr); - for (uint32_t j = 0; j < tensorDef.dims()->size(); j++) { - dims.push_back(tensorDef.dims()->data()[j]); - } - } - auto tensor = - std::unique_ptr(new (std::nothrow) Tensor(tensorDef.dataType(), dims, tensorDef.format(), nullptr)); - if (tensor == nullptr) { - MS_LOGE("new Tensor failed"); - return nullptr; - } - - if (tensorDef.refCount() == MSConst_WEIGHT_REFCOUNT && tensorDef.data() != nullptr && tensorDef.data()->size() > 0) { - if (dims.size() < 1) { - tensor->SetDims({1}); - } - auto ret = tensor->MallocData(); - if (ret != RET_OK) { - MS_LOGE("malloc data fail,datasize %zu", tensor->GetDataSize()); - return nullptr; - } - auto tensorData = tensorDef.data()->data(); - ret = memcpy_sp(tensor->GetData(), tensor->GetDataSize(), tensorData, tensorDef.data()->size()); - if (ret != RET_OK) { - MS_LOGE("copy data fail,dst size %zu, src size %u", tensor->GetDataSize(), tensorDef.data()->size()); - return nullptr; - } - } - tensor->refCount = tensorDef.refCount(); - return tensor.release(); -} - -Tensor::Tensor(const Tensor &tensor, bool copyData) { - format = tensor.format; - dlTensor.data = nullptr; - dlTensor.ctx.device_type = tensor.dlTensor.ctx.device_type; - dlTensor.ctx.device_id = tensor.dlTensor.ctx.device_id; - dlTensor.strides = nullptr; - dlTensor.byte_offset = tensor.dlTensor.byte_offset; - dlTensor.dtype.code = tensor.dlTensor.dtype.code; - dlTensor.dtype.bits = tensor.dlTensor.dtype.bits; - dlTensor.dtype.lanes = tensor.dlTensor.dtype.lanes; - - dlTensor.ndim = tensor.dlTensor.ndim; - if (dlTensor.ndim > 0) { - dlTensor.shape = new (std::nothrow) int64_t[dlTensor.ndim]; - if (dlTensor.shape != nullptr) { - for (int i = 0; i < dlTensor.ndim; i++) { - dlTensor.shape[i] = tensor.dlTensor.shape[i]; - } - } else { - MS_LOGW("new shape fail,ndim %d", dlTensor.ndim); - } - } else { - dlTensor.shape = nullptr; - } - if (copyData) { - allocator = tensor.allocator; - refCount = tensor.refCount; - auto ret = MallocData(); - if (ret != RET_OK) { - return; - } - size_t datasize = GetDataSize(); - ret = memcpy_sp(dlTensor.data, datasize, tensor.dlTensor.data, datasize); - if (ret != RET_OK) { - return; - } - } -} - -Tensor::Tensor(DataType dt, const std::vector &dims, Format format, void *data) { - this->format = format; - dlTensor.data = data; - dlTensor.ctx.device_type = DLDeviceType::kDLCPU; - dlTensor.ctx.device_id = 0; - dlTensor.strides = nullptr; - dlTensor.byte_offset = 0; - - dlTensor.ndim = static_cast(dims.size()); - if (dlTensor.ndim > 0) { - dlTensor.shape = new (std::nothrow) int64_t[dlTensor.ndim]; - if (dlTensor.shape != nullptr) { - for (int i = 0; i < dlTensor.ndim; i++) { - dlTensor.shape[i] = dims[i]; - } - } else { - MS_LOGW("new shape fail,ndim %d", dlTensor.ndim); - } - } else { - dlTensor.shape = nullptr; - } - - SetDataType(dt); -} - -Tensor::~Tensor() { FreeTensor(); } - -DLDataType Tensor::GetTensorDtype() const { return dlTensor.dtype; } - -void *Tensor::GetData() const { return dlTensor.data; } - -void Tensor::SetData(void *data) { dlTensor.data = data; } - -DataType Tensor::GetDataType() const { - DataType dataType = DataType_DT_UNDEFINED; - switch (dlTensor.dtype.code) { - case kDLFloat: - if (dlTensor.dtype.bits == 32) { - dataType = DataType_DT_FLOAT; - } else if (dlTensor.dtype.bits == 16) { - dataType = DataType_DT_FLOAT16; - } - break; - case kDLInt: - if (dlTensor.dtype.bits == 32) { - dataType = DataType_DT_INT32; - } else if (dlTensor.dtype.bits == 8) { - dataType = DataType_DT_INT8; - } - break; - case kDLUInt: - if (dlTensor.dtype.bits == 32) { - dataType = DataType_DT_UINT32; - } else if (dlTensor.dtype.bits == 8) { - dataType = DataType_DT_UINT8; - } - break; - default: - break; - } - return dataType; -} - -void Tensor::SetDataType(DataType dt) { - switch (dt) { - case DataType_DT_FLOAT: - dlTensor.dtype.code = kDLFloat; - dlTensor.dtype.bits = 32; - dlTensor.dtype.lanes = 1; - break; - case DataType_DT_FLOAT16: - dlTensor.dtype.code = kDLFloat; - dlTensor.dtype.bits = 16; - dlTensor.dtype.lanes = 1; - break; - case DataType_DT_INT8: - dlTensor.dtype.code = kDLInt; - dlTensor.dtype.bits = 8; - dlTensor.dtype.lanes = 1; - break; - case DataType_DT_UINT8: - dlTensor.dtype.code = kDLUInt; - dlTensor.dtype.bits = 8; - dlTensor.dtype.lanes = 1; - break; - case DataType_DT_INT32: - dlTensor.dtype.code = kDLInt; - dlTensor.dtype.bits = 32; - dlTensor.dtype.lanes = 1; - break; - case DataType_DT_UINT32: - dlTensor.dtype.code = kDLUInt; - dlTensor.dtype.bits = 32; - dlTensor.dtype.lanes = 1; - break; - default: - MS_LOGW(" DataType %d is not implemented.", dt); - MS_LOGW(" DataType DT_FLOAT is used."); - dlTensor.dtype.code = kDLFloat; - dlTensor.dtype.bits = 32; - dlTensor.dtype.lanes = 1; - return; - } -} - -int Tensor::GetNDim() const { return dlTensor.ndim; } - -std::vector Tensor::GetDims() const { - std::vector dims; - for (int i = 0; i < dlTensor.ndim; i++) { - dims.push_back(dlTensor.shape[i]); - } - return dims; -} - -size_t Tensor::GetElementSize() const { - const int tile = 4; - if (format == Format_NC4HW4) { - size_t size = 1; - for (int i = 0; i < dlTensor.ndim; i++) { - auto var = static_cast(dlTensor.shape[i]); - if (i == 1) { - var = UP_DIV(var, tile) * tile; - } - size *= var; - } - return size; - } else { - size_t size = 1; - for (int i = 0; i < dlTensor.ndim; i++) { - size *= static_cast(dlTensor.shape[i]); - } - - return size; - } -} - -size_t Tensor::GetDataSize() const { - size_t size = GetElementSize(); - - const int BYTES = 8; - const int GAP = 7; - size *= (dlTensor.dtype.bits * dlTensor.dtype.lanes + GAP) / BYTES; - return size; -} - -int Tensor::MallocData(std::shared_ptr allocator, int refCount) { - if (dlTensor.data != nullptr) { - this->refCount += refCount; - return RET_OK; - } - this->refCount = refCount; - - size_t size = GetDataSize(); - if (allocator) { - this->allocator = allocator; - dlTensor.data = allocator->Malloc(size); - } else { - if (size > MAX_MALLOC_SIZE) { - return RET_ERROR; - } - dlTensor.data = malloc(size); - } - if (dlTensor.data == nullptr) { - return RET_ERROR; - } - return RET_OK; -} - -void Tensor::ForceFreeData() { - if (allocator) { - allocator->Free(dlTensor.data); - } else { - free(dlTensor.data); - } - dlTensor.data = nullptr; -} - -void Tensor::FreeData() { - --refCount; - if (refCount <= 0) { - ForceFreeData(); - } -} - -bool Tensor::CompareShape(const Tensor &dst) { - if (dlTensor.ndim != dst.dlTensor.ndim || dlTensor.shape == nullptr || dst.dlTensor.shape == nullptr) { - MS_LOGE("param error, one.ndim: %d, other.ndim: %d, one shape %p,other shape %p", dlTensor.ndim, dst.dlTensor.ndim, - dlTensor.shape, dst.dlTensor.shape); - return false; - } - - for (int i = 0; i < dlTensor.ndim; i++) { - if (dlTensor.shape[i] != dst.dlTensor.shape[i]) { - MS_LOGE("one.shape[%d]: %ld, other.shape[%d]: %ld", i, dlTensor.shape[i], i, dst.dlTensor.shape[i]); - return false; - } - } - return true; -} - -bool Tensor::CompareShape(const std::vector &other) { - if (dlTensor.ndim != other.size() || dlTensor.shape == nullptr) { - return false; - } - - for (int i = 0; i < dlTensor.ndim; i++) { - if (dlTensor.shape[i] != other[i]) { - return false; - } - } - return true; -} - -int64_t Tensor::Height() const { - if (dlTensor.shape == nullptr) { - MS_LOGE("shape is null"); - } - if (dlTensor.ndim != DIM_DEFAULT_SIZE) { - MS_LOGE("Tensor should be 4 dimensional."); - return -1; - } - switch (this->format) { - case Format_NCHW: - case Format_NC4HW4: - return dlTensor.shape[NCHW_H]; - case Format_NHWC: - return dlTensor.shape[NHWC_H]; - default: - MS_LOGE("Unsupported format: %d", this->format); - return -1; - } -} - -int64_t Tensor::Width() const { - if (dlTensor.shape == nullptr) { - MS_LOGE("shape is null"); - } - if (dlTensor.ndim != DIM_DEFAULT_SIZE) { - MS_LOGE("Tensor should be 4 dimensional."); - return -1; - } - switch (this->format) { - case Format_NCHW: - case Format_NC4HW4: - return dlTensor.shape[NCHW_W]; - case Format_NHWC: - return dlTensor.shape[NHWC_W]; - default: - MS_LOGE("Unsupported format: %d", this->format); - return -1; - } -} - -int64_t Tensor::Channel() const { - if (dlTensor.shape == nullptr) { - MS_LOGE("shape is null"); - } - if (dlTensor.ndim != DIM_DEFAULT_SIZE) { - MS_LOGE("Tensor should be 4 dimensional."); - return -1; - } - switch (this->format) { - case Format_NCHW: - case Format_NC4HW4: - return dlTensor.shape[NCHW_C]; - case Format_NHWC: - return dlTensor.shape[NHWC_C]; - default: - MS_LOGE("Unsupported format: %d", this->format); - return -1; - } -} - -int64_t Tensor::Batch() const { - if (dlTensor.shape == nullptr) { - MS_LOGE("shape is null"); - } - if (dlTensor.ndim != DIM_DEFAULT_SIZE) { - MS_LOGE("Tensor should be 4 dimensional."); - return -1; - } - switch (this->format) { - case Format_NCHW: - case Format_NC4HW4: - case Format_NHWC: - return dlTensor.shape[NCHW_N]; - default: - MS_LOGE("Unsupported format: %d", this->format); - return -1; - } -} - -int64_t Tensor::Stride(int index) const { - if (dlTensor.strides) { - return dlTensor.strides[index]; - } - if (dlTensor.shape == nullptr) { - MS_LOGE("shape is null"); - return -1; - } - int64_t stride = 1; - for (int i = index + 1; i < dlTensor.ndim; i++) { - stride *= dlTensor.shape[i]; - } - return stride; -} - -void Tensor::SetStride() { - if (dlTensor.strides == nullptr) { - if (dlTensor.ndim < 1) { - MS_LOGE("dims of dlTensor is empty."); - return; - } - dlTensor.strides = new (std::nothrow) int64_t[dlTensor.ndim - 1]; - if (dlTensor.strides == nullptr) { - MS_LOGW("new stride fail, ndim %d.", dlTensor.ndim); - return; - } - } - - for (int idx = 0; idx < dlTensor.ndim - 1; idx++) { - int64_t stride = 1; - if (dlTensor.ndim <= idx + 1) { - MS_LOGE("out of for loop upper limit."); - return; - } - for (int i = idx + 1; i < dlTensor.ndim; i++) { - stride *= dlTensor.shape[i]; - } - dlTensor.strides[idx] = stride; - } -} -void Tensor::SetScale(bool isScale) { this->isScale = isScale; } - -void Tensor::SetStride(int index, int64_t stride) { - if (index >= dlTensor.ndim) { - return; - } - - if (dlTensor.strides == nullptr) { - SetStride(); - } - - dlTensor.strides[index] = stride; - return; -} - -void Tensor::SetDims(const std::vector &dims) { - if (dlTensor.shape != nullptr) { - delete[] dlTensor.shape; - } - dlTensor.ndim = static_cast(dims.size()); - if (dlTensor.ndim > 0) { - dlTensor.shape = new (std::nothrow) int64_t[dlTensor.ndim]; - if (dlTensor.shape != nullptr) { - for (int i = 0; i < dlTensor.ndim; i++) { - dlTensor.shape[i] = dims[i]; - } - } else { - MS_LOGW("new shape fail,ndim %d", dlTensor.ndim); - } - } else { - dlTensor.shape = nullptr; - } -} - -void Tensor::FreeTensor() { - if (dlTensor.shape != nullptr) { - delete[] dlTensor.shape; - dlTensor.shape = nullptr; - } - - if (dlTensor.strides != nullptr) { - delete[] dlTensor.strides; - dlTensor.strides = nullptr; - } - - dlTensor.ndim = 0; - - if (allocator != nullptr) { - allocator->Free(dlTensor.data); - } else { - free(dlTensor.data); - } - dlTensor.data = nullptr; -} - -size_t Tensor::GetNC4HW4ElementSize(bool isNhwc) { - int alignIndex = 1; - if (isNhwc) { - alignIndex = 3; - } - - size_t size = 1; - for (int i = 0; i < dlTensor.ndim; i++) { - auto var = static_cast(dlTensor.shape[i]); - if (i == alignIndex) { - var = ALIGN_UP4(var); - } - size *= var; - } - return size; -} - -size_t Tensor::GetNC4HW4DataSize(bool isNhwc) { - size_t size = GetNC4HW4ElementSize(isNhwc); - const int BYTES = 8; - const int GAP = 7; - size *= (dlTensor.dtype.bits * dlTensor.dtype.lanes + GAP) / BYTES; - return size; -} -} // namespace predict -} // namespace mindspore diff --git a/predict/test/CMakeLists.txt b/predict/test/CMakeLists.txt deleted file mode 100755 index 9370ff7ce0..0000000000 --- a/predict/test/CMakeLists.txt +++ /dev/null @@ -1,49 +0,0 @@ -cmake_minimum_required(VERSION 3.12) -project(ms-test) - -set(CMAKE_CXX_STANDARD 11) - -#include 3rd -include_directories(${3RD_DIR}/securec/include) -include_directories(${3RD_DIR}/flatbuffers/include) -include_directories(${3RD_DIR}/googletest/googletest/include) -include_directories(${3RD_DIR}/googletest/googlemock/include) -include_directories(${3RD_DIR}/securec/include) - -#include ms -include_directories(.) -include_directories(..) - -link_directories(${CMAKE_CURRENT_SOURCE_DIR}/../output/lib/) - -set(COMMON_SRC ${PREDICT_DIR}/common/flag_parser.cc - ${PREDICT_DIR}/common/file_utils.cc - ${PREDICT_DIR}/common/mslog.cc - ${PREDICT_DIR}/common/storage.cc - ${PREDICT_DIR}/common/utils.cc) - -#tools src -file(GLOB_RECURSE TOOLS_SRC ../tools/*.cpp) - -add_executable(ms-test - ${COMMON_SRC} - ${TOOLS_SRC} - src/graph_tests.cc - benchmark/benchmark_tests.cc - ${CMAKE_SOURCE_DIR}/benchmark/benchmark.cc - ${TF_PROTO_SRC} - ${MS_CONVERTER_SRC} - test_context.h - test_context.cc - main.cc) - -target_link_libraries(ms-test mspredict gtest libsecurec.a) -add_dependencies(ms-test securec) -add_dependencies(ms-test gtest) - -# copy test file -add_custom_command(TARGET ms-test POST_BUILD - COMMAND mkdir -pv ${DOTEST_DIR} - COMMAND cp ${PREDICT_BUILD_DIR}/test/ms-test ${DOTEST_DIR} - COMMAND cp ${PREDICT_DIR}/test/run_tests.sh ${PREDICT_BUILD_DIR}/test/ - COMMAND cp -r ${PREDICT_DIR}/test/data/ ${PREDICT_BUILD_DIR}/test/doTest/) diff --git a/predict/test/benchmark/benchmark_tests.cc b/predict/test/benchmark/benchmark_tests.cc deleted file mode 100644 index e1e218e851..0000000000 --- a/predict/test/benchmark/benchmark_tests.cc +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright 2019 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include -#include -#include "test/test_context.h" -#include "benchmark/benchmark.h" - -#define LENET_ARGS 2 -#define MS_ARGS 4 - -namespace mindspore { -namespace predict { -class BenchmarkTest : public ::testing::Test { - protected: - void SetUp() {} - - void TearDown() {} - std::string root; -}; - -TEST_F(BenchmarkTest, BenchmarkRun) { - const char* args[LENET_ARGS]; - args[0] = "./benchmark"; - args[1] = "--modelPath=./data/lenet/lenet.ms"; - - int errorcode = mindspore::predict::RunBenchmark(LENET_ARGS, args); - EXPECT_EQ(0, errorcode); -} - -TEST_F(BenchmarkTest, LenetRun) { - const char* args[MS_ARGS]; - args[0] = "./benchmark"; - args[1] = "--modelPath=./data/ms/mindspore.ms"; - args[2] = "--inDataPath=./data/ms/mindspore.bin"; - args[3] = "--calibDataPath=./data/ms/mindspore.out"; - - int errorcode = mindspore::predict::RunBenchmark(MS_ARGS, args); - EXPECT_EQ(0, errorcode); -} - -TEST_F(BenchmarkTest, MindSporeRun) { - const char* args[4]; - args[0] = "./benchmark"; - args[1] = "--modelPath=./data/lenet/lenet.ms"; - args[2] = "--inDataPath=./data/lenet/lenet.bin"; - args[3] = "--calibDataPath=./data/lenet/lenet.out"; - - int errorcode = mindspore::predict::RunBenchmark(4, args); - EXPECT_EQ(0, errorcode); -} -} // namespace predict -} // namespace mindspore diff --git a/predict/test/data/lenet/lenet.bin b/predict/test/data/lenet/lenet.bin deleted file mode 100755 index 6aef53bd64bf1fcffdeab0eecbb2b011a314fa9d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3136 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 Cdn_dY diff --git a/predict/test/data/lenet/lenet.ms b/predict/test/data/lenet/lenet.ms deleted file mode 100755 index c66948dd67738f5b9cfd6c0f132f8547623c9abf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1725872 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%0DZXwEG8NRCG?qG^ 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�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≧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%0A6RPa44+sx_&-y;PB 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 z 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 z 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

5`Cmn0$Pp0u+qoLUaAf}i}}P(QI7Obs~udH!~x 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∨wfsV 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