char类型是多少 mat_ncnn初探二: 图解ncnn::Mat的内存排布

本文介绍了ncnn库中Mat类的内存布局,包括数据成员如data、refcount、dims等的含义和作用,通过图解展示了不同维度和elempack值的Mat内存组织方式,帮助理解Mat如何存储和管理数据。
摘要由CSDN通过智能技术生成

1. 读完本章你可以学到的知识点

  • 熟悉ncnn::Mat的内存排布

2. ncnn::Mat的实现以及内存排布

  • 首先, 在c++里我们看到一个类, 一般都想知道这个类是要做什么的, 有什么功能, 简单的说类就是封装了一些函数和一些数据. ncnn::Mat这个类不涉及继承, 它在ncnn里扮演的角色就是数据的表示. 通常来说在阅读一个我首次见到的class时, 都是从它的构造函数, 数据成员, 析构函数开始看的.
  • 我们先来看构造函数, 构造函数一般来说就是初始化类的数据成员.
  • 下面的代码块, 我单独拎出来了其中的一个构造函数, 可以看到这个构造函数的实现是: 先初始化各个数据成员为0, data(0),refcount(0),elemsize(0),elempack(0),allocator(0),dims(0),w(0),h(0),c(0),cstep(0)然后调用create函数, 再重新初始化这些数据成员.
  • 下面分别解释一下各个数据成员的含义
  • data: 表示Mat分配的内存的头地址, 是一个指针类型
  • refcount: 表示Mat的引用计数, 是一个指针类型
  • allocator: 本章我们不太关系这个变量可以认为它的值始终为0, 是一个指针类型
  • dims: 表示数据的维度, 是一个整数类型
  • w: 表示数据的width, 是一个整数类型
  • h: 表示数据的height, 是一个整数类型
  • c: 表示数据的channel, 是一个整数类型
  • elempack: 表示有多少个数据打包在一起, 是一个整数类型
  • elemsize: 表示打包在一起的数据占的字节数, 是一个整数类型
  • cstep: 表示channel step, 即走一个channel跨过的字节数, 是一个整数类型
  • 是不是感觉好复杂, 怎么多的数据成员, 我怎么看的过来呢. 是的初看确实很多, 但是不要慌, 怎么多的变量是为了更加细粒度的控制数据的表示, 到后面你会发现这些数据成员都是那么自然.
// the three dimension matrix
class Mat {
public:
    // dim
    Mat(int w, int h, int c, size_t elemsize = 4u, Allocator* allocator = 0);
}

inline Mat::Mat(int _w, int _h, int _c, size_t _elemsize, Allocator* _allocator)
    : data(0), refcount(0), elemsize(0), elempack(0), allocator(0), dims(0), w(0), h(0), c(0), cstep(0) {
    create(_w, _h, _c, _elemsize, _allocator);
}

inline void Mat::create(int _w, int _h, int _c, size_t _elemsize, Allocator* _allocator) {
    if (dims == 3 && w == _w && h == _h && c == _c && elemsize == _elemsize && elempack == 1 && allocator == _allocator)
        return;
    release();
    elemsize = _elemsize;
    elempack = 1;
    allocator = _allocator;

    dims = 3;
    w = _w;
    h = _h;
    c = _c;

    cstep = alignSize((size_t)w * h * elemsize, 16) / elemsize;

    if (total() > 0) {
        size_t totalsize = alignSize(total() * elemsize, 4);
        if (allocator)
            data = allocator->fastMalloc(totalsize + (int)sizeof(*refcount));
        else
            data = fastMalloc(totalsize + (int)sizeof(*refcount));
        refcount = (int*)(((unsigned char*)data) + totalsize);
        *refcount = 1;
    }
}
  • 接下来都以图解的形式, 直观的表示Mat的内存排布

c7d342e4ff2c7c09020efe32792ef492.png

d59d8f26ade27eead349d0a55b0302fd.png

下面是一个三维的Mat, 先给出实例代码以及对m赋值, 然后图解m的内存具体长什么样

fa049d4fbc292e55257b09f2ea4ac3f5.png

0c556c5278c5dd98375fbf3b82629c9d.png
// 返回总共的元素
// 1. 当是3维时, cstep = alignSize((size_t)w * h * elemsize, 16) / elemsize;
// 这时为了内存对齐, cstep 可能并不等于 w * h, 所以此时total()返回的元素个数大于等于w * h * c

// 2. 当是2维时, cstep = (size_t)w * h, 此时total()返回的元素个数等于w * h * c

// 3. 当是1维时, cstep = w, 此时total()返回的元素个数等于w * h * c
inline size_t Mat::total() const {
    return cstep * c;
}
  • 第164, 165行, 将一个对象转换为float*, 是c++里类型转换操作符(type conversion operator)的语法
// access raw data, 将Mat类型的对象返回一个T*
template<typename T>
operator T*();
  • 以上分别解释了一维, 二维, 三维的ncnn::Mat的内存排布, 但是它们都是elempack = 1的, 因为有很多小伙伴都不理解elempack这个变量的含义, 下面也给出图解实例, 阅读前请确保理解了上面的讲解

62977e7ba610cbb9e3c91d8c74fd1058.png

以上便是ncnn::Mat的内存排布的讲解, 下面是它的析构函数, 值得注意的是NCNN_XADD(refcount,-1)是一个宏, 它的作用是使refcount这个指针所指向的值减去一, 不过返回的还是之前的值, 有点类似(i++), 对i加一, 不过返回的是i之前的值.

inline Mat::~Mat() {
    release();
}

inline void Mat::release() {
    if (refcount && NCNN_XADD(refcount, -1) == 1) {
        if (allocator) allocator->fastFree(data);
        else fastFree(data);
    }
    data = 0;
    elemsize = 0;
    elempack = 0;
    dims = 0;
    w = 0;
    h = 0;
    c = 0;
    cstep = 0;
    refcount = 0;
}

3. References

  • Tencent/ncnn
  • 关于mat中data的内存排列问题 · Issue #334 · Tencent/ncnn

4. 写在最后

  • 你要是觉得有帮助, 就点个赞吧, peace.
  • Feel free to contact me via OFShare at outlook dot com.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值