source/core/Tensor.cpp (440 lines of code) (raw):

// // Tensor.cpp // MNN // // Created by MNN on 2018/07/06. // Copyright © 2018, Alibaba Group Holding Limited // #include <complex.h> #include <string.h> #include <MNN/Tensor.hpp> #include "core/Backend.hpp" #include "core/MNNMemoryUtils.h" #include "core/Macro.h" #include "core/TensorUtils.hpp" using namespace std; namespace MNN { Tensor::Tensor(int dimSize, DimensionType type) { MNN_ASSERT(dimSize <= MNN_MAX_TENSOR_DIM); mDescribe = new InsideDescribe; mDescribe->mContent.reset(new InsideDescribe::NativeInsideDescribe); auto nativeDescribe = mDescribe->mContent.get(); mBuffer.dimensions = dimSize; mBuffer.type = halide_type_of<float>(); mBuffer.device = 0; mBuffer.host = nullptr; mBuffer.dim = &nativeDescribe->dims[0]; mBuffer.flags = 0; switch (type) { case CAFFE: nativeDescribe->dimensionFormat = MNN_DATA_FORMAT_NCHW; break; case TENSORFLOW: nativeDescribe->dimensionFormat = MNN_DATA_FORMAT_NHWC; break; case CAFFE_C4: nativeDescribe->dimensionFormat = MNN_DATA_FORMAT_NC4HW4; break; default: break; } } Tensor::Tensor(const Tensor* tensor, DimensionType type, bool allocMemory) { MNN_ASSERT(tensor != nullptr); auto buffer = tensor->buffer(); mDescribe = new InsideDescribe; mDescribe->mContent.reset(new InsideDescribe::NativeInsideDescribe); auto nativeDescribe = mDescribe->mContent.get(); mBuffer.dimensions = buffer.dimensions; mBuffer.type = buffer.type; mBuffer.device = 0; mBuffer.host = nullptr; mBuffer.dim = &nativeDescribe->dims[0]; mBuffer.flags = 0; for (int i = 0; i < buffer.dimensions; ++i) { mBuffer.dim[i].extent = buffer.dim[i].extent; } switch (type) { case CAFFE: nativeDescribe->dimensionFormat = MNN_DATA_FORMAT_NCHW; break; case TENSORFLOW: nativeDescribe->dimensionFormat = MNN_DATA_FORMAT_NHWC; break; case CAFFE_C4: nativeDescribe->dimensionFormat = MNN_DATA_FORMAT_NC4HW4; type = CAFFE; break; default: break; } // format mapping auto originType = tensor->getDimensionType(); if (originType != type && buffer.dimensions >= 3) { std::vector<int> axisMap; // NCHW -> NHWC if (originType == CAFFE) { axisMap.push_back(0); for (int i = 2; i < buffer.dimensions; ++i) { axisMap.push_back(i); } axisMap.push_back(1); } // NHWC -> NCHW else { axisMap.push_back(0); axisMap.push_back(buffer.dimensions - 1); for (int i = 1; i < buffer.dimensions - 1; ++i) { axisMap.push_back(i); } } for (int i = 0; i < buffer.dimensions; ++i) { mBuffer.dim[i].extent = buffer.dim[axisMap[i]].extent; } } TensorUtils::setLinearLayout(this); for (int i = mBuffer.dimensions; i < 4; i++) { mBuffer.dim[i].extent = 1; } if (allocMemory) { auto memorySize = size(); if (memorySize > 0) { nativeDescribe->memoryType = Tensor::InsideDescribe::MEMORY_HOST; mBuffer.host = (uint8_t*)MNNMemoryAllocAlign(size(), MNN_MEMORY_ALIGN_DEFAULT); MNN_ASSERT(mBuffer.host != nullptr); } } } Tensor::Tensor(bool deepCopy, const Tensor* tensor) { mDescribe = new InsideDescribe; mDescribe->mContent = tensor->mDescribe->mContent; mDescribe->setBackend(tensor->mDescribe->getBackend()); mDescribe->mem = tensor->mDescribe->mem; mBuffer.dim = TensorUtils::getDescribe(tensor)->dims; mBuffer.type = tensor->getType(); mBuffer.device = tensor->deviceId(); mBuffer.host = tensor->buffer().host; mBuffer.dimensions = tensor->buffer().dimensions; mBuffer.flags = tensor->buffer().flags; } Tensor::~Tensor() { // MNN_PRINT("free tensor:%p\n", this); auto nativeDescribe = mDescribe->mContent.get(); if (nativeDescribe->memoryType == InsideDescribe::MEMORY_HOST) { if (nullptr != mBuffer.host) { MNNMemoryFreeAlign(mBuffer.host); } } delete mDescribe; } Tensor* Tensor::createDevice(const std::vector<int>& dims, halide_type_t type, DimensionType dimType) { auto shapeTensor = new Tensor((int)dims.size(), dimType); for (int i = 0; i < dims.size(); ++i) { shapeTensor->setLength(i, dims[i]); } shapeTensor->buffer().type = type; TensorUtils::setLinearLayout(shapeTensor); return shapeTensor; } Tensor* Tensor::create(const std::vector<int>& dims, halide_type_t type, void* userData, DimensionType dimType) { Tensor shapeTensor((int)dims.size(), dimType); for (int i = 0; i < dims.size(); ++i) { shapeTensor.setLength(i, dims[i]); } shapeTensor.buffer().type = type; bool ownData = userData == nullptr; auto result = new Tensor(&shapeTensor, dimType, ownData); if (nullptr != userData) { result->buffer().host = (uint8_t*)userData; } return result; } Tensor* Tensor::clone(const Tensor* src, bool deepCopy) { return new Tensor(deepCopy, src); } bool Tensor::copyFromHostTensor(const Tensor* hostTensor) { auto bn = mDescribe->getBackend(); if (nullptr == bn) { return false; } auto hostbn = hostTensor->mDescribe->getBackend(); std::shared_ptr<Tensor> tmpTensor; if (nullptr != hostbn && hostbn->type() != bn->type() && hostbn->type() != MNN_FORWARD_CPU) { tmpTensor.reset(new Tensor(hostTensor, hostTensor->getDimensionType())); hostTensor->copyToHostTensor(tmpTensor.get()); hostTensor = tmpTensor.get(); } bn->onCopyBuffer(hostTensor, this); return true; } bool Tensor::copyToHostTensor(Tensor* hostTensor) const { auto bn = mDescribe->getBackend(); if (nullptr == bn) { return false; } bn->onCopyBuffer(this, hostTensor); return true; } Tensor* Tensor::createHostTensorFromDevice(const Tensor* device, bool copyContent) { auto tensor = Tensor::create(device->shape(), device->getType(), nullptr, TensorUtils::getDimType(device)); if (copyContent) { device->copyToHostTensor(tensor); } return tensor; } Tensor::DimensionType Tensor::getDimensionType() const { auto nativeDescribe = mDescribe->mContent.get(); if (nativeDescribe->dimensionFormat == MNN_DATA_FORMAT_NHWC) { return Tensor::TENSORFLOW; } return Tensor::CAFFE; } Tensor::HandleDataType Tensor::getHandleDataType() const { if (halide_type_handle != mBuffer.type.code) { return HANDLE_NONE; } return HANDLE_STRING; } void Tensor::setType(int type) { auto nativeDescribe = mDescribe->mContent.get(); switch (type) { case DataType_DT_DOUBLE: case DataType_DT_FLOAT: mBuffer.type = halide_type_of<float>(); break; case DataType_DT_BFLOAT16: mBuffer.type = halide_type_t(halide_type_bfloat, 16); break; case DataType_DT_QINT32: case DataType_DT_INT32: case DataType_DT_BOOL: case DataType_DT_INT64: mBuffer.type = halide_type_of<int32_t>(); break; case DataType_DT_QINT8: case DataType_DT_INT8: mBuffer.type = halide_type_of<int8_t>(); break; case DataType_DT_QUINT8: case DataType_DT_UINT8: mBuffer.type = halide_type_of<uint8_t>(); break; case DataType_DT_QUINT16: case DataType_DT_UINT16: mBuffer.type = halide_type_of<uint16_t>(); break; case DataType_DT_QINT16: case DataType_DT_INT16: mBuffer.type = halide_type_of<int16_t>(); break; default: MNN_PRINT("Unsupported data type! %d\n", type); MNN_ASSERT(false); break; } } std::vector<int> Tensor::shape() const { std::vector<int> result; for (int i = 0; i < mBuffer.dimensions; ++i) { result.push_back(mBuffer.dim[i].extent); } return result; } template <typename T> void printData(const Tensor* tensor, const void* data, const char* fmt) { const T* buffer = (const T*)data; if (tensor->dimensions() != 4) { auto size = tensor->elementSize(); for (int i = 0; i < size; i++) { MNN_PRINT(fmt, buffer[i]); } MNN_PRINT("\n"); return; } auto tf = tensor->getDimensionType() == Tensor::TENSORFLOW; auto batch = tensor->batch(); auto channel = tensor->channel(); auto height = tensor->height(); auto width = tensor->width(); auto unit = sizeof(T); if (tf) { auto bytesPerRow = channel * unit; auto bytesPerImage = width * bytesPerRow; auto bytesPerBatch = height * bytesPerImage; for (int b = 0; b < batch; b++) { auto bytes = buffer + b * bytesPerBatch / unit; MNN_PRINT("batch %d:\n", b); for (int h = 0; h < height; h++) { for (int w = 0; w < width; w++) { for (int c = 0; c < channel; c++) { MNN_PRINT(fmt, bytes[h * width * channel + w * channel + c]); } MNN_PRINT("\n"); } MNN_PRINT("--------------\n"); } } } else if (TensorUtils::getDescribe(tensor)->dimensionFormat == MNN_DATA_FORMAT_NC4HW4) { // NC/4HW4 auto components = 4; auto bytesPerRow = width * components * unit; auto bytesPerImage = height * bytesPerRow; auto bytesPerBatch = UP_DIV(channel, 4) * bytesPerImage; for (int b = 0; b < batch; b++) { auto bytes = buffer + b * bytesPerBatch / unit; MNN_PRINT("batch %d:\n", b); for (int c = 0; c < channel; c++) { for (int h = 0; h < height; h++) { for (int w = 0; w < width; w++) { auto n = c / components, r = c % components; MNN_PRINT(fmt, bytes[(n * width * height + h * width + w) * components + r]); } MNN_PRINT("\n"); } MNN_PRINT("--------------\n"); } } } else { // NCHW auto bytesPerRow = width * unit; auto bytesPerImage = height * bytesPerRow; auto bytesPerBatch = channel * bytesPerImage; for (int b = 0; b < batch; b++) { auto bytes = buffer + b * bytesPerBatch / unit; MNN_PRINT("batch %d:\n", b); for (int c = 0; c < channel; c++) { for (int h = 0; h < height; h++) { for (int w = 0; w < width; w++) { MNN_PRINT(fmt, bytes[c * width * height + h * width + w]); } MNN_PRINT("\n"); } MNN_PRINT("--------------\n"); } } } } void Tensor::print() const { // print dimensions MNN_PRINT("====== Tensor %p ======", this); MNN_PRINT("\nDimension: "); for (int i = 0; i < mBuffer.dimensions; i++) { MNN_PRINT("%d, ", mBuffer.dim[i].extent); } // convert to host if needed auto printee = this; bool device = this->buffer().host == NULL && this->buffer().device != 0; if (device) { printee = this->createHostTensorFromDevice(this, true); } auto buffer = printee->buffer().host; MNN_PRINT("\nData: "); if (printee->getType().code == halide_type_int) { if (printee->getType().bits == 8) { // int8 printData<int8_t>(printee, buffer, "%d, "); } else if (printee->getType().bits == 16) { // int16 printData<int16_t>(printee, buffer, "%d, "); } else if (printee->getType().bits == 32) { // int32 printData<int32_t>(printee, buffer, "%d, "); } else { MNN_PRINT("\nunsupported data type"); } } else if (printee->getType().code == halide_type_uint) { if (printee->getType().bits == 8) { // uint8 printData<uint8_t>(printee, buffer, "%d, "); } else { MNN_PRINT("\nunsupported data type"); } } else if (printee->getType().code == halide_type_float) { if (printee->getType().bits == 32) { // float32 printData<float>(printee, buffer, "%f, "); } else { MNN_PRINT("\nunsupported data type\n"); } } else { MNN_PRINT("\nunsupported data type"); } // clean up if (printee != this) { delete printee; } } void Tensor::printShape() const { const int dims = this->dimensions(); MNN_PRINT("\t**Tensor shape**: "); if (dims == 0) { MNN_PRINT("\t*Scalar*"); } for (int i = 0; i < dims; ++i) { MNN_PRINT("%d, ", this->length(i)); } MNN_PRINT("\n"); } size_t Tensor::usize() const { size_t dataSize = mBuffer.type.bytes(); MNN_ASSERT(dataSize >= 1); auto nativeDescribe = mDescribe->mContent.get(); for (int i = 0; i < this->buffer().dimensions; i++) { int currentDimSize = mBuffer.dim[i].extent; if (nativeDescribe->dimensionFormat == MNN_DATA_FORMAT_NC4HW4 && 1 == i) { currentDimSize = ALIGN_UP4(currentDimSize); } dataSize *= currentDimSize; } return dataSize; } int Tensor::size() const { return static_cast<int>(usize()); } void* Tensor::map(MapType mtype, DimensionType dtype) { auto nativeDescribe = mDescribe; auto bn = nativeDescribe->getBackend(); if (nullptr == bn) { return mBuffer.host; } auto mapPtr = bn->onMapTensor(mtype, dtype, this); if(mapPtr != nullptr) { // Get mapPtr in specific backend return mapPtr; } /* Common backend */ auto needSize = this->size(); void* hostPtr = malloc(needSize); if(mtype == Tensor::MAP_TENSOR_READ) { //tmpTensor alloc MNN::Tensor tmpTensor(this, dtype, false); tmpTensor.buffer().host = (uint8_t *)hostPtr; //use onCopyBuffer bn->onCopyBuffer(this, &tmpTensor); } return hostPtr; } void Tensor::unmap(MapType mtype, DimensionType dtype, void *mapPtr) { auto nativeDescribe = mDescribe; auto bn = nativeDescribe->getBackend(); if (nullptr == bn) { return; } bool ret = bn->onUnmapTensor(mtype, dtype, this, mapPtr); if(true == ret) { //do unmap already, just return return; } if(mtype == Tensor::MAP_TENSOR_WRITE) { //srcTensor alloc MNN::Tensor srcTensor(this, dtype, false); srcTensor.buffer().host = (uint8_t *)mapPtr; //use onCopyBuffer bn->onCopyBuffer(&srcTensor, this); } if(mapPtr != nullptr) { free(mapPtr); mapPtr = nullptr; } } int Tensor::wait(MapType mtype, bool finish) { auto nativeDescribe = mDescribe; auto bn = nativeDescribe->getBackend(); if (nullptr == bn) { return 0; } return bn->onSync(mtype, finish, this); } bool Tensor::setDevicePtr(const void* devicePtr, int memoryType) { mBuffer.flags = memoryType; mBuffer.device = (uint64_t)devicePtr; // To use memoryType afterwards return true; } void Tensor::destroy(Tensor* tensor) { if (nullptr != tensor) { delete tensor; } } bool Tensor::getDeviceInfo(void* dst, int type) const { auto nativeDescribe = mDescribe; if (nullptr == nativeDescribe->getBackend()) { return false; } if (nativeDescribe->getBackend()->type() != type) { return false; } return nativeDescribe->getBackend()->onGetTensorInfo(this, dst); } } // namespace MNN