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

news2024/10/7 17:30:16

文章目录

  • 数据结构Mat
    • 成员变量
    • 成员方法
      • 构造函数
        • 1、普通构造函数
        • 2、外部数据指针构造函数
        • 3、拷贝构造函数和opertor =
      • 深拷贝函数
      • 类型转换
      • 引用计数的实现
      • 其他数据操作函数

数据结构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对齐
3816
7816
91616
172432
253232
后面的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得到的原始指针。先将原始指针偏移一个指针大小,然后再做内存对齐,偏移的一个指针大小用来存储原始指针。
在这里插入图片描述
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中进行分类实现

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/742397.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STM32F407ZGT6正点原子F4探索者开发板 -- 跑马灯例程

1. USB 转USART1 烧录工具 FlyMcu 2. FlyMcu 配置 3. 查看开发板原理图&#xff0c;LED0、LED1 硬件连接 LED0 - PF9 LED1 - PF10 PF9 0&#xff0c; LED0 亮&#xff0c;PF9 1&#xff0c;LED0 灭 PF10 0&#xff0c; LED1 亮&#xff0c;PF10 1&#xff0c;LED1 灭 4.…

cesium 实现多颗卫星与多颗地面站雷达通信效果

最主要的部分是计算空间内两点之间的距离以及卫星对地点是否在雷达扫描范围内 先看效果 1.计算空间内两点之间的距离 //计算距离 function distance(point1, point2) {var point1cartographic = Cesium.Cartographic.fromCartesian(point1);var</

医师访问学者申请需要注意什么?

医师访问学者申请是医学领域中一项重要的学术交流和合作方式。在准备申请时&#xff0c;有几个关键点需要注意&#xff0c;下面就随知识人网小编一起来看一看。 1. 目标和动机&#xff1a;明确访问学者的目标和动机&#xff0c;例如学术研究、专业发展、文化交流等。清楚表达你…

产品经理学习画原型-登录界面(交互样式设置)

经过第一阶段学习&#xff0c;目前效果如下&#xff1a; 下面我们来加入交互样式效果&#xff1a; 效果&#xff1a;

[SSM]MyBatis的缓存与逆向工程

目录 十三、MyBatis的缓存 13.1一级缓存 13.2二级缓存 13.3MyBatis集成EhCache 十四、MyBatis的逆向工程 14.1逆向工程配置与生成 14.2测试 十三、MyBatis的缓存 缓存&#xff1a;cache 缓存的作用&#xff1a;通过减少IO的方式&#xff0c;来提高程序的执行效率。 myb…

【分布式】 ELK 企业级日志分析系统 二

目录 一、FilebeatELK 部署1.1 环境部署 二、grok 正则捕获插件mutate 数据修改插件multiline 多行合并插件date 时间处理插件 一、FilebeatELK 部署 1.1 环境部署 Node1节点&#xff08;2C/4G&#xff09;&#xff1a;node1/192.168.137.101 Elasticsearch Node2节点&…

【C++顺序容器】forward_list的成员函数和非成员函数

目录 forward_list 1. forward_list的成员函数 1.1 构造、析构和赋值运算符重载 1.1.1 构造函数 1.1.2 析构函数 1.1.3 赋值运算符重载 1.2 迭代器 1.3 容量 1.4 元素访问 1.4.1 遍历方法 1.5 修改器 1.6 操作 1.7 观察者 2. forward_list的非成员函数 forward_l…

npm5中本地间模块引用的最好方式(附带引用方法总结)

引用其他的包 正常情况下在项目 package.json 所在的目录&#xff08;一般也是项目根目录&#xff09;运行npm install xxxx 命令之后&#xff0c;会从远程或者代理地址下载xxxx包到node_modules&#xff0c;然后在package.json生成对应的包名和版本 如果想要依赖本地自己开发…

TypeScript 学习笔记(一):类型

文章目录 一、常见类型1. 数组2. 布尔3. 数值4. 字符串5. object6. null 和 undefined7. symbol7.1 作为属性名7.2 属性名遍历7.3 静态方法&#xff1a;Symbol.for()和 Symbol.keyFor()7.4 内置 symbol 值7.4.1 Symbol.hasInstance7.4.2 Symbol.isConcatSpreadable7.4.3 Symbol…

第四十五章Java 接口

Java 接口 接口&#xff08;英文&#xff1a;Interface&#xff09;&#xff0c;在JAVA编程语言中是一个抽象类型&#xff0c;是抽象方法的集合&#xff0c;接口通常以interface来声明。一个类通过继承接口的方式&#xff0c;从而来继承接口的抽象方法。 接口并不是类&#x…

DevOps(二)

CD 1. 平台选择2. 技术选型3. 阶段性目标4. 搭建示例4.1 环境准备(节点机)1. java版本升级2. 编译安装git3. docker安装4. docker-compose安装5. sonarqube安装6. harbor安装7. gitlab私服 4.2 示例一&#xff08;手动&#xff09;1. 创建项目2. 编码3. Dockerfile4. 拷贝pytho…

【linux】线程详解

线程 线程的概念 在官方书籍对于线程的概念&#xff1a; 1.在进程内部的执行流 2.线程比进程粒度更细&#xff0c;调度成本更低。 3.线程是CPU调度的最小单位。 线程&#xff08;tcb&#xff09;&#xff1a;进程&#xff08;pcb&#xff09; n&#xff1a;1 进程和线程在执…

Java 动态规划 面试题 17.16. 按摩师

代码展示&#xff1a; class Solution {public int massage(int[] nums) {int nnums.length;if(n0){return 0;}//创建数组int f[]new int[n]; //f[i]表示接i位置的最长时间int g[]new int[n]; //g[i]表示不接i位置的最长时间//初始化f[0]nums[0];g[0]0;//填充数组for(int i1;i…

VectorCAST单元测试手动配置测试用例

一、单元测试 等待环境创建完成后&#xff0c;就可以开始单元测试。 二、生成测试用例 在 VectorCAST 中&#xff0c;一共有两种方法来生成测试用例&#xff0c;一种是手动生成测试用例&#xff0c;另外一种是自动 生成测试用例。 三、手动生成测试用例 在 VectorCAST 中&a…

《面试1v1》Redis分布式锁

&#x1f345; 作者简介&#xff1a;王哥&#xff0c;CSDN2022博客总榜Top100&#x1f3c6;、博客专家&#x1f4aa; &#x1f345; 技术交流&#xff1a;定期更新Java硬核干货&#xff0c;不定期送书活动 &#x1f345; 王哥多年工作总结&#xff1a;Java学习路线总结&#xf…

泛微E-Cology SQL注入漏洞复现(QVD-2023-15672)

0x01 产品简介 泛微协同管理应用平台e-cology是一套兼具企业信息门户、知识文档管理、工作流程管理、人力资源管理、客户关系管理、项目管理、财务管理、资产管理、供应链管理、数据中心功能的企业大型协同管理平台。 0x02 漏洞概述 由于泛微e-cology未对用户的输入进行有效的…

matlab学习指南(2):安装工具箱Toolbox的方法(详细图解)

&#x1f305;*&#x1f539;** φ(゜▽゜*)♪ **&#x1f539;*&#x1f305; 欢迎来到馒头侠的博客&#xff0c;该类目主要讲数学建模的知识&#xff0c;大家一起学习&#xff0c;联系最后的横幅&#xff01; 喜欢的朋友可以关注下&#xff0c;私信下次更新不迷路&#xff0…

【项目 进程1】2.1 进程概述 2.2 进程状态转换

文章目录 2.1进程概述程序和进程**时间片****并行和并发****进程控制块(PCB)** 2.2进程状态转换**进程的状态** **进程相关命令****实时显示进程动态** 2.1进程概述 程序和进程 程序是包含一系列信息的文件&#xff0c;这些信息描述了如何在运行时创建一个进程&#xff1a; …

Linux的软链接与硬链接

Linux的软链接与硬链接 1&#xff0c;创建硬链接&#xff1a;2&#xff0c;创建软链接&#xff1a;3&#xff0c;软链接是什么4&#xff0c;软链接文件的权限5&#xff0c;硬链接是什么6&#xff0c;做个小实验 总结问题&#xff1a;为什么有软链接了&#xff08;快捷方式&…

Centos7.9通过expect脚本批量修改H3C交换机配置

背景&#xff1a; 公司有几百台H3C二层交换机设备&#xff0c;当需要批量更改配置时非常的消耗工作量 解决&#xff1a; 通过一台Linux服务器&#xff0c;编写shell脚本&#xff0c;模拟Telnet至各台交换机&#xff0c;让一切变的很容易 1.首先在安装Telnet服务前需要检测centO…