std::unordered_set如何避免迭代中每个元素上潜在的页面错误

How does an std::unordered_set avoid potential page faults on every element in iteration?

本文关键字:元素 错误 set unordered 何避免 迭代 std      更新时间:2023-10-16

还是这样?

澄清一下:在std::vector中,如果在迭代期间访问了当前不在缓存到内存中的页面上的元素,则存在页面错误,并且该页面已加载,但随后的k-1元素保证缓存到内存,其中k是适合内存页面的元素数。

在我看来,在像std::unordered_set这样的哈希表数据结构中,在迭代过程中,根本不能保证访问的任何元素的后续元素都会在内存中接近它,这意味着在具有大量元素的生成条件下,迭代过程中访问的每个元素都可能出现页面错误。我错了吗?如果是,std::unordered_set使用什么方法来防止这种情况发生?

只是好奇。我正在概述我现在计划实施的一个算法,并试图了解它在退化条件下如何在有限的内存和大量元素的情况下预成型,而我对std::unordered_set如何在引擎盖下工作的不完全了解是一个限制因素。

没有。std::unordered_set是使用哈希表实现的,带有单独的链接。也就是说,每个bucket都有一个链表,用来存储bucket中的项目。这意味着,当您一个桶接一个桶地迭代时,每次都必须取消引用指向动态内存的指针,并冒着缓存未命中的风险

如果你想要类似的东西,它使用连续内存,从而在迭代时避免页面错误,你可以使用一个带有开放寻址的哈希表。例如,你可以使用线性探测,或者使用杜鹃散列之类的东西。但是您必须自己实现这些,或者使用其他人的非标准容器库。这些类型的映射和集合不能完全以尊重标准映射和集合的迭代器无效要求的方式来完成。

无序和有序的关联容器都必然受到引用内存不连续性的影响。

我写了"必然",因为C++标准坚持认为,只要元素没有从容器中删除,指向关联容器中元素的指针和引用就会保持有效。这使得某些技术无法改进引用的局部性,这将需要移动元素来改进存储器布局。

内存分散不仅(甚至主要)会导致过多的分页。最明显的后果通常是内存缓存利用率降低,这会显著增加运行时间。(显然,过度的交换也会产生巨大的影响,但如果你把我们限制在内存中,无论哈希表引用的位置如何,你都会遇到问题。)

缓存感知算法确实存在,但它们不经常使用有几个原因:

  1. 优化仅在一些非常特定的用例中有用。

  2. 在不了解大量被散列的数据的情况下,很难编写一个缓存感知算法。通用库函数可以处理任何数据类型,很少为特定的数据类型提供优化的专业化。

  3. 标准关联容器的引用稳定性在大量用例中非常有用。

无序列表可以被认为是一个向量(由键的散列键控),具有类似散列元素的链表。

通过无序列表的迭代迭代会迭代向量(共享相同的页面错误模式),然后是链表。如果没有项目共享一个槽,它将具有与向量相同的读取模式。

由于它是无序的,所以这个迭代是数据的随机视图。