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

294 lines
8.7 KiB
C++

//
// MusaBackend.cpp
// MNN
//
// Created by MNN on 2026/02/25.
// Copyright © 2026, Alibaba Group Holding Limited
//
#include "backend/musa/core/MusaBackend.hpp"
#include "MNN_generated.h"
#include <map>
#include <mutex>
#include "core/Macro.h"
#include "shape/SizeComputer.hpp"
#include "core/TensorUtils.hpp"
#include "core/BufferAllocator.hpp"
namespace MNN {
namespace MUSA {
std::map<OpType, MusaBackend::Creator*>* gCreator() {
static std::map<OpType, MusaBackend::Creator*>* creators = nullptr;
static std::once_flag gOnce;
std::call_once(gOnce, [&]() { creators = new std::map<OpType, MusaBackend::Creator*>; });
return creators;
};
class MusaRuntimeAllocator : public BufferAllocator::Allocator {
public:
MusaRuntimeAllocator(MusaRuntime* rt) : mRuntime(rt) {}
virtual ~MusaRuntimeAllocator() = default;
virtual MemChunk onAlloc(size_t size, size_t align) override {
return MemChunk(mRuntime->alloc(size), 0);
}
virtual void onRelease(MemChunk ptr) override {
mRuntime->free(ptr.first);
}
private:
MusaRuntime* mRuntime;
};
MusaRuntimeWrapper::MusaRuntimeWrapper(BackendConfig::PrecisionMode precision, BackendConfig::PowerMode power, BackendConfig::MemoryMode memory, int deviceId) {
mMusaRuntime.reset(new MusaRuntime(deviceId));
if (mMusaRuntime.get()) {
if (mMusaRuntime->isCreateError()) {
mIsCreateError = true;
return;
}
std::shared_ptr<BufferAllocator::Allocator> allocator(new MusaRuntimeAllocator(mMusaRuntime.get()));
mBufferPool.reset(new EagerBufferAllocator(allocator));
}
mDefaultPrecision = precision;
mDefaultMemory = memory;
}
MusaRuntimeWrapper::~MusaRuntimeWrapper() {}
float MusaRuntimeWrapper::onGetMemoryInMB() {
auto staticMemoryInMB = mBufferPool->totalSize() / 1024.0f / 1024.0f;
return staticMemoryInMB;
}
std::pair<const void*, size_t> MusaRuntimeWrapper::onGetCache() {
return mMusaRuntime->makeCache();
}
bool MusaRuntimeWrapper::onSetCache(const void* buffer, size_t size) {
return mMusaRuntime->setCache(std::make_pair(buffer, size));
}
Backend* MusaRuntimeWrapper::onCreate(const BackendConfig* config, Backend* origin) const {
auto precision_mode = mDefaultPrecision;
auto memory_mode = mDefaultMemory;
if (nullptr != config) {
precision_mode = config->precision;
memory_mode = config->memory;
}
int precision = 0;
if (precision_mode == BackendConfig::Precision_Low) {
precision = 2;
} else if (precision_mode == BackendConfig::Precision_Normal) {
precision = 0;
} else if (precision_mode == BackendConfig::Precision_Low_BF16) {
precision = 3;
} else {
precision = 1;
}
return new MusaBackend(mBufferPool, mMusaRuntime, precision, memory_mode);
}
void MusaRuntimeWrapper::onGabageCollect(int level) {
mBufferPool->release(false);
}
MusaBackend::MusaBackend(std::shared_ptr<BufferAllocator> st,
std::shared_ptr<MusaRuntime> rt,
int precision, BackendConfig::MemoryMode memory)
: Backend(MNN_FORWARD_MUSA) {
mBufferPool.reset(new EagerBufferAllocator(BufferAllocator::Allocator::createRecurse(st.get())));
mStaticBufferPool = st;
mMusaRuntime = rt;
mUseFp16AsFp32 = (precision == 2);
mPrecision = precision;
mMemory = memory;
}
MusaBackend::~MusaBackend() {}
MusaRuntime* MusaBackend::getMusaRuntime() {
MNN_ASSERT(nullptr != mMusaRuntime.get());
return mMusaRuntime.get();
}
const Runtime* MusaBackend::getRuntime() {
return (const Runtime*)mMusaRuntime.get();
}
bool MusaBackend::useFp16() const {
return mUseFp16AsFp32;
}
int MusaBackend::getPrecision() const {
return mPrecision;
}
BackendConfig::MemoryMode MusaBackend::getMemoryMode() const {
return mMemory;
}
class MusaMemObj : public Backend::MemObj {
public:
MusaMemObj(BufferAllocator* allocator, MemChunk points) {
mPoint = std::move(points);
mAllocator = allocator;
}
virtual ~MusaMemObj() {
mAllocator->free(mPoint);
}
MemChunk chunk() override {
return mPoint;
}
private:
BufferAllocator* mAllocator;
MemChunk mPoint;
};
int MusaBackend::getBytes(const Tensor* tensor) const {
auto bytes = tensor->getType().bytes();
if (mPrecision == 2 || mPrecision == 3) { // Fp16 or Bf16
if (halide_type_float == tensor->getType().code) {
bytes = 2;
}
}
auto quant = TensorUtils::getDescribe(tensor)->quantAttr.get();
if (nullptr != quant && TensorUtils::getDescribe(tensor)->type == DataType_DT_INT8) {
bytes = 1;
}
return bytes;
}
CPUResizeCache* MusaBackend::getCache() {
return &mCache;
}
Backend::MemObj* MusaBackend::onAcquire(const Tensor* nativeTensor, StorageType storageType) {
BufferAllocator* allocator = nullptr;
auto bytes = getBytes(nativeTensor);
size_t mallocSize = realSize(nativeTensor) * bytes;
MemChunk buffer;
if (storageType == DYNAMIC_SEPERATE) {
buffer = mBufferPool->alloc(mallocSize, true);
allocator = mBufferPool.get();
} else if (storageType == DYNAMIC) {
buffer = mBufferPool->alloc(mallocSize, false);
allocator = mBufferPool.get();
} else {
MNN_ASSERT(storageType == STATIC);
buffer = mStaticBufferPool->alloc(mallocSize, false);
allocator = mStaticBufferPool.get();
}
if (nullptr == buffer.first) {
return nullptr;
}
auto host = buffer.ptr();
((Tensor*)nativeTensor)->buffer().device = (uint64_t)host;
auto des = TensorUtils::getDescribeOrigin(nativeTensor);
des->offset = buffer.second;
return new MusaMemObj(allocator, buffer);
}
bool MusaBackend::onClearBuffer() {
mCache.reset();
mBufferPool->release(true);
return true;
}
size_t MusaBackend::realSize(const Tensor* tensor) {
auto dim = TensorUtils::getDescribe(tensor)->dimensionFormat;
int pack = 1;
if (dim == MNN_DATA_FORMAT_NC4HW4) {
pack = PACK_NUMBER;
if (getDataType(tensor) == DataType_DT_INT8 || tensor->getType().bytes() == 1) {
pack = INT8_PACK_NUMBER;
}
}
size_t res = 1;
for (int i = 0; i < tensor->dimensions(); ++i) {
size_t l = tensor->length(i);
if (1 == i) {
l = UP_DIV(l, pack) * pack;
}
res *= l;
}
return res;
}
Execution* MusaBackend::onCreate(const std::vector<Tensor*>& inputs, const std::vector<Tensor*>& outputs,
const MNN::Op* op) {
auto opType = op->type();
auto creators = gCreator();
auto iter = creators->find(opType);
if (iter == creators->end()) {
if (nullptr != op->name()) {
MNN_PRINT("MusaBackend Don't support type %s, %s\n", EnumNameOpType(opType), op->name()->c_str());
} else {
MNN_PRINT("MusaBackend Don't support type %s\n", EnumNameOpType(opType));
}
return NULL;
}
auto exe = iter->second->onCreate(inputs, outputs, op, this);
if (NULL == exe) {
if (nullptr != op->name()) {
MNN_PRINT("MusaBackend The Creator Don't support type %s, %s\n", EnumNameOpType(opType), op->name()->c_str());
} else {
MNN_PRINT("MusaBackend The Creator Don't support type %s\n", EnumNameOpType(opType));
}
return NULL;
}
return exe;
}
void MusaBackend::onResizeBegin() {}
ErrorCode MusaBackend::onResizeEnd() {
return NO_ERROR;
}
void MusaBackend::onExecuteBegin() const {
mMusaRuntime->activate();
}
void MusaBackend::onExecuteEnd() const {}
void MusaBackend::onCopyBuffer(const Tensor* srcTensor, const Tensor* dstTensor) const {
auto& srcBuffer = srcTensor->buffer();
auto& dstBuffer = dstTensor->buffer();
void* src = (void*)srcBuffer.device;
void* dst = (void*)dstBuffer.device;
auto size = realSize(srcTensor) * getBytes(srcTensor);
if (nullptr != src && nullptr != dst) {
mMusaRuntime->memcpy(dst, src, size, MNNMemcpyDeviceToDevice, true);
}
}
int MusaBackend::onSync(Tensor::MapType mtype, bool toCpu, const Tensor* dstTensor) {
mMusaRuntime->device_sync();
return 0;
}
DataType MusaBackend::getDataType(const Tensor* tensor) {
auto dtype = tensor->getType();
if (dtype.code == halide_type_float && dtype.bits == 32) {
return DataType_DT_FLOAT;
} else if (dtype.code == halide_type_float && dtype.bits == 16) {
return DataType_DT_BFLOAT16; // Use BF16 as FP16 placeholder
} else if (dtype.code == halide_type_int && dtype.bits == 8) {
return DataType_DT_INT8;
}
return DataType_DT_FLOAT;
}
bool MusaBackend::addCreator(OpType t, Creator* c) {
auto creators = gCreator();
creators->insert(std::make_pair(t, c));
return true;
}
} // namespace MUSA
} // namespace MNN