Files
2026-07-13 13:33:03 +08:00

462 lines
13 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// Backend.hpp
// MNN
//
// Created by MNN on 2018/07/06.
// Copyright © 2018, Alibaba Group Holding Limited
//
#ifndef Backend_hpp
#define Backend_hpp
#include <MNN/MNNForwardType.h>
#include <MNN/ErrorCode.hpp>
#include <map>
#include "Command.hpp"
#include "NonCopyable.hpp"
#include "BufferAllocator.hpp"
#include <future>
#include <atomic>
namespace MNN {
struct Op;
class Execution;
class Runtime;
class Backend;
struct RuntimeHint {
// 0: Defer, 1: Eager
int memoryAllocatorType = 0;
int winogradMemoryUsed = 3;
// 0-100, 50 means litter core has 50% capacity of large core
int cpuDecreaseRate = 50;
int dynamicQuantOption = 0;
// attentionOption % 8:
// 0: Do not quantize
// 1: Q,K: Int8, V: Float
// 2: Q,K,V: Int8
// attentionOption / 8:
// 0: don't use flash attention
// 1: use flash attention
int attentionOption = 8;
// the kvcache size limit of each layer
// if the size of kvcache in memory exceeds the limit
// it will be moved to disk to save memory
// -1 for no limit
int kvcacheSizeLimit = -1;
// path of the kvcache directory
std::string kvcacheDirPath = "";
// path of the kvcache directory
std::string prefixcacheDirPath = "prefixcache";
std::string midMemoryPath;
std::string weightMemoryPath;
int mmapFileSize = 1024; // MB
int useCachedMmap = 0;
// op encoder number for once commit
int encorderNumForCommit = 10;
int initThreadNumber = 0;
// whether to use Arm sme2 cores when threads>1
bool useArmSme2Cores = true;
#ifdef MNN_DEFAULT_USE_KLEIDIAI
bool enableKleidiAI = true;
#else
bool enableKleidiAI = false;
#endif
// Use CPU Ids
std::vector<int> cpuIds;
// Division ration between SME and NEON when runtime threads>=4
// Default: 41, which means that in LLM inference,
// during the Prefill stage the workload
// per single SME core is six times that of NEON,
//while during the Decode stage it is the same (1×).
int divisionRatio = 41;
int smeCores = 2; // Number of SME cores of the backend, default is 2, if supports sme
};
/** abstract backend */
class Backend : public NonCopyable {
public:
/** info used to create backend */
struct Info {
/** forward type. */
MNNForwardType type = MNN_FORWARD_CPU;
/** numThread for CPU . number of threads. gpuMode for GPU only. tuning/memory Mode setting. */
union {
int numThread = 4;
int gpuMode;
};
/** user data. */
BackendConfig* user = NULL;
enum Mode {
// The Op will be run in execution->onExecute
DIRECT = 0,
// The Op will be recorded. Run in onExecuteBegin and Wait in onExecuteEnd
INDIRECT = 1
};
Mode mode = DIRECT;
};
/** backend buffer storage type */
enum StorageType {
/**
use NOT reusable memory.
- allocates memory when `onAcquireBuffer` is called.
- releases memory when `onReleaseBuffer` is called or when the backend is deleted.
- do NOTHING when `onClearBuffer` is called.
*/
STATIC,
/**
use reusable memory.
- allocates or reuses memory when `onAcquireBuffer` is called. prefers reusing.
- collects memory for reuse when `onReleaseBuffer` is called.
- releases memory when `onClearBuffer` is called or when the backend is deleted.
*/
DYNAMIC,
/**
use NOT reusable memory.
- allocates memory when `onAcquireBuffer` is called.
- do NOTHING when `onReleaseBuffer` is called.
- releases memory when `onClearBuffer` is called or when the backend is deleted.
*/
DYNAMIC_SEPERATE,
DYNAMIC_IN_EXECUTION
};
public:
/**
* @brief initializer.
* @param type forward type.
*/
Backend(MNNForwardType type) : mType(type) {
// nothing to do
}
/**
* @brief deinitializer.
*/
virtual ~Backend() = default;
public:
/**
* @brief create execution for op with input and output tensors.
* @param inputs input tensors.
* @param outputs output tensors.
* @param op given op.
* @return created execution if op is supported, nullptr otherwise.
*/
virtual Execution* onCreate(const std::vector<Tensor*>& inputs, const std::vector<Tensor*>& outputs,
const MNN::Op* op) = 0;
/**
* @brief callback before resize ops.
*/
virtual void onResizeBegin() {
// nothing to do
}
/**
* @brief callback after resize ops.
*/
virtual ErrorCode onResizeEnd() = 0;
/**
* @brief callback before executing ops.
*/
virtual void onExecuteBegin() const = 0;
/**
* @brief callback after executing ops.
*/
virtual void onExecuteEnd() const = 0;
virtual const Runtime* getRuntime() {
return nullptr;
}
/**
* @brief allocate buffer of tensor for given storage type.
* @param tensor buffer provider.
* @param storageType buffer storage type.
* @return success or not.
*/
MNN_PUBLIC bool onAcquireBuffer(const Tensor* tensor, StorageType storageType);
/**
* @brief release buffer of tensor for given storage type.
* @param tensor buffer provider.
* @param storageType buffer storage type.
* @return success or not.
*/
MNN_PUBLIC bool onReleaseBuffer(const Tensor* tensor, StorageType storageType);
class MemObj : public RefCount {
public:
MemObj() {}
virtual ~ MemObj() {}
virtual MemChunk chunk() { return MemChunk(); }
};
/**
* @brief allocate buffer of tensor for given storage type.
* @param tensor buffer provider.
* @param storageType buffer storage type.
* @return MemObj for release, if failed, return nullptr.
*/
virtual MemObj* onAcquire(const Tensor* tensor, StorageType storageType) = 0;
virtual bool onSelectDynamicAllocator(int index, int maxIndex) {
return false;
}
/**
* @brief get buffer from tensor directly
* @param tensor buffer provider.
* @return support or not
*/
virtual bool onGetTensorInfo(const Tensor* tensor, void* dstInfo) {
return false;
}
/**
* @brief clear all dynamic buffers.
* @return success or not.
*/
virtual bool onClearBuffer() = 0;
/**
* @brief copy buffer from tensor to tensor.
* @param srcTensor source buffer provider.
* @param dstTensor dest buffer provider.
*/
virtual void onCopyBuffer(const Tensor* srcTensor, const Tensor* dstTensor) const = 0;
public:
/**
* @brief get forward type.
* @return forward type.
*/
inline MNNForwardType type() const {
return mType;
}
public:
/**
* @brief get Gpu Tensor map host ptr/ unmap
*/
virtual void* onMapTensor(Tensor::MapType mtype, Tensor::DimensionType dtype, const Tensor* srcTensor) {
return nullptr;
}
virtual bool onUnmapTensor(Tensor::MapType mtype, Tensor::DimensionType dtype, const Tensor* dstTensor, void* mapPtr) {
return false;
}
virtual int onSync(Tensor::MapType mtype, bool toCpu, const Tensor* dstTensor) {
return 0;
}
public:
void* getMetaPtr() {
return mMetaPtr;
}
void setMetaPtr(void* ptr) {
mMetaPtr = ptr;
}
// path of the NPU model directory
std::string pNPUModelDirPath = ".";
private:
const MNNForwardType mType;
void* mMetaPtr;
};
/** Each backend belong to a runtime*/
class Runtime : public NonCopyable {
public:
/**
Origin Op -> (Compiler) -> New Op -> Backend
Default use Compiler_Geometry, Origin Op -> Compiler_Geometry -> Little Op
For serveral Backend, we can't use Geometry to decompose origin op, then it set Compiler_Origin
*/
enum CompilerType {
Compiler_Geometry = 0,
Compiler_Origin = 1,
Compiler_Loop = 2,
};
enum AllocatorType {
Allocator_Defer = 0,
Allocator_Eager = 1,
};
void setRuntimeHint(const RuntimeHint& hint) {
mHint = hint;
}
const RuntimeHint& hint() const {
return mHint;
}
virtual CompilerType onGetCompilerType() const {
return Compiler_Loop;
}
virtual ~Runtime() = default;
/**
@brief create backend
@return created backend
*/
virtual Backend* onCreate(const BackendConfig* config = nullptr, Backend* origin = nullptr) const = 0;
/**
@brief reset runtime
*/
virtual void onReset(int numberThread, const BackendConfig* config, bool full) {
// Do nothing
}
/**
@brief clear unuseful resource
@param level clear level: 0 - 100, bigger mean clear more, smaller mean cache more
*/
virtual void onGabageCollect(int level) = 0;
/**
@brief Measure the memory it used in MB
*/
virtual float onGetMemoryInMB() {
return 0.0f;
}
// For NPU backend don't support load from buffer , use onSetCachePath
virtual bool onSetCachePath(const char* path, int mode) {
return false;
}
// If buffer is not nullptr, try copy cache, else delete cache
virtual bool onSetCache(const void* buffer, size_t size) {
//default cache valid, avoid being reset
return true;
}
virtual std::pair<const void*, size_t> onGetCache() {
return std::make_pair(nullptr, 0);
}
virtual int onGetRuntimeStatus(RuntimeStatus statusEnum) const {
return 0;
}
// If the info user set can't be match by runtime, return false and set real info
virtual bool onCheckInfo(Backend::Info& info) const {
return true;
}
struct OpInfo {
bool initCostLong;
float exeutionCost; // In ms
float initCost; // In ms
};
/**
* @brief measure the cost for op with input and output tensors.
* @param inputs input tensors.
* @param outputs output tensors.
* @param op given op.
* @param dstInfo the Info for write.
* @return support the op or not;
*/
virtual bool onMeasure(const std::vector<Tensor*>& inputs, const std::vector<Tensor*>& outputs,
const MNN::Op* op, OpInfo& dstInfo) const {
return true;
}
// FIXME: Temply use to mask cache valid, in future will delete
virtual void onMaskOpReady(const std::vector<Tensor*>& inputs, const std::vector<Tensor*>& outputs,
const MNN::Op* op) {
// Do nothing
}
// FIXME: Temply used, in future will refract
std::atomic_bool mCancelled = ATOMIC_VAR_INIT(false);
MNN_PUBLIC bool hasAsyncWork() const;
void setAsyncWork(std::future<int>&& future);
MNN_PUBLIC void waitAsyncWork();
virtual void onConcurrencyBegin() const {
// Do nothing
}
virtual void onConcurrencyEnd() const {
// Do nothing
}
mutable int pCurrentStatus = 0; // NO_ERROR
mutable int pExecutionStatus = 0; // NO_ERROR
// GPU profiling: last execution time in ms measured by GPU timestamps.
// Returns -1.0f if profiling is not supported or not enabled.
virtual float onGetLastGpuTimeMs() const { return -1.0f; }
// TODO: Move to Backend
void* pMeta = nullptr;
private:
std::future<int> mFuture;
RuntimeHint mHint;
};
/** abstract Runtime register */
class RuntimeCreator {
public:
/**
@brief initializer.
*/
virtual ~RuntimeCreator() = default;
virtual Runtime* onCreate(const Backend::Info& info) const = 0;
/**
@brief Turn info to supported.
@param info info to valid.
@return success or not
*/
virtual bool onValid(Backend::Info& info) const {
info.mode = Backend::Info::DIRECT;
return true;
}
virtual bool onGetDeviceInfo(const std::string& deviceKey, std::string& deviceValue) const {
return false;
}
virtual bool onSetQuantInfo(const Op* op, const std::vector<Tensor*>& inputs, const std::vector<Tensor*>& outputs) const {
return false;
}
protected:
/**
@brief deinitializer.
*/
RuntimeCreator() = default;
};
/**
* @brief get registered backend creator for given forward type.
* @param type given forward type.
* @return backend creator pointer if registered, nullptr otherwise.
*/
MNN_PUBLIC const RuntimeCreator* MNNGetExtraRuntimeCreator(MNNForwardType type);
/**
* @brief register backend creator for given forward type.
* @param type given forward type.
* @param creator registering backend creator.
* @return true if backend creator for given forward type was not registered before, false otherwise.
*/
MNN_PUBLIC bool MNNInsertExtraRuntimeCreator(MNNForwardType type, const RuntimeCreator* creator,
bool needCheck = false);
MNN_PUBLIC bool MNNCPUCopyBuffer(const Tensor* srcTensor, const Tensor* dstTensor);
} // namespace MNN
#endif /* Backend_hpp */