美文网首页
ncnn源码阅读笔记(六)

ncnn源码阅读笔记(六)

作者: 半笔闪 | 来源:发表于2020-05-12 18:12 被阅读0次

本文来分析一下ncnn中的基础数据结构mat以及内存分配allocator。
今天要分析的代码主要是ncnn/scr下的allocator.h、allocator.cpp、mat.h和mat.cpp。还是老样子,暂时不去看vulkan方面的代码。

allocator

先来看allocator.h,除去#if NCNN_VULKAN和#endif之间的内容剩下的就是与内存分配相关的类和函数。先来看Allocator基类:

class Allocator
{
public:
    //Allocator基类析构函数
    virtual ~Allocator();
    //分配内存的函数,纯虚函数需要在子类中继承实现,输入需要分配的size
    virtual void* fastMalloc(size_t size) = 0;
    //释放内存的函数,纯虚函数需要在子类中继承实现,输入需要释放的内存的指针
    virtual void fastFree(void* ptr) = 0;
};

Allocator有两个子类PoolAllocator和UnlockedPoolAllocator,一个是带锁的内存分配,一个是无锁的内存分配。当然两个类里都继承了fastMalloc和fastFree函数。接下来主要来分析这两个函数以及一些辅助函数。
在本文集的《内存对齐》中提到过,cpu在读取内存时是一块一块进行读取的,块的大小可以是2,4,8,16(总之是2的倍数),这里宏定义表示这里我们需要内存对齐的块大小为16,

#define MALLOC_ALIGN    16

这里把fastMalloc声明成静态内联函数,这个我谈谈自己的想法,首先,inline函数跟宏定义类似,不存在所谓的函数入口。如果只用inline,不加static,当包含inline函数的.h文件被不同的文件包含时,会出现重名。加了static后函数只能在文件内部可见,则不会有重名问题,相当于一个inline在不同的.c文件里生成了不同的实例。

static inline void* fastMalloc(size_t size)
{
    //分配size + sizeof(void*) + MALLOC_ALIGN大的空间
    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;
#endif
}

首先,分配size + sizeof(void) + MALLOC_ALIGN大的空间,我们需要size大的空间,那多出来的sizeof(void) + MALLOC_ALIGN空间是做什么用的,sizeof(void)就是一个指针的大小,指针的本质就是内存地址(x86是4byte,x64是8byte),所以这里要分配一个内存地址的空间来存储内存地址,这个内存地址就是未对分配空间做对齐时的起始地址,那么多出来MALLOC_ALIGN的空间呢?也就是16byte的空间,这个空间最后是浪费的,但是它还是有作用的,它的作用是对size + sizeof(void) + MALLOC_ALIGN的空间做完对齐后,我们需要的size的这一部分内存空间的起始地址就不是原来我们分配的size + sizeof(void) + MALLOC_ALIGN的其实地址了,为了保证对齐之后的起始地址之后还有size大小的空间,所以我们还需要至少MALLOC_ALIGN的空间。
由于16长度画图太难,这里把它缩减为4长度。
下面我们分配了4长度的内存(蓝色部分)

image.png
对齐之后我们的起始地址变成0x4,我们还是要得到4长度的空间,那我们还是要分配的空间至少要原来分配的空间(蓝色部分)后面再加上对齐长度(这里是4)。
image.png
从上面的注释我们可以看到关键的一句在alignPtr函数这一行,这里把udata转换成(unsigned char )并加1。这个作用主要是在我们分配的size + sizeof(void) + MALLOC_ALIGN的空间开始位置留出一个8Byte的空间,用于存指针。也就是假如原来udata指向0x0,那么(unsigned char
)udata + 1指向0x8,这个地址即为我们需要对齐后的size部分的空间的起始地址。
alignPtr函数主要作用是返回内存对齐后size那部分内存的起始地址。_Tp即为unsigned char *类型。这里n=16,&-n就是让指针指向16的整数倍以达到内存对齐的目的。
// Aligns a pointer to the specified number of bytes
// ptr Aligned pointer
// n Alignment size that must be a power of two
template<typename _Tp> static inline _Tp* alignPtr(_Tp* ptr, int n=(int)sizeof(_Tp))
{
    return (_Tp*)(((size_t)ptr + n-1) & -n);
}

接下来是释放内存的函数fastFree,如果指针不为空,则把内存重新指向原来申请的总内存的起始地址,然后free掉。这里应该比较简单,不做过多分析。

static inline void fastFree(void* ptr)
{
    if (ptr)
    {
        unsigned char* udata = ((unsigned char**)ptr)[-1];
        free(udata);
    }
}

这.h文件中还有一个函数

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

他的主要作用就是内存对齐。n是2的幂次。

mat

相关文章

  • ncnn源码阅读笔记(六)

    本文来分析一下ncnn中的基础数据结构mat以及内存分配allocator。今天要分析的代码主要是ncnn/scr...

  • ncnn源码阅读笔记(五)

    Extractor 完成模型网络结构和模型权重参数的载入,就可以运行网络了,这就需要Extractor了。在第一篇...

  • ncnn源码阅读笔记(四)

    load_model 前几篇算是把网络结构参数的载入说完了,本篇开始网络权重参数载入load_model。在net...

  • ncnn源码阅读笔记(二)

    Net 使用ncnn部署模型时,我们要先定义一个Net对象,然后使用load_param和load_model载入...

  • ncnn源码阅读笔记(一)

    工作需要,最近在使用ncnn,为了写自定义层,深入理解下源码,在此作个笔记。https://github.com/...

  • ncnn源码阅读笔记(三)

    继续上一篇,上一篇最后有这么一段代码: 这一段就是把对应的特殊参数传递给根据不同的层类型创建的layer对象。这里...

  • 零基础centerFace+ncnn移植到ios

    参考资料: centerFace源码 ncnn ios demo ncnn faceDetection demo ...

  • 内存对齐

    ncnn中内存对齐 在ncnn源码net.cpp中,int Net::load_param(const unsig...

  • OpenCL并行编程

    看了mnn、mace和ncnn的源码,对于深度学习手机gpu计算的优化,mace选择了opencl,而ncnn选择...

  • iOS 编译ncnn静态库

    ncnn官方在github上有编译好的静态库可以直接使用。但如果需要修改ncnn代码的话就只能自己编译了。 源码及...

网友评论

      本文标题:ncnn源码阅读笔记(六)

      本文链接:https://www.haomeiwen.com/subject/ydobnhtx.html