RoaringBitmap学习笔记

RoaringBitmap学习笔记

读代码时,看到一份roaring的实现,读了一下。下面总结下要点

实现思路

  • roaringbitmap是一个contaner的数组。
  • 每个container内部维护了2^16 个元素,最大有 2^16 个container,按高16位分桶。
  • container 内有3种数据维护的方式:bitset,array,run(按区间维护[min, max])。
  • roaringbitmap实现了不同的类型的container的各种and\or\xor等集合运算和查询方法,并且他们两两之间的集合运算,也都做了实现,类型互转都做了实现。
  • 维护container时会根据数据分布,选择存储更优的container type。

添加一个元素对应方法: roaring_bitmap_add

  1. 计算高16位
  2. 二分找到高16位所在的container
  3. 找到对应的 containers时
    • 获取位置i的container,以及他的type
    • 将低16位,加入到这个container中
    • 释放老的container
    • 设置新的container
  4. 当没有找到所在的container时
    • 对于不存在的container创建一个新的
    • 将低16位,加入到这个container中
    • 数组向后移一个位置,插入这个新的container

SMID指令加速有序集合的交集运算

论文:Fast Sorted-Set Intersection using SIMD Instructions
1. 使用 _mm_load_si128 加载v_a, v_b 2个向量
2. 利用_mm_cmpestrm实现向量内全部比较,同时_mm_extract_epi32获取r,表示 v_a 中哪几个数字在v_b中出现了
image
3. _mm_extract_epi32 分别提取,v_a和v_b中最大的元素(最后一个元素)
4. 将较小max值较大的指针向后移动(因为不可能再后之后的数字产生重复元素了)
5. 通过r查表,找到对应的排序mask,使用 _mm_shuffle_epi8 从 v_a 提取符合条件的数字,放到结果向量的前面
5. _mm_storeu_si128 将结果记录到 C 中
6. _mm_popcnt_u32 统计 r 中这一轮记录了多少个数字

int intersect(short *A, short *B,
    int l_a, int l_b, short* C) {
  int count = 0;
  short i_a = 0, i_b = 0;
  while(i_a < l_a && i_b < l_b) {
    // 1. Load the vectors
    __m128i v_a = _mm_load_si128((__m128i*)&A[i_a]);
    __m128i v_b = _mm_load_si128((__m128i*)&B[i_b]);
    // 2. Full comparison
    __m128i res_v = _mm_cmpestrm(v_b, 8, v_a, 8,
        _SIDD_UWORD_OPS|_SIDD_CMP_EQUAL_ANY|_SIDD_BIT_MASK);
    int r = _mm_extract_epi32(res_v, 0);
    unsigned short a7 = _mm_extract_epi32(v_a, 7);
    unsigned short b7 = _mm_extract_epi32(v_b, 7);
    A += ( a7 <= b7 ) * 8;
    B += ( a7 >= b7 ) * 8;
    // 3. Write back common values
    __m128i p = _mm_shuffle_epi8(v_a, sh_mask[r]);
    _mm_storeu_si128((__m128i*)&C[count], p);
    count += _mm_popcnt_u32(r);
  }
  return count;
}
posted @ 2025-08-03 01:06  RRRR_wys  阅读(51)  评论(0)    收藏  举报