google.sparsetable 实现细节

goolge.sparse*系列容器依赖关系:

 

- sparsetable

   - sparsehashtable

      - sparse_hash_map

      - sparse_hash_set

 

与现有的一些“标准”实现不同,sparsehash table 使用二次探测法,而不是链接,来解决hash冲突。

 

sparse table 就更奇特了,它是一个两级结构,第一级使用直接索引法,而第二级使用的是顺序查找

不过,第二级的尺寸比较小,可以放入cpucache,并且它的顺序查找使用的是popcount(bitmap[i]=count of (j < i and bitmap[j]==1))。在实现中它使用的是字节直接索引法,速度还行。第二级索引使用位图实现,如果值存在,bitmap[i]被置为1,否则为0,当值不存在时,不会消耗内存。具体说:

sparsetable::operator[i]是这样执行的:

 

// google 的代码比较长,并且不够直接
// 用这个直接且较短的代码来表示
value_type& sparsetable::operator[](int i)
{
    int j = i % GroupSize;
    sparsegroup* group = m_groups[i / GroupSize];
    unsigned char* bitmap = group->bitmap;
    if (bitmap[j/8] & 1 << j%8) {
        // value existed
        int valueIndex = popcount(bitmap, j);
        return group->values[valueIndex];
    }
    return s_default_value;
}
 

还有一个顺序查找/插入的地方:group->values是一个value_type数组,它的插入、删除是O(n),当然,在这里,因为n<=GroupSize,而GroupSize是一个编译时常量,并且不太大,google说它是O(1)也不算错。

 

 google.GroupSize的默认值是48, 是为了更好地对齐sparsegroup结构,在64位系统上,sizeof(sparsegroup)==sizeof(void*)+(GroupSize+7)/8 + 2 == 16).。

 

其实,在intel 和 amd 的新cpu的SSE4.2指令集中,有一个popcnt指令,其它一些cpu中也有popcount指令。并且,在icc/msvc/gcc的新版中都有 intrinsic 支持,这比软件实现要快得多。理想上,google应该在检测有cpu支持时就直接用硬件popcount。

gcc 有以下内建函数(相当于msvc/icc的intrinsic),并且可以用于所有实现了popcount指令的cpu

int __builtin_popcount (unsigned int);
int __builtin_popcountl (unsigned long);
int __builtin_popcountll (unsigned long long);

 

根据我的测试: Intel Xeon 64bit ,64bit popcount, cycle unroll 8

硬件popcount比 google 的软件算法快了30倍,而 sparsetable 查询时最慢的地方就在这里,事实上现在大部分主流cpu都支持硬件popcount,希望google可以改进。

 

要开启gcc硬件popcount,加编译选项 -mpopcnt。

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值