OB的Hashjoin的cacheline访存性能讨论

【 使用环境 】生产环境/测试环境
【 OB or 其他组件 】执行器
【 使用版本 】任意
【问题描述】如下
先说一下背景,在研究hashjoin的性能优化,400万的内表,6亿的外表这个规模的数据,不使用并行。在传统模式的hashjoin的实现中,采用一个大的hash表存储内表的数据,假如我一个元素有8字节,那么probe阶段获取外表的一行数据hash取模后命中一个hashbucket,对于一个64字节的缓存行来说,有效载荷只有八分之一,所以对于probe阶段的访问是随机的情况,缓存的命中率极低,导致性能上不去。
所以目前一个比较高效的方式是对hash表做分区,一个分区的大小大致与一个cpu二级缓存的大小相等,将内表hash表做分区,当probe阶段的时候,也采用同样的方式做分区,然后做join的时候将一个分区整个prefetch到缓存中进行join,这样可以,可以保证内存的访问只限于当前的分区,并且分区的内存已经加载到cache中了,然后一个分区做完后再做下一个分区。
我了解到ob好像也是类似的实现,具体的代码细节没太看完,粗略的看了一下状态机的轮转,以及各个状态的实现,这里我有几个问题想请教一下:
因为prefetch指令本身也是有成本的,那么外表如果只获取一个数据,就要对内表的这个分区进行prefetch这是不合算的,毕竟prefetch一个二级缓存这么大内存的耗时肯定已经大于一个cachemiss的时间了,所以可能外表需要积攒一定量的数据后,再进行prefetch分区这样的是比较高效的,当然,最高效的肯定是获取全部外表数据后在prefetch,但内存是有限的,所以想请教一下OB这里的实现细节是不是如我上面的逻辑所说,如果是的话,那么外表获取多少数据后进行prefetch分区的操作,才能既保证效率,又保证内存使用不会过高?如果实现的逻辑不是这样,还请简单介绍一下

2 个赞

OB当前hash join实现中, 会对数据进行分区, 内存的使用量由自动内存管理机制对数据进行落盘控制
;
对于hash表的构建及probe有两种方式:
一种是对每个分区构建hash表, hash表大小没有要求一定要在L2 cache内, 只要有合适的内存能够放下具体hash 表和数据即可, 在进行probe时, 会从外表中先获取一批数据, 并对这一批数据中, 对应的hash 表bucket及数据行进行prefetch; 然后进行match计算;

另一种实现是cache aware的实现方式, 主要思想可以看下这篇论文( https://15721.courses.cs.cmu.edu/spring2016/papers/kim-vldb2009.pdf), 对hash表的构建方式会将行数描述信息<hash_val, Row *>放入一个连续数组中, 并对数据重排, 最终确保相同hash数据在数组中连续, 每个hash表的大小在L2 cache内, 外表对应分区数据的行描述信息也会放入连续数组中(不需要进行重排), 然后遍历外表每一行, 定位到该行对应的内表连续数组所在的起始位置和结束位置, 依次进行匹配;

1 个赞

感谢您的不吝赐教,我还有几个问题想请教一下:
1.我在我的研究中发现,如果我们只针对缓存性能的角度考虑:如果我将内表数据进行分区并构建独立的hash表,但是外表的数据进过hash后,对于内表的访问是相对随机的,根据hash值先定位的分区,再定位到hashbucket,那么这个分区的优势从何处体现呢?除非我对外表也进行分区,这样才可以确保对内表的访问一定会落在对应的分区内,但对于纯内存的hashjoin,对外表分区要拥有足够大的内存,这一点很难控制。并且我还发现,即使内表的hash表可以放入cache中,随着对外表数据的读取与处理,cache中的低频访问的cacheline可能会被外表一些高频数据置换调,如果想维持cache中hash表,依然要使用prefetch,那么和我不进行分区,直接prefetch对应分区的hashbucket又有什么区别呢?
2.对于group prefetch来说,需要一定量的group宽度来掩盖prefetch的生效时间,请问一点上OB的group prefetch的宽度大概是怎么考虑的,和向量化的批处理量有什么联系?
3.对于您说的第二点,我之前也有过考虑,我的方式是在hashbucket的最高bit上使用1bit来标记是否存在冲突,这样可能避免无效的冲突元组的访问,并将冲突的元组也进行调整,将所有冲突的元组置入一个或多个连续的cacheline中,来提高缓存的访问性能。我看OB的cache aware机制无法在向量化中生效,这个是有什么原因吗?
4.OB在probe阶段,对于外表一次处理的批量长度是多少?我看好像是256吗?

还有几个问题像请教一下您:
我刚看了一下can_use_cache_aware_opt,这里如果开启cache aware的时候total_memory_size的计算为什么只算了内表,没有计算外表的内存呢,这里不需要判断工作内存外表的数据是否装的下吗?