2019-04-17 10:49:11 +08:00
|
|
|
//
|
|
|
|
// CPUBackend.cpp
|
|
|
|
// MNN
|
|
|
|
//
|
|
|
|
// Created by MNN on 2018/07/06.
|
|
|
|
// Copyright © 2018, Alibaba Group Holding Limited
|
|
|
|
//
|
|
|
|
|
|
|
|
#include "CPUBackend.hpp"
|
2019-08-22 20:13:46 +08:00
|
|
|
#include <cmath>
|
2019-04-17 10:49:11 +08:00
|
|
|
#include <mutex>
|
|
|
|
#include "BufferAllocator.hpp"
|
|
|
|
#include "CPUConcat.hpp"
|
|
|
|
#include "CPUTensorConvert.hpp"
|
|
|
|
#include "CommonOptFunction.h"
|
|
|
|
#include "TensorUtils.hpp"
|
2019-07-11 13:56:52 +08:00
|
|
|
#include "ThreadPool.hpp"
|
2019-04-17 10:49:11 +08:00
|
|
|
#ifdef _OPENMP
|
|
|
|
#include <omp.h>
|
|
|
|
#endif // _OPENMP
|
|
|
|
#include "CPURuntime.hpp"
|
|
|
|
|
|
|
|
#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
|
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) {
|
|
|
|
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: {
|
|
|
|
buffer.host = (uint8_t*)mStaticAllocator->alloc(size, true);
|
|
|
|
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) {
|
|
|
|
if (nullptr == nativeTensor->buffer().host) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (STATIC == storageType) {
|
|
|
|
mStaticAllocator->free(nativeTensor->buffer().host, true);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (DYNAMIC_SEPERATE == storageType) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
mDynamicAllocator->free(nativeTensor->buffer().host);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPUBackend::onClearBuffer() {
|
|
|
|
mDynamicAllocator->release();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Don't support cpu to gpu / gpu to cpu
|
|
|
|
MNN_ASSERT(srcBuffer.host != nullptr && dstBuffer.host != nullptr);
|
|
|
|
|
2019-08-22 20:13:46 +08:00
|
|
|
CPUTensorConverter::convert(srcTensor, dstTensor);
|
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
|