ncnn源码阅读(三)----数据结构Mat

这篇具有很好参考价值的文章主要介绍了ncnn源码阅读(三)----数据结构Mat。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

数据结构Mat

个人认为一个框架中的比较核心的两个点,一个是数据结构,一个任务调度。两者之间其实是相互独立的,数据结构为调度过程中的数据流动提供了一个载体。在ncnn中的数据结构是Mat,类似于OpenCV中的Mat,但又增加了一些属于它本身的一些特性,在这个部分学习一下ncnn的Mat

成员变量

成员变量有7个

成员变量 含义
int dims; 表示当前数据是几维数据
float* data; Mat中数据的指针
int* refcount; 实现引用计数功能,实现类似智能指针的自动管理内存功能
int w; 第一个维度
int h; 第二个维度
int c; 第三个维度
size_t cstep; 表示在channel维的步长
这几个变量的,具体含义在成员函数中体会的会更加深刻。

成员方法

构造函数

构造函数主要完成对成员变量进行初始化

1、普通构造函数
  • 空构造函数:所有的成员赋值0
  • 一维数据构造:数据为1维,会分配相应的内存空间;
inline Mat::Mat(int _w)
    : dims(0), data(0), refcount(0)
{
    create(_w);
}
inline void Mat::create(int _w)
{
    release();
    dims = 1;
    w = _w;
    h = 1;
    c = 1;

    cstep = w;
	
    if (cstep * c > 0)
    {
        size_t totalsize = cstep * c * sizeof(float);
        data = (float*)fastMalloc(totalsize + (int)sizeof(*refcount));
        refcount = (int*)(((unsigned char*)data) + totalsize);
        *refcount = 1;
    }
}
  • 二维数据构造:数据为2维,会分配相应的内存空间;
inline Mat::Mat(int _w, int _h)
    : dims(0), data(0), refcount(0)
{
    create(_w, _h);
}

inline void Mat::create(int _w, int _h)
{
    release();
    dims = 2;
    w = _w;
    h = _h;
    c = 1;
    cstep = w * h;

    if (cstep * c > 0)
    {
        size_t totalsize = cstep *c * sizeof(float);
        data = (float*)fastMalloc(totalsize + (int)sizeof(*refcount));
        refcount = (int*)(((unsigned char*)data) + totalsize);
        *refcount = 1;
    }
}
  • 三维数据构造:数据为3维,会分配相应的内存空间;
inline Mat::Mat(int _w, int _h, int _c)
    : dims(0), data(0), refcount(0)
{
    create(_w, _h, _c);
}
inline void Mat::create(int _w, int _h, int _c)
{
    release();
    dims = 3;
    w = _w;
    h = _h;
    c = _c;
    cstep = alignSize(w * h * sizeof(float), 16) >> 2;

    if (cstep * c > 0)
    {
        size_t totalsize = cstep * c * sizeof(float);
        data = (float*)fastMalloc(totalsize + (int)sizeof(*refcount));
        refcount = (int*)(((unsigned char*)data) + totalsize);
        *refcount = 1;
    }
}

上面的三个构造函数内部,都会调用到create函数,而在create内部又会调用release函数,这是因为create的调用者不局限在构造函数中,其它的调用在后面再细说。
上面构造中还调用了两个其他的函数:

static inline size_t alignSize(size_t sz, int n)
{
    return (sz + n-1) & -n;
}

static inline void* fastMalloc(size_t size)
{
    unsigned char* udata = (unsigned char*)malloc(size + sizeof(void*) + MALLOC_ALIGN);
    if (!udata)
        return 0;
    unsigned char** adata = alignPtr((unsigned char**)udata + 1, MALLOC_ALIGN);
    adata[-1] = udata;
    return adata;
}
static inline void fastFree(void* ptr)
{
    if (ptr)
    {
        unsigned char* udata = ((unsigned char**)ptr)[-1];
        free(udata);
    }
}

alignSize函数主要用来做对齐,,举例:

原始大小 8对齐 16对齐
3 8 16
7 8 16
9 16 16
17 24 32
25 32 32
后面的fastMalloc和fastFree是对malloc和free的封装,其中也使用的内存对齐相关的内容
针对指针的对齐方法:
template<typename _Tp> static inline _Tp* alignPtr(_Tp* ptr, int n=(int)sizeof(_Tp))
{
    return (_Tp*)(((size_t)ptr + n-1) & -n);
}

fastMalloc方法中,针对要分配的空间会多分配,对齐大小+一个指针的大小
多分配的一个指针大小,将存储由malloc分配出来的原始指针用以内存的释放使用;
多分配的对齐大小将用来做内存对齐填充;
具体的操作在下面这两句代码:

 unsigned char** adata = alignPtr((unsigned char**)udata + 1, MALLOC_ALIGN);
 adata[-1] = udata;

udata为malloc得到的原始指针。先将原始指针偏移一个指针大小,然后再做内存对齐,偏移的一个指针大小用来存储原始指针。
ncnn源码阅读(三)----数据结构Mat,ncnn,数据结构
fastFree时,需要将使用的ptr指针,向后偏移一个指针大小,然后调用系统的free进行释放

2、外部数据指针构造函数

只能接受外部的float*类型的数据指针进行构造。同样的也是分为一维、二维和三维数据的构造

inline Mat::Mat(int _w, float* _data)
    : dims(1), data(_data), refcount(0)
{
    w = _w;
    h = 1;
    c = 1;

    cstep = w;
}

inline Mat::Mat(int _w, int _h, float* _data)
    : dims(2), data(_data), refcount(0)
{
    w = _w;
    h = _h;
    c = 1;

    cstep = w * h;
}

inline Mat::Mat(int _w, int _h, int _c, float* _data)
    : dims(3), data(_data), refcount(0)
{
    w = _w;
    h = _h;
    c = _c;

    cstep = alignSize(w * h * sizeof(float), 16) >> 2;
}
3、拷贝构造函数和opertor =
  • 拷贝构造函数,只是一个浅拷贝,两个Mat公用一块数据内存,引用计数加一
inline Mat::Mat(const Mat& m)
    : dims(m.dims), data(m.data), refcount(m.refcount)
{
    if (refcount)
        NCNN_XADD(refcount, 1);

    w = m.w;
    h = m.h;
    c = m.c;

    cstep = m.cstep;
}
  • operaotr=:也会导致引用计数加1,然后两个Mat也是共用一块数据内存,与构造不同的是,需要将左边Mat的进行release
inline Mat& Mat::operator=(const Mat& m)
{
    if (this == &m)
        return *this;

    if (m.refcount)
        NCNN_XADD(m.refcount, 1);

    release();

    dims = m.dims;
    data = m.data;
    refcount = m.refcount;

    w = m.w;
    h = m.h;
    c = m.c;

    cstep = m.cstep;

    return *this;
}

深拷贝函数

inline Mat Mat::clone() const
{
    if (empty())
        return Mat();

    Mat m;
    if (dims == 1)
        m.create(w);
    else if (dims == 2)
        m.create(w, h);
    else if (dims == 3)
        m.create(w, h, c);

    if (total() > 0)
    {
        memcpy(m.data, data, total() * sizeof(float));
    }

    return m;
}

类型转换

inline Mat::operator float*()
{
    return data;
}

inline Mat::operator const float*() const
{
    return data;
}

引用计数的实现

实现:一段堆空间存储引用计数,当发生拷贝时,引用计数加1,销毁的时候,引用计数减1,如果引用计数为0,则释放资源。
在ncnn中的mat的实现方式:

  • 在申请数据空间,多一段空间用来存储引用计数
 data = (float*)fastMalloc(totalsize + (int)sizeof(*refcount));
 refcount = (int*)(((unsigned char*)data) + totalsize);

当发生拷贝构造、赋值时,需要对引用计数加一
当析构的时候需要对引用计数减一,并判断引用计数,决定是否释放资源

inline void Mat::release()
{
    if (refcount && NCNN_XADD(refcount, -1) == 1)
        fastFree(data);

    dims = 0;
    data = 0;

    w = 0;
    h = 0;
    c = 0;

    cstep = 0;

    refcount = 0;
}

其他数据操作函数

设计方法:在Mat.h中声明包含了上述的函数和一些数据操作函数,但具体的实现,可以在两个cpp中进行分类实现文章来源地址https://www.toymoban.com/news/detail-548886.html

到了这里,关于ncnn源码阅读(三)----数据结构Mat的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 数据结构与集合源码

    目录 数据结构概述 概述: 数据结构的研究对象(三个): 逻辑结构: 物理结构(存储结构) 运算(相关算法操作) 常见存储结构: 树的理解  经典二叉树 BST(二叉排序树) 平衡二叉树(AVL) 红黑树(RBT)(复杂,不多讲,了解) List源码解析   ArraysList在JDK7和JDK8中的

    2024年04月13日
    浏览(35)
  • 【数据结构】堆详解!(图解+源码)

    🎥 屿小夏 : 个人主页 🔥个人专栏 : 数据结构解析 🌄 莫道桑榆晚,为霞尚满天! 堆是一种基本而强大的数据结构。本文将深入探讨堆的概念、原理以及实现。 普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存

    2024年02月05日
    浏览(55)
  • 【数据结构】 顺序表详解!(源码+解析)

    🎥 屿小夏 : 个人主页 🔥个人专栏 : 数据结构解析 🌄 莫道桑榆晚,为霞尚满天! ​ 什么是数据结构?我们为什么要学数据结构?数据结构中的顺序表长什么样子?它是怎么运用? ​ 本期我们将对这些一一讲解,彻底明白数据结构的重要性,以及顺序表是一种什么的数据

    2024年02月06日
    浏览(46)
  • 【数据结构】栈算法(算法原理+源码)

    博主介绍:✌全网粉丝喜爱+、前后端领域优质创作者、本质互联网精神、坚持优质作品共享、掘金/腾讯云/阿里云等平台优质作者、擅长前后端项目开发和毕业项目实战✌有需要可以联系作者我哦! 🍅附上相关C语言版源码讲解🍅 👇🏻 精彩专栏推荐订阅👇🏻 不然下次找

    2024年01月23日
    浏览(65)
  • 数据结构之队列的实现(附源码)

    目录 一、队列的概念及结构 二、队列的实现  拓展:循环队列 三、初学的队列以及栈和队列结合的练习题 队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出 FIFO(First In First Out) 入队列:进行插入操作的一端称为 队尾 出队

    2024年02月09日
    浏览(32)
  • Redis源码篇 - inset数据结构

    Redis底层原理篇

    2024年02月16日
    浏览(56)
  • 数据结构——七大排序[源码+动图+性能测试]

    本章代码gitee仓库:排序 我们日常打扑克牌,摸牌,让后将牌按顺序插入好,这其实就是插入排序的过程,打小插入排序的思想就植入我们的脑海 第一张牌不用管,直接拿在手里,之后的牌按照大小再一个一个插入即可 直接插入排序特性: 越接近有序,效率越高(不用那么多

    2024年02月09日
    浏览(49)
  • 【数据结构与算法】堆的实现(附源码)

      目录 一.堆的概念及结构 二.接口实现 A.初始化  Heapinit   销毁 Heapdestroy B.插入 Heappush 向上调整  AdjustUp 1.Heappush 2.AdjustUp C.删除 Heappop  向下调整  AdjustDown D.堆的判空  Heapempty  堆顶数据  Heaptop  堆的大小  Heapsize 三.源码 Heap.h Heap.c test.c 1.概念      如果有一个关键码的

    2024年02月01日
    浏览(99)
  • 【数据结构与算法】图的概述(内含源码)

    个人主页:【😊个人主页】 系列专栏:【❤️数据结构与算法】 学习名言:天子重英豪,文章教儿曹。万般皆下品,惟有读书高——《神童诗劝学》 第一章 ❤️ 学前知识 第二章 ❤️ 单向链表 第三章 ❤️ 递归 … 与线性表中的元素是“一对一”的关系和树中的元素是“

    2024年02月04日
    浏览(63)
  • 第14章_数据结构与集合源码

    我们举一个形象的例子来理解数据结构的作用:    战场: 程序运行所需的软件、硬件环境 敌人: 项目或模块的功能需求 指挥官: 编写程序的程序员 士兵和装备: 一行一行的代码 战术和策略: 数据结构    上图:没有战术,打仗事倍功半    上图:有战术,打仗事半功

    2023年04月16日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包