项目文件夹

文件
2026-07-13 13:33:03 +08:00

620 行
25 KiB
Plaintext

//
// MetalConvolutionCommon.mm
// MNN
//
// Created by MNN on 2019/02/25.
// Copyright © 2018, Alibaba Group Holding Limited
//
#import "backend/metal/MetalConvolutionCommon.hpp"
#import "core/Macro.h"
#import "backend/metal/MetalBackend.hpp"
#import "backend/metal/MetalConvolution1x1.hpp"
#import "backend/metal/MetalConvolutionWinograd.hpp"
#import "core/TensorUtils.hpp"
#include "core/OpCommonUtils.hpp"
#if MNN_METAL_ENABLED
namespace MNN {
static const char* gWeightTrans = R"metal(
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct weight_shape {
int group;
int goc;
int goc_4;
int gic;
int gic_4;
int kh;
int kw;
};
kernel void int4_weight_transform_fast(const device uint8_t* src [[buffer(0)]],
device uint8_t* dst [[buffer(1)]],
constant weight_shape &uConstant [[buffer(2)]],
uint3 gid [[thread_position_in_grid]]
) {
if ((int)gid.x < uConstant.goc && (int)gid.y < uConstant.gic) {
auto zo = gid.x / 4, ro = gid.x % 4;
auto zi = gid.y / 4, ri = gid.y % 4;
dst[((zo * uConstant.gic_4 + zi) * 16 + ro * 4 + ri) / 2] = src[(gid.x * uConstant.gic + gid.y) / 2];
}
}
kernel void int4_weight_transform_c4_fast(const device uint16_t* src [[buffer(0)]],
device uint16_t* dst [[buffer(1)]],
constant weight_shape &uConstant [[buffer(2)]],
uint3 gid [[thread_position_in_grid]]
) {
if ((int)gid.x < uConstant.goc && (int)gid.y < uConstant.gic_4) {
auto zo = gid.x / 4, ro = gid.x % 4;
dst[(zo * uConstant.gic_4 + gid.y) * 4 + ro] = src[gid.x * uConstant.gic_4 + gid.y];
}
}
kernel void weight_transform_common(const device IType* src [[buffer(0)]],
device OType* dst [[buffer(1)]],
constant weight_shape &uConstant [[buffer(2)]],
uint3 gid [[thread_position_in_grid]]
) {
if ((int)gid.x < uConstant.group * uConstant.goc && (int)gid.y < uConstant.gic && (int)gid.z < uConstant.kh * uConstant.kw) {
auto g = gid.x / uConstant.goc;
auto goc = gid.x % uConstant.goc;
auto zo = goc / 4, ro = goc % 4;
auto zi = gid.y / 4, ri = gid.y % 4;
auto h = gid.z / uConstant.kw;
auto w = gid.z % uConstant.kw;
// to [g][o/4][i/4][h][w][16]
// from [g][o][i][h][w]
int dx = g * uConstant.goc_4 * uConstant.gic_4 * uConstant.kh * uConstant.kw * 16 + zo * uConstant.gic_4 * uConstant.kh * uConstant.kw * 16 + ro * 4 + zi * uConstant.kh * uConstant.kw * 16 + ri + (h * uConstant.kw + w) * 16;
int sx = (gid.x * uConstant.gic + gid.y) * uConstant.kh * uConstant.kw + gid.z;
dst[dx] = (OType)src[sx];
}
}
)metal";
static std::shared_ptr<MNN::Tensor> biasForConv(Backend *bn, const Op* op, const Convolution2D *conv, bool fp16) {
auto bias = conv->bias();
auto oc = conv->common()->outputCount();
int bytes = fp16 ? 2 : 4;
auto bias_size_unit = UP_DIV(oc, 16) * 16;
std::shared_ptr<MNN::Tensor> t(MNN::Tensor::createDevice<float>({bias_size_unit}));
auto res = bn->onAcquireBuffer(t.get(), Backend::STATIC);
if (!res) {
return nullptr;
}
const bool useCachedMmap = (bn->getRuntime() && bn->getRuntime()->hint().useCachedMmap > 1);
if (useCachedMmap) {
return t;
}
auto buffer = MetalBackend::getBuffer(t.get());
auto dstOrigin = (uint8_t*)[buffer.first contents] + buffer.second;
::memset(dstOrigin, 0, bias_size_unit * bytes);
const float* src = nullptr;
std::unique_ptr<float[]> externalBias;
if (nullptr != bias && bias->size() >= oc) {
src = bias->data();
} else if (nullptr != op && nullptr != op->externalPath() && USE_EXTERNAL_DATA(conv) && nullptr != conv->external() && conv->external()->size() >= 3 && conv->external()->data()[2] > 0) {
auto externalInfo = conv->external()->data();
size_t biasBytes = externalInfo[2];
size_t expectedBytes = (size_t)oc * sizeof(float);
if (biasBytes < expectedBytes) {
return t;
}
externalBias.reset(new float[oc]);
std::unique_ptr<FileLoader> external(new FileLoader(op->externalPath()->c_str()));
external->offset(externalInfo[0] + externalInfo[1]);
external->read((char*)externalBias.get(), expectedBytes);
src = externalBias.get();
}
if (nullptr == src) {
return t;
}
if (fp16) {
auto dst = (__fp16 *)dstOrigin;
#pragma clang loop vectorize(enable) unroll(enable)
for (int i = 0; i < oc; i++) {
dst[i] = src[i];
}
} else {
::memcpy(dstOrigin, src, oc * sizeof(float));
}
return t;
}
MetalConvolutionCommon::MetalConvolutionCommon(Backend *backend, const MNN::Op *op, std::shared_ptr<MNN::Tensor> bias) : MetalExecution(backend) {
auto mtbn = static_cast<MetalBackend*>(backend);
auto conv = op->main_as_Convolution2D();
auto common = conv->common();
mOp = op;
mKernelX = common->kernelX();
mKernelY = common->kernelY();
mStrideX = common->strideX();
mStrideY = common->strideY();
mDilateX = common->dilateX();
mDilateY = common->dilateY();
if (nullptr != bias) {
mBias = bias;
} else {
mBias = biasForConv(backend, op, conv, mtbn->useFp16InsteadFp32());
}
mActivationType = common->relu() ? 1 : (common->relu6() ? 2 : 0);
if (nullptr == mBias) {
mValid = false;
}
}
template <typename FType, typename TType>
void MetalConvolutionCommon::convertWeightFormat(int group, int oc, int ic, int kh, int kw, const FType *src, TType* dstOrigion, Tensor* dstTensor, id<MTLBuffer> srcGpuBuffer) {
auto goc = oc / group;
auto gic = ic / group;
auto goc_4 = UP_DIV(goc, 4);
auto gic_4 = UP_DIV(gic, 4);
auto backend = static_cast<MetalBackend *>(this->backend());
auto context = (__bridge MNNMetalContext *)static_cast<MetalBackend *>(backend)->context();
if(srcGpuBuffer == nil) {
srcGpuBuffer = [context newDeviceBuffer:group * goc * gic * kh * kw * sizeof(FType) access:CPUReadWrite];
::memcpy((void *)srcGpuBuffer.contents, (void *)src, group * goc * gic * kh * kw * sizeof(FType));
}
std::string IType = "float";
std::string OType = "float";
if(std::is_same<FType, int8_t>::value) {
IType = "int8_t";
}
if(std::is_same<TType, __fp16>::value) {
OType = "half";
} else if(std::is_same<TType, int8_t>::value) {
OType = "int8_t";
}
MTLCompileOptions *option = [[MTLCompileOptions alloc] init];
auto dic = [NSMutableDictionary dictionaryWithCapacity:0];
[dic setValue:@(IType.c_str()) forKey:@"IType"];
[dic setValue:@(OType.c_str()) forKey:@"OType"];
option.preprocessorMacros = dic;
// create const buffer
int constants[] = {group,
goc,
goc_4,
gic,
gic_4,
kh,
kw};
auto constBuffer = backend->getConstBuffer(sizeof(constants));
::memcpy(constBuffer.contents, constants, sizeof(constants));
auto encoder = [backend->getCommandBufferForBufferCopy() computeCommandEncoder];
auto pipeline = backend->makeComputePipelineWithSourceOption(gWeightTrans, "weight_transform_common", option);
[encoder setComputePipelineState:pipeline];
[encoder setBuffer:srcGpuBuffer offset:0 atIndex:0];
MetalBackend::setTensor(dstTensor, encoder, 1);
[encoder setBuffer:constBuffer offset:0 atIndex:2];
auto gl = [context computeBestGroupAndLocal:pipeline threads:MTLSizeMake((NSInteger)group * goc, (NSInteger)gic, (NSInteger)kh * kw)];
[encoder dispatchThreadgroups:gl.first threadsPerThreadgroup:gl.second];
[encoder endEncoding];
// just commit, don‘t wait for not block
backend->commit();
}
template<typename DType>
static std::pair<std::shared_ptr<MNN::Tensor>, float> getDequantScale(const float* scale, int size, MetalBackend *backend, bool asymmetric, int oc) {
int totalCount = 0;
if (asymmetric) {
totalCount = size / 2;
} else {
totalCount = size;
}
int blockSize = totalCount / oc;
int alignOutputCount = ALIGN_UP4(oc);
std::shared_ptr<MNN::Tensor> dequantScale(MNN::Tensor::createDevice<uint8_t>({alignOutputCount * blockSize * (int)(sizeof(DType) * 2) + (int)sizeof(float)}));
bool res = backend->onAcquireBuffer(dequantScale.get(), Backend::STATIC);
if (!res) {
MNN_ERROR("Buffer allocated error!\n");
return std::make_pair(nullptr, 1.0);
}
auto buffer0 = MetalBackend::getBuffer(dequantScale.get());
DType* dst_scale = (DType*)((uint8_t*)[buffer0.first contents] + buffer0.second);
auto coefPtr = (float*)((uint8_t*)dst_scale + alignOutputCount * blockSize * (int)(sizeof(DType) * 2));
if (backend->getRuntime()->hint().useCachedMmap > 1) {
return std::make_pair(dequantScale, *coefPtr);
}
::memset(dst_scale, 0, dequantScale->usize());
float coef = 1.0;
if(std::is_same<DType, __fp16>::value) {
float max_data = 0.0;
if(asymmetric) {
for (int z=0; z<oc; ++z) {
auto srcZ = scale + z * blockSize * 2;
for (int bi=0; bi<blockSize; ++bi) {
float s = fabs(srcZ[2*bi+1]);
float b = fabs(srcZ[2*bi+0]);
float temp = ALIMAX(s, b);
if(temp > max_data) {
max_data = temp;
}
}
}
} else {
for (int z=0; z<oc; ++z) {
auto srcZ = scale + z * blockSize;
for (int bi=0; bi<blockSize; ++bi) {
float s = srcZ[bi];
if(s > max_data) {
max_data = s;
}
}
}
}
// too big scale may cause half precision loss
coef = 1000.0 / max_data;
}
if (asymmetric) {
for (int z=0; z<oc; ++z) {
int zo = z / 4;
int zi = z % 4;
auto srcZ = scale + z * blockSize * 2;
auto dstSZ = dst_scale + zo * blockSize * 8 + zi;
auto dstBZ = dst_scale + zo * blockSize * 8 + zi + 4;
for (int bi=0; bi<blockSize; ++bi) {
float s = srcZ[2*bi+1];
float b = srcZ[2*bi+0];
dstSZ[bi * 8] = (DType)(s * coef);
dstBZ[bi * 8] = (DType)(b * coef);
}
}
} else {
for (int z=0; z<oc; ++z) {
int zo = z / 4;
int zi = z % 4;
auto srcZ = scale + z * blockSize;
auto dstSZ = dst_scale + zo * blockSize * 8 + zi;
auto dstBZ = dst_scale + zo * blockSize * 8 + zi + 4;
for (int bi=0; bi<blockSize; ++bi) {
float s = srcZ[bi];
float b = 0.0f;
dstSZ[bi * 8] = (DType)(s * coef);
dstBZ[bi * 8] = b;
}
}
}
*coefPtr = coef;
return std::make_pair(dequantScale, coef);
}
void MetalConvolutionCommon::loadWeight(const MNN::Op *op, bool loadWeightInt8) {
auto conv = op->main_as_Convolution2D();
auto common = conv->common();
auto kw = common->kernelX();
auto kh = common->kernelY();
auto group = common->group();
auto oc = common->outputCount();
int ic = common->inputCount();
void* weightMemPtr = nullptr;
id<MTLBuffer> srcGpuBuffer = nil;
auto useOriginMmap = backend()->getRuntime()->hint().useCachedMmap > 1;
bool preAllocGpuMem = ic != 0 && conv->quanParameter();
int quantBit;
// only for weight int4/int8 now.
if(loadWeightInt8) {
quantBit = conv->quanParameter()->aMaxOrBits();
// 3.1.2 and after has aMaxOrBits for quant bits
if (quantBit == 0) {
// support old model for external weight file with int4/int8 quant
quantBit = ConvolutionCommon::getQuantBitFromExternalFile(op);
}
if(quantBit != 4 && quantBit != 8) {
preAllocGpuMem = false;
}
}
if (preAllocGpuMem && (!useOriginMmap)) {
size_t size = oc * ic * kh * kw / group;
if (loadWeightInt8) {
if(quantBit == 4) {
size = UP_DIV(size, 2);
}
} else {
size *= sizeof(float);
}
auto backend = static_cast<MetalBackend *>(this->backend());
auto context = (__bridge MNNMetalContext *)static_cast<MetalBackend *>(backend)->context();
srcGpuBuffer = [context newDeviceBuffer:size access:CPUReadWrite];
}
std::shared_ptr<ConvolutionCommon::Int8Common> qnt = NULL;
if (loadWeightInt8) {
qnt = ConvolutionCommon::load(op, backend(), false, true, (void *)srcGpuBuffer.contents);
} else if (conv->quanParameter()) {
qnt = ConvolutionCommon::load(op, backend(), true, false, (void *)srcGpuBuffer.contents);
}
// param
size_t size = 0;
if (ic > 0) {
size = oc * ic * kh * kw / group;
} else {
size = qnt ? MAX(qnt->weight.size(), qnt->weightFloat.size()) : conv->weight()->size();
ic = size / kw / kh / (oc / group);
}
// convert
if (loadWeightInt8) {
auto backend = static_cast<MetalBackend *>(this->backend());
bool useInt2 = qnt->canUseInt2;
bool useInt3 = qnt->canUseInt3;
bool int4Path = qnt->canUseInt4 && !useInt2 && !useInt3;
bool int8Path = !int4Path && !useInt2 && !useInt3;
int subBits = useInt2 ? 2 : (useInt3 ? 3 : 0);
mWeight = weightTransform(group, oc, ic, kh, kw, (float*)qnt->weight.get(), int8Path, int4Path, srcGpuBuffer, subBits);
if(backend->useFp16InsteadFp32()) {
auto dequantParams = getDequantScale<__fp16>(qnt->alpha.get(), qnt->alphaSize, backend, qnt->asymmetric, oc);
mDequantScaleBias = dequantParams.first;
mScaleCoef = dequantParams.second;
} else {
auto dequantParams = getDequantScale<float>(qnt->alpha.get(), qnt->alphaSize, backend, qnt->asymmetric, oc);
mDequantScaleBias = dequantParams.first;
mScaleCoef = dequantParams.second;
}
mDequantBits = useInt2 ? 2 : (useInt3 ? 3 : (int4Path ? 4 : 8));
} else if (qnt && qnt->weightFloat.get()) {
mWeight = weightTransform(group, oc, ic, kh, kw, qnt->weightFloat.get(), false, false, srcGpuBuffer);
} else {
const float* src = nullptr;
std::unique_ptr<float[]> externalWeight;
if (nullptr != conv->weight() && conv->weight()->size() > 0) {
src = conv->weight()->data();
} else {
const bool useCachedMmap = (backend()->getRuntime() && backend()->getRuntime()->hint().useCachedMmap > 1);
if (!useCachedMmap && nullptr != op->externalPath() && USE_EXTERNAL_DATA(conv) && nullptr != conv->external() && conv->external()->size() >= 2 && conv->external()->data()[1] > 0) {
auto externalInfo = conv->external()->data();
size_t weightBytes = externalInfo[1];
size_t expectedBytes = size * sizeof(float);
if (weightBytes < expectedBytes) {
mValid = false;
return;
}
externalWeight.reset(new float[size]);
std::unique_ptr<FileLoader> external(new FileLoader(op->externalPath()->c_str()));
external->offset(externalInfo[0]);
external->read((char*)externalWeight.get(), expectedBytes);
src = externalWeight.get();
}
}
mWeight = weightTransform(group, oc, ic, kh, kw, src, false, false, srcGpuBuffer);
}
}
std::shared_ptr<MNN::Tensor> MetalConvolutionCommon::weightTransform(int group, int oc, int ic, int kh, int kw, const float *src, bool int8Weight, bool int4Weight, id<MTLBuffer> srcGpuBuffer, int subBits) {
if(srcGpuBuffer != nil) {
MNN_ASSERT((void*)src == (void*)srcGpuBuffer.contents);
}
auto backend = static_cast<MetalBackend *>(this->backend());
auto context = (__bridge MNNMetalContext *)static_cast<MetalBackend *>(backend)->context();
auto goc = oc / group;
auto gic = ic / group;
auto goc_4 = UP_DIV(goc, 4);
auto gic_4 = UP_DIV(gic, 4);
auto weight_len = group * ROUND_UP(goc_4, 4) * gic_4 * kw * kh * 16;
auto ori_len = group * goc * gic * kh * kw;
bool needMemset = (goc % 4 != 0 || gic % 4 != 0);
#ifdef MNN_LOW_MEMORY
if (subBits == 3) {
// 3-bit packed: 6 bytes / (4 OC, 4 IC) tile.
size_t weight_bytes = (size_t)group * goc_4 * gic_4 * kh * kw * 6;
std::shared_ptr<MNN::Tensor> weightLow(MNN::Tensor::createDevice<int8_t>({(int)weight_bytes}));
if (!backend->onAcquireBuffer(weightLow.get(), Backend::STATIC)) {
MNN_ERROR("Memory alloc error!\n");
return nullptr;
}
if (nil == src) {
return weightLow;
}
auto buf = MetalBackend::getBuffer(weightLow.get());
auto dstPtr = (uint8_t*)[buf.first contents] + buf.second;
::memset(dstPtr, 0, weight_bytes);
auto srcPtr = (const int8_t*)src;
for (int g = 0; g < group; g++) {
for (int o = 0; o < goc; o++) {
int zo = o / 4, ro = o % 4;
for (int i = 0; i < gic; i++) {
int zi = i / 4, ri = i % 4;
for (int h = 0; h < kh; h++) {
for (int w = 0; w < kw; w++) {
int srcIdx = ((g * goc + o) * gic + i) * kh * kw + h * kw + w;
int sv = (int)srcPtr[srcIdx] + 4;
int tileBase = (((g * goc_4 + zo) * gic_4 + zi) * kh + h) * kw * 6 + w * 6;
dstPtr[tileBase + ro] |= (uint8_t)((sv & 3) << (6 - ri * 2));
int hiByte = tileBase + 4 + (ro / 2);
int hiShift = (ro % 2 == 0 ? 4 : 0) + (3 - ri);
dstPtr[hiByte] |= (uint8_t)(((sv >> 2) & 1) << hiShift);
}
}
}
}
}
return weightLow;
}
if (subBits == 2) {
// 2-bit packed: 4 bytes / (4 OC, 4 IC) tile.
size_t weight_bytes = (size_t)group * goc_4 * gic_4 * kh * kw * 4;
std::shared_ptr<MNN::Tensor> weightLow(MNN::Tensor::createDevice<int8_t>({(int)weight_bytes}));
if (!backend->onAcquireBuffer(weightLow.get(), Backend::STATIC)) {
MNN_ERROR("Memory alloc error!\n");
return nullptr;
}
if (nil == src) {
return weightLow;
}
auto buf = MetalBackend::getBuffer(weightLow.get());
auto dstPtr = (uint8_t*)[buf.first contents] + buf.second;
::memset(dstPtr, 0, weight_bytes);
auto srcPtr = (const int8_t*)src;
for (int g = 0; g < group; g++) {
for (int o = 0; o < goc; o++) {
int zo = o / 4, ro = o % 4;
for (int i = 0; i < gic; i++) {
int zi = i / 4, ri = i % 4;
for (int h = 0; h < kh; h++) {
for (int w = 0; w < kw; w++) {
int srcIdx = ((g * goc + o) * gic + i) * kh * kw + h * kw + w;
int sv = (int)srcPtr[srcIdx] + 2;
int tileBase = (((g * goc_4 + zo) * gic_4 + zi) * kh + h) * kw * 4 + w * 4;
dstPtr[tileBase + ro] |= (uint8_t)((sv & 3) << (6 - ri * 2));
}
}
}
}
}
return weightLow;
}
if (int4Weight) {
weight_len = UP_DIV(weight_len, 2);
std::shared_ptr<MNN::Tensor> weightLow(MNN::Tensor::createDevice<int8_t>({weight_len}));
auto res = backend->onAcquireBuffer(weightLow.get(), Backend::STATIC);
if (!res) {
MNN_ERROR("Memory alloc error!\n");
return nullptr;
}
if (nil == src) {
// Use mmap weight. No need to compute
return weightLow;
}
auto buf = MetalBackend::getBuffer(weightLow.get());
auto dstPtr = (uint8_t*)[buf.first contents] + buf.second;
if(needMemset) {
::memset(dstPtr, 0, weight_len);
}
bool fastBlit = (group == 1 && kh == 1 && kw == 1 && ic % 2 == 0);
auto oc_4 = UP_DIV(oc, 4);
auto ic_4 = UP_DIV(ic, 4);
// fast int4 reorder
if (fastBlit) {
if(srcGpuBuffer == nil) {
srcGpuBuffer = [context newDeviceBuffer:UP_DIV(ori_len, 2) access:CPUReadWrite];
::memcpy((void *)srcGpuBuffer.contents, (void *)src, UP_DIV(ori_len, 2));
}
MTLCompileOptions *option = [[MTLCompileOptions alloc] init];
auto dic = [NSMutableDictionary dictionaryWithCapacity:0];
[dic setValue:@"uint8_t" forKey:@"IType"];
[dic setValue:@"uint8_t" forKey:@"OType"];
option.preprocessorMacros = dic;
// create const buffer
int constants[] = {group,
goc,
goc_4,
gic,
gic_4,
kh,
kw};
auto constBuffer = backend->getConstBuffer(sizeof(constants));
::memcpy(constBuffer.contents, constants, sizeof(constants));
auto encoder = [backend->getCommandBufferForBufferCopy() computeCommandEncoder];
id<MTLComputePipelineState> pipeline;
bool c4_fast = ic % 4 == 0;
if(c4_fast) {
pipeline = backend->makeComputePipelineWithSourceOption(gWeightTrans, "int4_weight_transform_c4_fast", option);
} else {
pipeline = backend->makeComputePipelineWithSourceOption(gWeightTrans, "int4_weight_transform_fast", option);
}
[encoder setComputePipelineState:pipeline];
[encoder setBuffer:srcGpuBuffer offset:0 atIndex:0];
MetalBackend::setTensor(weightLow.get(), encoder, 1);
[encoder setBuffer:constBuffer offset:0 atIndex:2];
MTLSize totalThread;
if(c4_fast) {
totalThread = MTLSizeMake((NSInteger)goc, (NSInteger)gic_4, (NSInteger)1);
} else {
totalThread = MTLSizeMake((NSInteger)goc, (NSInteger)gic, (NSInteger)1);
}
auto gl = [context computeBestGroupAndLocal:pipeline threads:totalThread];
[encoder dispatchThreadgroups:gl.first threadsPerThreadgroup:gl.second];
[encoder endEncoding];
// just commit, don‘t wait for not block
backend->commit();
} else {
auto srcPtr = (int8_t*)src;
// slow int4 reorder
int sx = 0;
auto goc_4 = UP_DIV(goc, 4);
auto gic_4 = UP_DIV(gic, 4);
for (int g = 0; g < group; g++) {
for (int o = 0; o < goc; o++) {
auto zo = o / 4, ro = o % 4;
for (int i = 0; i < gic; i++) {
auto zi = i / 4, ri = i % 4;
for (int h = 0; h < kh; h++) {
for (int w = 0; w < kw; w++) {
// to [g][o/4][i/4][h][w][16]
// from [g][o][i][h][w]
int dx = g * goc_4 * gic_4 * kh * kw * 16 + zo * gic_4 * kh * kw * 16 + ro * 4 + zi * kh * kw * 16 + ri + (h * kw + w) * 16;
uint8_t s = srcPtr[sx/2];
s = (sx % 2) ? (s & 0xf) : (s >> 4);
s = (dx % 2) ? s : (s << 4);
dstPtr[dx/2] |= s;
sx++;
}
}
}
}
}
}
return weightLow;
}
#endif
std::shared_ptr<MNN::Tensor> t(MNN::Tensor::createDevice<float>({weight_len}));
if (int8Weight || int4Weight) {
t.reset(MNN::Tensor::createDevice<int8_t>({weight_len}));
}
bool res = backend->onAcquireBuffer(t.get(), Backend::STATIC);
if (!res) {
return nullptr;
}
if (nullptr == src) {
// No need to compute
return t;
}
auto buffer = MetalBackend::getBuffer(t.get());
auto dst = (uint8_t*)[buffer.first contents] + buffer.second;
if (int8Weight) {
if(needMemset) {
::memset(dst, 0, weight_len);
}
convertWeightFormat<int8_t, int8_t>(group, oc, ic, kh, kw, (const int8_t*)src, (int8_t *)dst, t.get(), srcGpuBuffer);
} else if (backend->useFp16InsteadFp32()) {
if(needMemset) {
::memset(dst, 0, weight_len * sizeof(__fp16));
}
convertWeightFormat<float, __fp16>(group, oc, ic, kh, kw, (const float*)src, (__fp16 *)dst, t.get(), srcGpuBuffer);
} else {
if(needMemset) {
::memset(dst, 0, weight_len * sizeof(float));
}
convertWeightFormat<float, float>(group, oc, ic, kh, kw, (const float*)src, (float *)dst, t.get(), srcGpuBuffer);
}
return t;
}
} // namespace MNN
#endif