unordered_map 查找数组的索引
Posted
技术标签:
【中文标题】unordered_map 查找数组的索引【英文标题】:unordered_map to find indices of an array 【发布时间】:2020-10-30 21:59:27 【问题描述】:我想有效地找到一个集合的索引。我正在使用 unordered_map 并像这样制作逆映射
std::unordered_map <int, int> myHash (size);
Int i = 0;
for (it = someSet.begin(); it != someSet.end(); it++)
myHash.insert(*it , i++);
它有效,但效率不高。我这样做了,所以只要我需要索引,我就可以访问它们 O(1)。性能分析显示这部分成为我代码的热点。
VTune 告诉我 new
运营商是我的热点。我猜 unordered_map 内部正在发生一些事情。
在我看来,这个案子应该得到有效处理。我还没有找到好的方法。有更好的解决方案吗?一个正确的构造函数?
也许我应该将更多信息传递给构造函数。我查看了初始化列表,但它并不是我想要的。
更新:让我添加更多信息。套装没那么重要;我将集合保存到一个数组中(排序)。稍后我需要找到唯一值的索引。我可以在登录中做到这一点,但速度不够快。这就是我决定使用哈希的原因。集合的大小(子矩阵的列)在此之后不会改变。
它来自稀疏矩阵计算,我需要在更大的矩阵中找到子矩阵的索引。因此,查找的大小和模式取决于输入矩阵。它适用于较小的问题。我可以使用查找表,但是当我计划并行执行时,每个线程的查找表可能很昂贵。我在创建时具有哈希的确切大小。我认为通过将它发送给构造函数它会停止重新分配。我真的不明白为什么要重新分配这么多。
【问题讨论】:
Int
?你的意思是int
?
你要转换多少个元素?你在做多少次查找?创建查找表的费用可能超过您获得的任何节省,因此它可能是错误的优化。有一些阈值,元素数量 > N 和查找数量 > M 产生正面结果,但低于该阈值实际上是净负面结果。
为什么要一个 set 元素的索引?即使你拥有它,访问元素(使用 std::distance()
也是 O(n)。
@ALX23z std::set 在调整大小时确实无效,它没有调整大小...
这个问题很可能是由于数组的大小。由于过大的碎片分配,使查找太大肯定会导致问题。考虑为您的项目解决算法问题。尝试以其他方式查找索引或使用pmr
分配unordered_map
。如果您只是添加元素,也许您可以进行大量预订,然后将元素一个接一个地放置
【参考方案1】:
问题是,std::unordered_map
,主要实现为向量列表,对缓存非常不友好,并且在使用小键/值时表现尤其差(例如 int,int
在您的情况下),更不用说需要大量(重新)分配。
作为替代方案,您可以尝试使用 linear probing 实现 open addressing 的第三方哈希映射(虽然很复杂,但底层结构只是一个向量,即对缓存更友好)。例如,谷歌的dense_hash_map
或这个:flat_hash_map
。两者都可以用作unordered_map
的直接替代品,并且只需要另外指定一个int
值作为“空”键。
【讨论】:
std::unordered_map
在重新分配方面没有任何问题。也许查找表需要这些但不是基本元素。虽然它确实进行了大量分配,因此不建议使用大哈希。
我最终使用线性探测实现了我自己的哈希。效率更高。【参考方案2】:
std::unordered_map
std::vector<std::list<std::par<int, int>>>
这会导致每个节点的大量分配和解除分配,每个(解除)分配都使用会导致争用的锁。
您可以通过使用 emplace 代替 insert 来帮助它,或者您可以跳入 pmr 分配器的奇妙新世界。如果您对 pmr::unordered_map 的创建和销毁是单线程的,那么您应该能够从中获得很多额外的性能。请参阅 Jason Turners C++ Weekly - Ep 222 - 3.5x Faster Standard Containers With PMR!,他的示例有点小,但您可以大致了解。
【讨论】:
问题的描述是正确的,但我不太确定 PMR 是最好的建议。谷歌的哈希表被广泛使用,还有其他更快的选择 - probablydance.com/2017/02/26/i-wrote-the-fastest-hashtable 是一个很好的阅读。以上是关于unordered_map 查找数组的索引的主要内容,如果未能解决你的问题,请参考以下文章