Tensorflow中的内存分配

Tensorflow中的内存分配

Tensorflow系统复杂,支持平台多,各类数据结构也多,所以设计一个统一的内存管理分配接口很重要。本文主要探讨tensorflow中的内存分配的相关机制,会重点研究其中实现的两种分配算法。

内存分配是系统中非常重要的一环,大家平常接触最多的就是malloc(new)和free(delete)。也就是分配和施放,在tensorflow中也是这样。Tensorflow提供了一个公共的接口类,Allocator类,该类提供了两个重要的方法:

AllocateRaw分配内存,DeallocateRaw施放内存。Tensorflow中提供的内存分配方法都是继承该类去实现。

PoolAllocator是一种实现了LRU策略的内存池分配算法。

        

PtrRecord是分配的数据块,在poolAlloctor包含一个双向链表和一个multimap类型成员pool_,pool_的key是分配的内存大小。这两个成员都保存了当前系统未分配的内存块。

poolAlloctor在施放内存时,接口只传入了地址,并没有传递需要施放内存的大小,所以在poolalloctor实现时,采用了cookie技术,即采用了下面的数据结构,来存放分配的

Poolalloctor每次向系统申请的内存时如上图所示,但是前面会多一个sizeof(chunkprefix)字节的数据,真正返回给用户使用的内存起始地址是user_ptr指向的地址。

再来看分配代码,第104行可以看出,分配的字节数numbytes已经加上了ChunkPrefix结构图的大小。

110行,tensorflow使用numbytes做为key,从pool寻找适合当前请求的PtrRecord。可以看出为了提高查找效率,tensorflow使用mutilmap数据结构。

117行表示找到了符合当前请求的内存块,119行表示需要从双向链表内删除这个块,120行表示从pool_中删除这个块。

125行表示找到了内存,127行删除的是pool_中找到的元素。128行对内存进行处理,加上ccookie信息。

130表示没找到需要的内存块,需要向系统分配一款内存。131行也是对刚从系统分配的内存进行处理。

再看施放函数,137行表示根据ptr,查找cookie 信息,得到真正分配的内存地址

139-140行表示,如果分配的内存无限制且不自动resize,直接对内存进行施放。

142-151行,将内存加入pool_内,以备下次申请使用。144行表示当前池的大小已经到达上限,需要施放。EviceOne函数会施放双向链表的尾部元素。这样符合lru策略的逻辑。

147-149行,申请PtrRecord节点。150行,将节点插入链表头,151行,将元素插入pool_中。

         接下来我们来看BFCAllocator内存管理算法。它包含了21个Bin,每个Bin下面包含一个chunk的集合,每一类bin下面的chunk的大小是一样的,bin的排序也是根据chunk的大小进行升序排列的。也就是说chunk的大小也是固定有21类。其中最小的chunk是256个字节,之后每一个chunk大小是256向右移一位,即512,1024,2048等。

BFCAllocator的数据结构类图如上所示,BFCAlloctor内部包使用vector管理chunk。Bin中的chunks的元素使用的是chunk在vector中的索引。

该函数主要是将用户需求的内存大小对齐到256整数倍。

BFCAllocator最大的特点内存块的分裂和合并。当查找到符合满足用户需求大小的chunk大小是需求的2倍时,会执行分裂操作,系统也会根据当前的内存碎片会执行合并操作。

先看分配函数,主要看232行实现的AllocateRawInternal函数。

函数的主体部分已经截全,367-370行,主要计算对齐大小,并根据该大小定位要查找的chunk所在的bin。

373-376行尝试合并带时间戳的chunk。

377-380行表示在bin中去查找满足分配要求的chunk。

383-388行表示当没有满足要求的chunk时,需要从系统中分配一块满足要求的新chunk。

390-400行表示会从带时间戳的chunk,尚未被合并的列表中去合并一块满足当前要求的chunk。

407-411表示向系统未被使用的内存,由系统继续合并,然后再重新申请并尝试分配。

FindChunkPtr查找chunk函数

432行表示从满足要求的chunk 大小所在的bin进行查找。

444行表示查找到了满足要求的chunk,447行需要将该chunk从bin中删除。

453-458对chunk进行分裂,分裂的条件是chunk的大小是需求大小的2倍或者chunk大小和需求大小的差值大于128mb。

457行返回新chunk。

SplitChunk分裂函数。

495行建立新的chunk。注意新chunk的起始地址是原 chunk地址加上需求的大小

497行保存新chunk的地址和索引,这里是为了将来合并chunk时使用。注意region_manager成员变量。他主要保存chunk中的内存块地址和chunk索引的映射关系。

512-519行,对分配后的chunk进行串联处理。这一步也是为了将来合并chunk时使用。

TryToCoalesce对chunk块进行合并,合并的是之前分裂过的块。

650-656行查找chunk 的next块,如果next是空闲,则对齐进行合并。

660-667行查找chunk的prev块,如果prev是空闲,则对齐进行合并。

        

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值