Cache命中率是指在计算机系统中,CPU向Cache中请求数据,如果Cache中有所需数据则称为cache命中,否则称为cache未命中。Cache命中率是衡量计算机系统效率的一个重要指标。
一、提高Cache利用率
Cache的大小有限,因此需要尽可能的提高Cache的利用率。
1.1 数据局部性原理
数据局部性是指程序访问的数据集中在某一部分连续的地址空间中,存在着时间和空间局部性。
int sum = 0; for(int i = 0; i < 10000; i++) { sum += A[i]; }
在上述代码中,数组A的大小远远超出Cache的大小,如果每次都从内存中读取一次则会造成较大的Cache未命中。但是由于循环访问A数组存在较强的时间和空间局部性,可以采用Cache预先装入A数组中某一部分数据来减少Cache未命中率。
1.2 Cache预取
Cache预取是指在Cache未命中时提前对数据进行预取,目的是通过预先将数据载入Cache来降低Cache未命中率。
int sum = 0; for(int i = 0; i < 10000; i++) { sum += A[i] + A[i+1]; }
在上述代码中,每次访问A[i]时都需要访问A[i+1],如果每次都从内存中读取则会造成较大的Cache未命中。可以在每次访问A[i]时预取A[i+1]。
二、优化数据结构
优化程序的数据结构可以有效减少Cache未命中率。
2.1 Cache友好的数据结构
Cache友好的数据结构就是指能够利用Cache的时间和空间局部性的数据结构。
struct Node { int val; Node* next; }; int sum = 0; Node* cur = head; while(cur != NULL) { sum += cur->val; cur = cur->next; }
在上述代码中,每次遍历链表都需要跳转到下一个节点,而Chain表节点通常是通过指针来实现,因此每次跳转时都需要读取下一个节点指针指向的地址,如果多次跳转Cache未命中率会很高。此时可以通过引入指针数组来优化数据结构。
struct Node { int val; int next_idx; }; int sum = 0; int cur_idx = head; while(cur_idx != -1) { sum += node[cur_idx].val; cur_idx = node[cur_idx].next_idx; }
2.2 数据对齐
数据对齐是指数据存储时按照一定规则排列,通常会使得数据在Cache中的存放位置更为连续。
struct Node { int val; char pad[60]; Node* next; };
上述代码中的节点结构体大小为64字节,在x86架构下Cache行为64字节, 由于每次读取都是以64字节为单位,因此循环访问链表时,如果下一个节点跨越了整个Cache行,则会导致读取整个Cache行,浪费了大量的内存带宽和内存访问时间。可以通过在每个节点结构体中插入一定数量的填充字段来使得节点大小变为64字节的整数倍,从而避免上述情况的发生。
三、Cache替换策略
在Cache中存储的数据是有限的,如果Cache已满,则需要替换部分数据。Cache替换策略有多种,常见的有随机置换、先进先出置换、最少使用置换、最近不经常使用置换等。
3.1 最近不经常使用
最近不经常使用是指选择最近一段时间内未访问或访问次数最少的数据进行替换。
// 定义Cache中每个块的访问计数器 int counters[Cache_size]; // 在Cache未命中时,根据最近不经常使用法选择替换块 int min_idx = 0; for(int i = 1; i < Cache_size; i++) { if(counters[i] < counters[min_idx]) { min_idx = i; } } Cache_replace(min_idx, input); counters[min_idx] = 0; // 重置计数器
3.2 Least Recently Used(LRU)
最近最少使用是指选择最近一段时间内没有被访问过的数据进行替换,该策略通常采用链表来维护最近访问顺序。
// 使用链表+哈希表来实现LRU Cache class LRUCache { public: LRUCache() {} int get(int key) {} void put(int key, int value) {} private: list<pair> cache_; unordered_map<int, list<pair>::iterator> map_; int capacity_; };
四、总结
Cache命中率是计算机系统效率的重要指标之一,通过提高Cache利用率、优化数据结构、选择合适的换出策略可以有效降低Cache未命中率,提高程序运行效率。