使用C中的大型结构缓存局部性

时间:2015-10-06 17:39:06

标签: c caching struct

如果我有这样的链接结构:

struct phonebook {
    char LastName[16];
    char FirstName[16];
    char Email[16];
    char PhoneNumber1[10];
    char PhoneNumber2[10];
    char Addr1[16];
    char Addr2[16];
    char City[10];
    char Country[12];
    char State[2];
    struct phonebook *pNext;
}

当我想搜索匹配姓氏的人时,
我可以用

while (pHead != NULL) {
    if (strcasecmp(lastname, pHead->LastName) == 0)
            return pHead;
    pHead = pHead->pNext;
}

return NULL;  
像这样的东西,但每次我得到一个电话簿节点,缓存都会加载整个结构和缓存未命中。 那么,如何提高缓存命中率呢? 如何在缓存中获得分组LastName

没有热/冷或断开链接列表到链接哈希表。

2 个答案:

答案 0 :(得分:1)

正如您所指出的,在一般情况下,链接列表的每个节点都可能指向一个完全不同的地址范围,导致缓存未命中。

如果在构建列表时堆未进行分段,则结构的总内存空间可能是连续的,即使单个节点不是(假设您以某个频率将节点插入链的中间)。如果此时您的堆已碎片化,则列表将进一步传播。

如果您遇到碎片堆,但知道链表的大小有多大,则可以在程序开始时预先分配大块内存,并根据需要进行子分配。这可能会浪费RAM,但与针对已经碎片化的堆分配节点的情况相比,可以减少缓存未命中。

TCMalloc也可以提高缓存命中率,因为它对于小分配来说相当节省空间。它还尝试在同一个4K内存页面中保留连续的小分配。

  

如何改进搜索方式

如果您的链接列表已排序,则仅按一个条件排序。您可以维护一个单独的数据结构(例如哈希表),该结构将特定搜索键(例如LastName + FirstName)映射到链表中该节点的指针。这在概念上类似于数据库如何具有表示行的物理排序的聚集索引以及可能针对不同搜索条件的多个非聚集索引(通过电子邮件,电话,名称搜索)。

答案 1 :(得分:0)

  

每次我收到电话簿节点时,缓存都会加载整个结构并缓存未命中的错误。   那么,我怎样才能提高缓存命中率?   如何在缓存中分组LastNames?

C要求每个struct对象的成员在内存中连续布局(但不一定是连续的)。因此,是的,即使在您认为结构本身可能不连续之前,各种结构的LastName数组也会在内存中展开。您不能更改它,因为它由C指定。

但是,您可以创建一个由较小结构的动态数组组成的索引,例如

struct pb_index {
    char LastName[16];
    struct phonebook *entry;
}

LastName数组在该动态数组中比在phonebook数组中更密集,因此扫描这样的数组将比扫描链接更有效地使用缓存列表。

所有这一切,设置它并保持它看起来像是相当多的工作,可能收益很少。如果您遇到效率问题,那么最好使用能够提供更高效访问的数据结构。哈希表或搜索树可能适合。