使用整数对的大数据集有效地初始化unordered_map

时间:2015-10-14 01:56:53

标签: c++ performance hashmap unordered-map scientific-computing

我有一个巨大的数组(比如,ParticleId[]唯一整数(表示粒子ID)以随机顺序存储在内存中。我需要构建一个哈希表来将每个ID映射到它在数组内的位置,即从ID到索引。 ID不一定是连续的整数,因此简单的查找数组不是一个好的解决方案。

我目前正在使用c ++ 11的unordered_map容器来实现这一目标。地图用循环初始化:

unordered_map <ParticleId_t, ParticleIndex_t> ParticleHash;
ParticleHash.rehash(NumberOfParticles);
ParticleHash.reserve(NumberOfParticles);
for(ParticleIndex_t i=0;i<NumberOfParticles;i++)
  ParticleHash[ParticleId[i]]=i;

ParticleId_tParticleIndex_t只是typedef-ed整数。 NumberOfParticles可能很大(例如1e9)。就哈希表而言,ParticleId[]数组和NumberOfParticlesconst

目前,如上所述构建unordered_map需要花费大量时间。我的问题是:

  1. 这个问题是unordered_map的最佳选择吗?
    • map初始化会更快,虽然在查找中效率可能不高吗?
  2. 是否可以加快初始化?
    • 使用ParticleHash.insert()ParticleHash[]=快得多吗?或任何其他功能?
    • 鉴于我的密钥已知是唯一整数,有没有办法优化地图和插入?
  3. 我正在考虑将英特尔concurrent_unordered_map并行化。但是,这会引入对intel TBB库的依赖,如果可能的话我想避免使用它。是否有使用本机STL容器的简单解决方案?

    更新

    现在我已经恢复到一个普通的排序索引表并依赖bsearch进行查找。至少表的初始化现在快了20倍,并且可以很容易地并行化。

3 个答案:

答案 0 :(得分:2)

构建查找表的应用程序似乎是内存绑定,而不是cpu绑定。这可以通过分析应用程序的原型来验证。这个答案的其余部分假定这是真的。

构建查找表的过程正在对输入数据进行全局视图,这可能会导致大量内存与磁盘之间的内存交换。

如果是这种情况,该解决方案是一种替代算法,一次处理较小的内存块。 假设有100万个整数。当前进程可能在此时插入到更靠近1的哈希表的低端,并且在下一刻它可能插入接近1百万的高端。这导致了大量的交换。

另一种方法是通过一次处理较小的数据集块来避免交换。我们可以从桶/基数排序借用想法。在这种方法中,构建查找表的步骤将被排序步骤替换。 Bucket / Radix排序应该在线性时间内运行。数据集中的所有整数都是唯一的这一事实是使用这些排序算法的另一个原因。 如果可以组合线性时间排序和交换最小化,则可以提高性能。

答案 1 :(得分:1)

我不认为你可以做很多事情,但这里有一些事情需要尝试。

首先,由于您正在拨打realloc,因此您无需致电rehash

insert可能比operator[]更快,因为operator[]会调用insert以默认值向地图添加元素,然后将您的值分配给新插入的元素,但优化器可能能够消除额外的工作。

仅仅因为是唯一的,这些键的散列值可能不是因为我不认为语言规范要求整数散列返回该整数(描述的部分)无论如何,哈希模板都没有说出来。

&#39;映射&#39;初始化可能会慢一些,因为在插入内容时必须保持树的重新平衡,并且查找速度会慢一些。如果您的map向量可以重新排列,则可以使用的ParticleID的一种替代方法是对向量​​进行排序,然后执行binary_search以查找ID所在的位置并计算索引。但它的性能与map相似,需要重新排列矢量。

如果您决定尝试concurrent_unordered_map,由于线程之间的所有内存争用,您可能会在3或4个线程后看到很多改进。

答案 2 :(得分:0)

给出&#34;以随机顺序存储的大量独特整数&#34; - 有什么东西取决于随机顺序吗?如果没有,只需对就地排列唯一整数数组,并从唯一整数映射到索引,就可以在数组中执行std::lower_bound

如果需要保留巨大数组的预先排序顺序,但是在填充该数组之后,您将构建索引作为一次性步骤(如您的说明性代码所做的那样) ),您可以根据指向的元素创建一个类似的大型ParticleId*std::sort数组(您需要对指向的值进行自定义<比较);之后,您可以使用std::lower_bound进行相同的<比较,以快速查找特定ParticleId的大量数组中的索引。

上述连续数组通过以缓存友好的方式使用连续内存,在性能和内存使用方面获益匪浅。

只有当您需要能够搜索时才会有大量新的ParticleId进入或被删除,您需要考虑std::unordered_map