首页 > 代码库 > 视频笔记 CppCon 2016 Chandler Carruth High Performance Code 201 Hybrid Data Structures

视频笔记 CppCon 2016 Chandler Carruth High Performance Code 201 Hybrid Data Structures

版权声明:转载请说明出处 http://www.cnblogs.com/eagledai/

https://www.youtube.com/watch?v=vElZc6zSIXM&t=1157s 
LLVM internal data structures
LLVM 内部数据结构
 
2:24 SmallVector
下面是 SmallVector 的简化版本。这是 LLVM 里最基本的数据结构,到处被使用。
技术分享

问题:为什么不直接使用 STL 的 vector? 因为在 STL 里,如果对 vector 进行 move 操作,按照规范,迭代器还要保持有效。这个特性虽好,但带来的问题是,不能进行 small-size 优化。

 
4:57 Wield Tricks for SmallVector (SmallVector 的怪异的小把戏)
技术分享

左边:仅仅有缓存 (char Buffer[])

右边:基类看起来像个 vector,但是没有缓存。缓存是通过基类的构造函数传入的。这种模式是非常强大的,因为类型擦除了 N

类型擦除N的好处?跨越接口边界的时候,不用关心这个N

 
6.27 SmallDenseSet
技术分享

It is a smalll size optimized set. Unlike std::unordered_set, instead we use open addressing here, an array of buckets, flat in memory. We use quadratic probing (二次探测). The traits class is related to hashing.

 
8:40 SmallDenseMap
技术分享

The map uses the same traits class.

For empty and tombstone buckets, don‘t put complete pair into those buckets. Acutually, just put the keys into them (special code).
 
LLVM,对于通用的数据机构,99%都是基于以上三个small size optimized containers。
 
10:37 问题:为什么small size optimized containers不使用allocators?
技术分享

 

上图是有人一种建议的实现方式(使用allocators二不是custom containers)。这个solution是能工作的,但是...
 
问题1:有人可能会忘了让这些数字在所有的callers都同步,否则...booom。也许有办法解决这个问题,但是很难找到简单有效的方法
技术分享

 

问题2:返回这个vector对象的时候,很有可能这个vector包含memory在stack里面。也许也是有办法解决了,但是很challenging
技术分享

使用costomed containers,而不是allocators,所有这些问题都消失了。

 
13:44 Small-size optimizations效果最好,只在values小的时候
works way better when the values are really small. 这是最最critical的!这样才有足够高的密度。所以真正的challenges,是优化这些values,使他们足够小。这比有上面那些containers更重要。Containers不难,但是有效的使用好它们需要很多的工作。
 
14:45 第一个关键的想法是给large objects address identity
address identity: 对象有内在的ID吗?something unique about the object。如果仔细分配,可以借用地址作为ID
技术分享

借用地址做ID,可以如上使用container。

问题:为什么不使用forward list?上面的用法比forward list效果好很多!因为地址是连续的,对缓存相对友好。如果遍历forward list,必须先要读出一个对象,才能知道下一个对象的地址在哪里。也就是如果有一次cache miss,就会有一序列的cache misses.
但是以上的用法也有问题,这些大对象本事没有locality。他们内存分配的时候一般都是随意哪里有内存就在哪里,根本没有locality。所以LLVM使用下面可怕的BumpPtrAllocator
 
20:00 class BumPtrAllocator 简化版本below
技术分享

"World‘s worst malloc implementation". Nice thing about this:

  • local. strong locality for large objects, though wasteful: a page (4k) at least
  • really fast
 
21:50 Sometimes, if pointers are too large, use an index
 
22:08 Aggressively pack the bits
  • to make objects small
  • there are 4 zeros in low bits of the pointer, e.g., 10000000010000000010000
技术分享

结合之后的提问:这里的bit operations基本上cost非常低,大多都是compile time的cost

 
28:28 use above packed pointer to build below:
技术分享

TinyPtrVector can be a T or a vector of T

The most useful case of this is for multi map:
  template <typename KeyT, typename ValueT>
  using SmallMultiMap = SmallDenseMap<KeyT, TinyPtrVector<ValueT>>;
 
 
31:24 Lastly, use bitfields everywhere
 
32:00 Sometimes, you really need an ordering
  不能依赖hash or set的ordering
  When possible, sort vectors
  有时候,按照什么排序不重要,但一定需要deterministic,也就是需要一个确定的顺序
 
技术分享

  类似的有 template class SmallMapVector;

  可以进一步优化,当数量小的时候,只使用vector, 线性搜索有更高的效率
 
37:00 提问开始

视频笔记 CppCon 2016 Chandler Carruth High Performance Code 201 Hybrid Data Structures