| 
									
										
										
										
											2019-04-17 10:49:11 +08:00
										 |  |  | //
 | 
					
						
							|  |  |  | //  CPUBackend.cpp
 | 
					
						
							|  |  |  | //  MNN
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //  Created by MNN on 2018/07/06.
 | 
					
						
							|  |  |  | //  Copyright © 2018, Alibaba Group Holding Limited
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-27 22:16:57 +08:00
										 |  |  | #include "backend/cpu/CPUBackend.hpp"
 | 
					
						
							| 
									
										
										
										
											2019-08-22 20:13:46 +08:00
										 |  |  | #include <cmath>
 | 
					
						
							| 
									
										
										
										
											2019-04-17 10:49:11 +08:00
										 |  |  | #include <mutex>
 | 
					
						
							| 
									
										
										
										
											2019-12-27 22:16:57 +08:00
										 |  |  | #include "core/BufferAllocator.hpp"
 | 
					
						
							|  |  |  | #include "backend/cpu/CPUConcat.hpp"
 | 
					
						
							|  |  |  | #include "backend/cpu/CPUTensorConvert.hpp"
 | 
					
						
							|  |  |  | #include "backend/cpu/compute/CommonOptFunction.h"
 | 
					
						
							|  |  |  | #include "core/TensorUtils.hpp"
 | 
					
						
							|  |  |  | #include "backend/cpu/ThreadPool.hpp"
 | 
					
						
							|  |  |  | #include "core/SizeComputer.hpp"
 | 
					
						
							| 
									
										
										
										
											2019-04-17 10:49:11 +08:00
										 |  |  | #ifdef _OPENMP
 | 
					
						
							|  |  |  | #include <omp.h>
 | 
					
						
							|  |  |  | #endif // _OPENMP
 | 
					
						
							| 
									
										
										
										
											2019-12-27 22:16:57 +08:00
										 |  |  | #include "backend/cpu/CPURuntime.hpp"
 | 
					
						
							| 
									
										
										
										
											2019-04-17 10:49:11 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | #define MAX_THREAD_NUMBER 32
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //#define MNN_DUMP_MEMORY_USAGE
 | 
					
						
							| 
									
										
										
										
											2019-08-22 20:13:46 +08:00
										 |  |  | #define MNN_CPU_CHECK_NAN 1
 | 
					
						
							| 
									
										
										
										
											2019-04-17 10:49:11 +08:00
										 |  |  | namespace MNN { | 
					
						
							| 
									
										
										
										
											2019-05-09 19:39:33 +08:00
										 |  |  | #ifdef MNN_CODEGEN_REGISTER
 | 
					
						
							|  |  |  | void registerCPUOps(); | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2019-04-17 10:49:11 +08:00
										 |  |  | static inline std::map<OpType, CPUBackend::Creator*>* getCreatorMap() { | 
					
						
							|  |  |  |     static std::once_flag of; | 
					
						
							|  |  |  |     static std::map<OpType, CPUBackend::Creator*>* ret = nullptr; | 
					
						
							|  |  |  |     std::call_once(of, [&]() { ret = new std::map<OpType, CPUBackend::Creator*>; }); | 
					
						
							|  |  |  |     return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool CPUBackend::addCreator(OpType t, Creator* c) { | 
					
						
							|  |  |  |     auto map = getCreatorMap(); | 
					
						
							|  |  |  |     if (map->find(t) != map->end()) { | 
					
						
							|  |  |  |         MNN_PRINT("Error: %d type has be added\n", t); | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     map->insert(std::make_pair(t, c)); | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-22 20:13:46 +08:00
										 |  |  | CPUBackend::CPUBackend(int numberThread, BackendConfig::MemoryMode memory, BackendConfig::PowerMode power, size_t flags) | 
					
						
							| 
									
										
										
										
											2019-04-17 10:49:11 +08:00
										 |  |  |     : Backend(MNN_FORWARD_CPU), mThreadNumber(numberThread), mMemory(memory), mPower(power) { | 
					
						
							|  |  |  |     mThreadNumber = std::max(1, mThreadNumber); | 
					
						
							|  |  |  |     mThreadNumber = std::min(mThreadNumber, MAX_THREAD_NUMBER); | 
					
						
							|  |  |  |     mDynamicAllocator.reset(new BufferAllocator); | 
					
						
							|  |  |  |     mStaticAllocator.reset(new BufferAllocator); | 
					
						
							| 
									
										
										
										
											2019-08-22 20:13:46 +08:00
										 |  |  |     mCheckNAN = flags == MNN_CPU_CHECK_NAN; | 
					
						
							| 
									
										
										
										
											2019-07-11 13:56:52 +08:00
										 |  |  | #ifdef _OPENMP
 | 
					
						
							| 
									
										
										
										
											2019-04-17 10:49:11 +08:00
										 |  |  |     switch (power) { | 
					
						
							|  |  |  |         case BackendConfig::Power_Low: | 
					
						
							|  |  |  |             MNNSetCPUThreadsMode(MNN_CPU_MODE_LITTLE); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case BackendConfig::Power_High: | 
					
						
							|  |  |  |             MNNSetCPUThreadsMode(MNN_CPU_MODE_POWER_FRI); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         default: | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-07-11 13:56:52 +08:00
										 |  |  | #endif
 | 
					
						
							|  |  |  | #ifdef MNN_USE_THREAD_POOL
 | 
					
						
							| 
									
										
										
										
											2019-07-19 17:09:09 +08:00
										 |  |  |     mThreadNumber = ThreadPool::init(mThreadNumber); | 
					
						
							|  |  |  |     if (mThreadNumber > 1) { | 
					
						
							|  |  |  |         mTaskIndex = ThreadPool::acquireWorkIndex(); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         mTaskIndex = -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (mTaskIndex >= 0 && mPower == BackendConfig::Power_High) { | 
					
						
							|  |  |  |         ThreadPool::active(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-07-11 13:56:52 +08:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
											
												- dynamic computation graph (beta)
	- add supports (/express)
	- add tests
	- add benchmarks with it (/benchmark/exprModels)
- Python
	- MNN engine and tools were submitted to pip
	- available on Windows/macOS/Linux
- Engine/Converter
	- add supports for each op benchmarking
	- refactor optimizer by separating steps
- CPU
	- add supports for Conv3D, Pool3D, ELU, ReverseSequence
	- fix ArgMax, Permute, Scale, BinaryOp, Slice, SliceTf
- OpenCL
	- add half transform in CPU
	- add broadcast supports for binary
	- optimize Conv2D, Reshape, Eltwise, Gemm, etc.
- OpenGL
	- add sub, real div supports for binary
	- add supports for unary
	- optimize Conv2D, Reshape
- Vulkan
	- add max supports for eltwise
- Metal
	- fix metallib missing problem
- Train/Quantization
	- use express to refactor training codes
											
										 
											2019-09-26 21:02:07 +08:00
										 |  |  |     mFlops = MNNGetCPUFlops(mThreadNumber); | 
					
						
							| 
									
										
										
										
											2019-04-17 10:49:11 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | CPUBackend::~CPUBackend() { | 
					
						
							| 
									
										
										
										
											2019-07-19 17:09:09 +08:00
										 |  |  | #ifdef MNN_USE_THREAD_POOL
 | 
					
						
							|  |  |  |     if (mTaskIndex >= 0 && mPower == BackendConfig::Power_High) { | 
					
						
							|  |  |  |         ThreadPool::deactive(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     ThreadPool::releaseWorkIndex(mTaskIndex); | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2019-04-17 10:49:11 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void CPUBackend::onExecuteBegin() const { | 
					
						
							|  |  |  | #ifdef MNN_DUMP_MEMORY_USAGE
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         auto dynamicMemoryInMB = mDynamicAllocator->totalSize() / 1024.0f / 1024.0f; | 
					
						
							|  |  |  |         FUNC_PRINT_ALL(dynamicMemoryInMB, f); | 
					
						
							|  |  |  |         auto staticMemoryInMB = mStaticAllocator->totalSize() / 1024.0f / 1024.0f; | 
					
						
							|  |  |  |         FUNC_PRINT_ALL(staticMemoryInMB, f); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2019-07-19 17:09:09 +08:00
										 |  |  | #ifdef MNN_USE_THREAD_POOL
 | 
					
						
							|  |  |  |     if (mTaskIndex >= 0 && mPower != BackendConfig::Power_High) { | 
					
						
							|  |  |  |         ThreadPool::active(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | #else
 | 
					
						
							| 
									
										
										
										
											2019-04-17 10:49:11 +08:00
										 |  |  | #ifdef _OPENMP
 | 
					
						
							|  |  |  |     omp_set_dynamic(0); | 
					
						
							|  |  |  |     omp_set_num_threads(mThreadNumber); | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2019-07-11 13:56:52 +08:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2019-04-17 10:49:11 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-07-19 17:09:09 +08:00
										 |  |  | void CPUBackend::onExecuteEnd() const { | 
					
						
							|  |  |  | #ifdef MNN_USE_THREAD_POOL
 | 
					
						
							|  |  |  |     if (mTaskIndex >= 0 && mPower != BackendConfig::Power_High) { | 
					
						
							|  |  |  |         ThreadPool::deactive(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-04-17 10:49:11 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | bool CPUBackend::onAcquireBuffer(const MNN::Tensor* nativeTensorConst, StorageType storageType) { | 
					
						
							| 
									
										
										
										
											2019-12-27 22:16:57 +08:00
										 |  |  |     if (nativeTensorConst == nullptr) { | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-04-17 10:49:11 +08:00
										 |  |  |     auto nativeTensor = (Tensor*)nativeTensorConst; | 
					
						
							|  |  |  |     auto& buffer      = nativeTensor->buffer(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto size = nativeTensor->size(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // MNN_PRINT("Acquire size = %d\n", size);
 | 
					
						
							|  |  |  |     if (size <= 0) { | 
					
						
							|  |  |  |         MNN_ASSERT(false); | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     switch (storageType) { | 
					
						
							|  |  |  |         case STATIC: { | 
					
						
							| 
									
										
										
										
											2020-01-15 13:33:47 +08:00
										 |  |  |             buffer.host = (uint8_t*)mStaticAllocator->alloc(size, false); | 
					
						
							| 
									
										
										
										
											2019-04-17 10:49:11 +08:00
										 |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         case DYNAMIC: { | 
					
						
							|  |  |  |             buffer.host = (uint8_t*)(mDynamicAllocator->alloc(size, false)); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         case DYNAMIC_SEPERATE: { | 
					
						
							|  |  |  |             buffer.host = (uint8_t*)(mDynamicAllocator->alloc(size, true)); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         default: | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (nullptr == buffer.host) { | 
					
						
							|  |  |  |         MNN_ERROR("Alloc buffer error for cpu backend\n"); | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (buffer.type.code == halide_type_handle) { | 
					
						
							|  |  |  |         ::memset(buffer.host, 0, size); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool CPUBackend::onReleaseBuffer(const MNN::Tensor* nativeTensor, StorageType storageType) { | 
					
						
							| 
									
										
										
										
											2019-12-27 22:16:57 +08:00
										 |  |  |     if (nativeTensor == nullptr) { | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-04-17 10:49:11 +08:00
										 |  |  |     if (nullptr == nativeTensor->buffer().host) { | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (STATIC == storageType) { | 
					
						
							| 
									
										
										
										
											2020-01-15 13:33:47 +08:00
										 |  |  |         mStaticAllocator->free(nativeTensor->buffer().host); | 
					
						
							| 
									
										
										
										
											2019-04-17 10:49:11 +08:00
										 |  |  |         return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (DYNAMIC_SEPERATE == storageType) { | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     mDynamicAllocator->free(nativeTensor->buffer().host); | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
											
												- dynamic computation graph (beta)
	- add supports (/express)
	- add tests
	- add benchmarks with it (/benchmark/exprModels)
- Python
	- MNN engine and tools were submitted to pip
	- available on Windows/macOS/Linux
- Engine/Converter
	- add supports for each op benchmarking
	- refactor optimizer by separating steps
- CPU
	- add supports for Conv3D, Pool3D, ELU, ReverseSequence
	- fix ArgMax, Permute, Scale, BinaryOp, Slice, SliceTf
- OpenCL
	- add half transform in CPU
	- add broadcast supports for binary
	- optimize Conv2D, Reshape, Eltwise, Gemm, etc.
- OpenGL
	- add sub, real div supports for binary
	- add supports for unary
	- optimize Conv2D, Reshape
- Vulkan
	- add max supports for eltwise
- Metal
	- fix metallib missing problem
- Train/Quantization
	- use express to refactor training codes
											
										 
											2019-09-26 21:02:07 +08:00
										 |  |  | std::pair<float, bool> CPUBackend::onMeasure(const std::vector<Tensor*>& inputs, const std::vector<Tensor*>& outputs, | 
					
						
							|  |  |  |                                     const MNN::Op* op) { | 
					
						
							|  |  |  |     auto map  = getCreatorMap(); | 
					
						
							|  |  |  |     auto iter = map->find(op->type()); | 
					
						
							|  |  |  |     if (iter == map->end()) { | 
					
						
							|  |  |  |         MNN_PRINT("Don't support type %d, %s\n", op->type(), op->name()->c_str()); | 
					
						
							|  |  |  |         return std::make_pair(0.0f, false); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     auto computeFlops = SizeComputer::computeFlops(op, inputs, outputs); | 
					
						
							|  |  |  |     return std::make_pair(computeFlops / mFlops * 1000.0f, true); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-04-17 10:49:11 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | /// get execution
 | 
					
						
							|  |  |  | Execution* CPUBackend::onCreate(const std::vector<Tensor*>& inputs, const std::vector<Tensor*>& outputs, | 
					
						
							|  |  |  |                                 const MNN::Op* op) { | 
					
						
							|  |  |  |     auto map  = getCreatorMap(); | 
					
						
							|  |  |  |     auto iter = map->find(op->type()); | 
					
						
							|  |  |  |     if (iter == map->end()) { | 
					
						
							|  |  |  |         MNN_PRINT("Don't support type %d, %s\n", op->type(), op->name()->c_str()); | 
					
						
							|  |  |  |         return nullptr; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     auto exe = iter->second->onCreate(inputs, outputs, op, this); | 
					
						
							|  |  |  |     if (nullptr == exe) { | 
					
						
							|  |  |  |         MNN_PRINT("The Creator Don't support type %d, %s\n", op->type(), op->name()->c_str()); | 
					
						
							|  |  |  |         return nullptr; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-08-22 20:13:46 +08:00
										 |  |  |     if (mCheckNAN) { | 
					
						
							|  |  |  |         class CheckNANExecution : public Execution { | 
					
						
							|  |  |  |         public: | 
					
						
							|  |  |  |             CheckNANExecution(Execution* exe) : Execution(exe->backend()) { | 
					
						
							|  |  |  |                 mExecution.reset(exe); | 
					
						
							|  |  |  |                 mValid = exe->valid(); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             virtual ~CheckNANExecution() { | 
					
						
							|  |  |  |                 // Do nothing
 | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             virtual ErrorCode onResize(const std::vector<Tensor*>& inputs, | 
					
						
							|  |  |  |                                        const std::vector<Tensor*>& outputs) override { | 
					
						
							|  |  |  |                 return mExecution->onResize(inputs, outputs); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             virtual ErrorCode onReleaseCache() override { | 
					
						
							|  |  |  |                 return mExecution->onReleaseCache(); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             virtual ErrorCode onExecute(const std::vector<Tensor*>& inputs, | 
					
						
							|  |  |  |                                         const std::vector<Tensor*>& outputs) override { | 
					
						
							|  |  |  |                 for (auto tensor : inputs) { | 
					
						
							|  |  |  |                     if (halide_type_float != tensor->getType().code) { | 
					
						
							|  |  |  |                         return NO_ERROR; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     auto size = tensor->elementSize(); | 
					
						
							|  |  |  |                     auto ptr  = tensor->host<float>(); | 
					
						
							|  |  |  |                     for (int i = 0; i < size; ++i) { | 
					
						
							|  |  |  |                         auto value = ptr[i]; | 
					
						
							|  |  |  |                         if (std::isnan(value) || std::isinf(value)) { | 
					
						
							|  |  |  |                             return INVALID_VALUE; | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 auto code = mExecution->onExecute(inputs, outputs); | 
					
						
							|  |  |  |                 if (NO_ERROR != code) { | 
					
						
							|  |  |  |                     return code; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 for (auto tensor : outputs) { | 
					
						
							|  |  |  |                     if (halide_type_float != tensor->getType().code) { | 
					
						
							|  |  |  |                         return NO_ERROR; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     auto size = tensor->elementSize(); | 
					
						
							|  |  |  |                     auto ptr  = tensor->host<float>(); | 
					
						
							|  |  |  |                     for (int i = 0; i < size; ++i) { | 
					
						
							|  |  |  |                         auto value = ptr[i]; | 
					
						
							|  |  |  |                         if (std::isnan(value) || std::isinf(value)) { | 
					
						
							|  |  |  |                             return INVALID_VALUE; | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 return NO_ERROR; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         private: | 
					
						
							|  |  |  |             std::unique_ptr<Execution> mExecution; | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |         return new CheckNANExecution(exe); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-04-17 10:49:11 +08:00
										 |  |  |     return exe; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool CPUBackend::onAllocateBuffer() { | 
					
						
							| 
									
										
										
										
											2020-01-15 13:33:47 +08:00
										 |  |  |     mStaticAllocator->release(false); | 
					
						
							| 
									
										
										
										
											2019-04-17 10:49:11 +08:00
										 |  |  |     return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool CPUBackend::onClearBuffer() { | 
					
						
							| 
									
										
										
										
											2020-01-15 13:33:47 +08:00
										 |  |  |     mDynamicAllocator->release(true); | 
					
						
							|  |  |  |     mStaticAllocator->release(false); | 
					
						
							| 
									
										
										
										
											2019-04-17 10:49:11 +08:00
										 |  |  |     return true; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-02-26 09:57:17 +08:00
										 |  |  | std::pair<int, int> CPUBackend::multiThreadDivide(int size) const { | 
					
						
							|  |  |  |     int sizeDivide = size / mThreadNumber; | 
					
						
							|  |  |  |     sizeDivide = UP_DIV(sizeDivide, 4) * 4; | 
					
						
							|  |  |  |     int scheduleNumber = 1; | 
					
						
							|  |  |  |     if (sizeDivide > 0) { | 
					
						
							|  |  |  |         scheduleNumber = UP_DIV(size, sizeDivide); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return std::make_pair(sizeDivide, scheduleNumber); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-04-17 10:49:11 +08:00
										 |  |  | void CPUBackend::onCopyBuffer(const Tensor* srcTensor, const Tensor* dstTensor) const { | 
					
						
							|  |  |  |     auto& srcBuffer = srcTensor->buffer(); | 
					
						
							|  |  |  |     auto& dstBuffer = dstTensor->buffer(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     MNN_ASSERT(srcBuffer.dimensions == dstBuffer.dimensions); | 
					
						
							|  |  |  |     MNN_ASSERT(srcBuffer.type == dstBuffer.type); | 
					
						
							|  |  |  |     if (srcTensor->getDimensionType() == dstTensor->getDimensionType()) { | 
					
						
							|  |  |  |         for (int i = 0; i < srcBuffer.dimensions; ++i) { | 
					
						
							|  |  |  |             MNN_ASSERT(srcBuffer.dim[i].extent <= dstBuffer.dim[i].extent); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
											
												- dynamic computation graph (beta)
	- add supports (/express)
	- add tests
	- add benchmarks with it (/benchmark/exprModels)
- Python
	- MNN engine and tools were submitted to pip
	- available on Windows/macOS/Linux
- Engine/Converter
	- add supports for each op benchmarking
	- refactor optimizer by separating steps
- CPU
	- add supports for Conv3D, Pool3D, ELU, ReverseSequence
	- fix ArgMax, Permute, Scale, BinaryOp, Slice, SliceTf
- OpenCL
	- add half transform in CPU
	- add broadcast supports for binary
	- optimize Conv2D, Reshape, Eltwise, Gemm, etc.
- OpenGL
	- add sub, real div supports for binary
	- add supports for unary
	- optimize Conv2D, Reshape
- Vulkan
	- add max supports for eltwise
- Metal
	- fix metallib missing problem
- Train/Quantization
	- use express to refactor training codes
											
										 
											2019-09-26 21:02:07 +08:00
										 |  |  |     if (nullptr == srcBuffer.host || nullptr == dstBuffer.host) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-04-17 10:49:11 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-27 22:16:57 +08:00
										 |  |  |     auto code = CPUTensorConverter::convert(srcTensor, dstTensor); | 
					
						
							|  |  |  |     if (NO_ERROR != code) { | 
					
						
							|  |  |  |         MNN_ERROR("Error in CPUBackend::onCopyBuffer\n"); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-04-17 10:49:11 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct CPUBackendCreator : BackendCreator { | 
					
						
							|  |  |  |     Backend* onCreate(const Backend::Info& info) const override { | 
					
						
							| 
									
										
										
										
											2019-08-22 20:13:46 +08:00
										 |  |  |         auto power   = BackendConfig::Power_Normal; | 
					
						
							|  |  |  |         auto memory  = BackendConfig::Memory_Normal; | 
					
						
							|  |  |  |         size_t flags = 0; | 
					
						
							| 
									
										
										
										
											2019-04-17 10:49:11 +08:00
										 |  |  |         if (nullptr != info.user) { | 
					
						
							|  |  |  |             power  = info.user->power; | 
					
						
							|  |  |  |             memory = info.user->memory; | 
					
						
							| 
									
										
										
										
											2019-08-22 20:13:46 +08:00
										 |  |  |             flags  = info.user->flags; | 
					
						
							| 
									
										
										
										
											2019-04-17 10:49:11 +08:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-05-09 19:39:33 +08:00
										 |  |  | #ifdef MNN_CODEGEN_REGISTER
 | 
					
						
							| 
									
										
										
										
											2019-05-08 15:44:57 +08:00
										 |  |  |         static std::once_flag s_flag; | 
					
						
							| 
									
										
										
										
											2019-05-09 19:39:33 +08:00
										 |  |  |         std::call_once(s_flag, [&]() { registerCPUOps(); }); | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2019-08-22 20:13:46 +08:00
										 |  |  |         return new CPUBackend(info.numThread, memory, power, flags); | 
					
						
							| 
									
										
										
										
											2019-04-17 10:49:11 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-08 15:44:57 +08:00
										 |  |  | void registerCPUBackendCreator() { | 
					
						
							| 
									
										
										
										
											2019-04-17 10:49:11 +08:00
										 |  |  |     MNNInsertExtraBackendCreator(MNN_FORWARD_CPU, new CPUBackendCreator); | 
					
						
							| 
									
										
										
										
											2019-05-08 15:44:57 +08:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2019-04-17 10:49:11 +08:00
										 |  |  | } // namespace MNN
 |