添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
  • 本文主要实现不同cache, 是因为之前笔试中有个类似的问题需要实现, 并且在牛客上也有类似的训练题, 因此本文做个总结;
  • cache中使用map作为key-value的存储, 在查询的时候速度会最快
  • 十月一也没啥地去玩, 提高一下自己的业务能力吧

1. 先验知识

1.1 时间相关

#include <chrono> // 时间戳等头文件
#include <unistd.h>  // sleep的头文件
using namespace std;
using namespace std::chrono;
void test(){
    // 通过高精度时钟计算当前时间(毫秒表示), 
    // time_point<high_resolution_clock> m_begin = high_resolution_clock::now(); // 当前时间戳
    auto m_begin = high_resolution_clock::now();
    usleep(50000); //  最小单位是ns, 这里延时50ms
    // sleep(1); // 注意这里是最小单位是s
    int64_t ms = duration_cast<chrono::milliseconds>(high_resolution_clock::now() - m_begin).count(); // 计算过去了多长时间 ms单位
    cout << ms <<endl;
    cout << high_resolution_clock::now().time_since_epoch().count() <<endl; // 当前时间到1970年一月一日过去了多少ms

1.2 容器list 双向链表遍历删除

  • 注意删除的时候一定要把堆上创建的内存delete一下
for(auto it=temp_time.begin(); it!=temp_time.end(); it++){
    while(it!=temp_time.end() && 时间差 > m_time){
        // 清除map中节点
        temp_kv.erase((*it)->key);
        // 清除list中节点
        fd *p = *it;
        it = temp_time.erase(it); // 在链表中删除, 并返回下一个指针
        // 回收堆上
        delete p; 
        m_capacity++;

2. 超时清除

设计个cache, 功能: 有最大cache空间, 最长保存时间; 当cache空间为0时, 不能写入数据, 数据超时后, 清除数据;

  1. set(int key, int value) 存在则更新key, 不存在则插入, 没空间则返回false
  2. get(int key) 存在则返回对应的value, 不存在则返回-1
  3. clean() 清除超时缓存

解决方案: 使用unorderd_map作为key,和fd的数据存储, 作为查询数据的返回空间, 使用list双向链表, 按照节点使用时间顺序保存, 超时清理时也是从头向后清理, 因为链表头是超时的, 链表尾是最新的;

#include <iostream>
#include <algorithm>
#include <vector>
#include <list>
#include <memory>
#include <chrono>
#include <unistd.h>
#include <unordered_map>
#include <queue>
using namespace std;
using namespace std::chrono;
// LRU设计1 超时清理
1. 初始化时, 传入的参数 a.缓存空间最大长度, b.缓存的最长时间
2. set(key:int, value:int)->bool:已经存在(更新),未存在且插入成功(插入)
3. get(key:int) -> value, 没有返回-1
3. clean()->bool:清理所有超时的内容
对于超时清理解决:
struct fd{
    int key; 
    int value;
    timePoint;
map[fd.key] = fd
// https://www.cnblogs.com/zuixime0515/p/10508079.html list使用介绍
维护一个链表, set或者get时, 更新timePoint, 从尾插入链表, 这样就是一个按照timePoint倒叙的链表
clean时, 从头遍历, 如果
struct fd{
    int key; 
    int value;
    time_point<high_resolution_clock> timePoint=high_resolution_clock::now();
    fd(int k, int v): key(k),value(v) {}
class TimeoutCache{
public:
    TimeoutCache(int capacity, int t):m_capacity(capacity),m_time(t){}
    ~TimeoutCache(){
        cout << "析构开始" <<endl;
        for(auto &a : temp_time){
            delete a;
        cout << "析构成功" <<endl;
    // 存在:
    // 不存在: 判断capacity
    bool set(int k, int v){
        cout << "set: ";
        if(temp_kv.count(k) == 1) { // key存在这里, 更新返回
            cout << k << "存在, 历史值:" << temp_kv[k]->value << "更新为:" <<v << endl;
            temp_kv[k]->value = v;
            update_temp_time(k);
        } else if(m_capacity != 0){ // key没在缓存, 并且有空间 插入缓存
            m_capacity--;
            fd * newFd = new fd(k, v);
            temp_kv[k] = newFd;
            insert_temp_time(newFd);
            cout << k << "不存在 插入值" << temp_kv[k]->value << endl;
        } else{ // 没空间
            cout << "空间不足" << k << "插入" << v << "失败" <<endl;
            return false;
        return true;
    // 更新时间为当前时间, 并且将节点放到最尾的位置
    void update_temp_time(int key){ 
        fd *p;
        for(auto it=temp_time.begin(); it!=temp_time.end(); it++){
            if((*it)->key == key){
                p = *it;
                temp_time.erase(it);
                break;
        cout << "提前了: " <<  p->key << " " << p->value <<endl;
        p->timePoint = high_resolution_clock::now();
        temp_time.push_back(p); 
    // temp_time头插节点t
    void insert_temp_time(fd *t){
        temp_time.push_back(t);
    // 得到key对应的value, 并且更新或者提前
    int get(int key){
        cout << "get: ";
        if(temp_kv.count(key) == 1){
            cout << key << " " << temp_kv[key]->value <<endl;
            update_temp_time(key);
            return temp_kv[key]->value;
        }else{
            cout << key << "不存在" << endl;
            return -1;
    // 清除所有超时的节点
    void clean(){
        cout << "clean: " <<endl;
        time_point<high_resolution_clock> now = high_resolution_clock::now();
        for(auto it=temp_time.begin(); it!=temp_time.end(); it++){
            while(it!=temp_time.end() && duration_cast<chrono::milliseconds>(now - (*it)->timePoint).count() > m_time){
                // 清除map中节点
                cout << (*it)->key << "在temp_kv已经清除, 因为时间到了.. " <<endl;
                temp_kv.erase((*it)->key);
                // 清除list中节点
                fd *p = *it;
                it = temp_time.erase(it); // 在链表中删除, 并返回下一个指针
                // 回收堆上
                delete p; 
                m_capacity++;
        }break;
private:
    unordered_map<int, fd*> temp_kv;
    list<fd*> temp_time; // 负责超时删除, 更新重排
    int m_capacity; // 最大容量
    int m_time; // 最大存在时间
void test(){
    TimeoutCache *myCache = new TimeoutCache(3, 50); // 3个空间, 4个时间长
    myCache->get(1);
    myCache->set(1,1);
    myCache->set(1,13);
    myCache->set(2,435);
    myCache->set(3,32);
    usleep(40000);
    myCache->set(1,32);
    usleep(30000); // 100毫秒过去了,么得了
    myCache->clean(); // 这里应该清除23, 保留1
    myCache->set(5,1);
    myCache->get(1);
    myCache->set(8,1);
    delete myCache;
int main(int argc, char const *argv[]){
    test();
    return 0;
(base) zjq@DESKTOP-82TMKG6:LeetCode101$ ./"test" 
get: 1不存在
set: 1不存在 插入值1
set: 1存在, 历史值:1更新为:13
提前了: 1 13
set: 2不存在 插入值435
set: 3不存在 插入值32
set: 1存在, 历史值:13更新为:32
提前了: 1 32
clean: 
2在temp_kv已经清除, 因为时间到了.. 
3在temp_kv已经清除, 因为时间到了.. 
set: 5不存在 插入值1
get: 1 32
提前了: 1 32
set: 8不存在 插入值1

3. 最不经常使用LFU

LeetCode跳转

定义两个哈希表,第一个 freq_table 以频率 freq 为索引,每个索引存放一个双向链表,这个链表里存放所有使用频率为 freq 的缓存,缓存里存放三个信息,分别为键 key,值 value,以及使用频率 freq。第二个 key_table 以键值 key 为索引,每个索引存放对应缓存在 freq_table 中链表里的内存地址,这样我们就能利用两个哈希表来使得两个操作的时间复杂度均为 O(1)O(1)。同时需要记录一个当前缓存最少使用的频率 minFreq,这是为了删除操作服务的。

// 缓存的节点信息
struct Node {
    int key, val, freq;
    Node(int _key,int _val,int _freq): key(_key), val(_val), freq(_freq){}
class LFUCache {
    int minfreq, capacity;
    unordered_map<int, list<Node>::iterator> key_table;
    unordered_map<int, list<Node>> freq_table;
public:
    LFUCache(int _capacity) {
        minfreq = 0;
        capacity = _capacity;
        key_table.clear();
        freq_table.clear();
    int get(int key) {
        if (capacity == 0) return -1;
        auto it = key_table.find(key);
        if (it == key_table.end()) return -1;
        list<Node>::iterator node = it -> second;
        int val = node -> val, freq = node -> freq;
        freq_table[freq].erase(node);
        // 如果当前链表为空,我们需要在哈希表中删除,且更新minFreq
        if (freq_table[freq].size() == 0) {
            freq_table.erase(freq);
            if (minfreq == freq) minfreq += 1;
        // 插入到 freq + 1 中
        freq_table[freq + 1].push_front(Node(key, val, freq + 1));
        key_table[key] = freq_table[freq + 1].begin();
        return val;
    void put(int key, int value) {
        if (capacity == 0) return;
        auto it = key_table.find(key);
        if (it == key_table.end()) {
            // 缓存已满,需要进行删除操作
            if (key_table.size() == capacity) {
                // 通过 minFreq 拿到 freq_table[minFreq] 链表的末尾节点
                auto it2 = freq_table[minfreq].back();
                key_table.erase(it2.key);
                freq_table[minfreq].pop_back();
                if (freq_table[minfreq].size() == 0) {
                    freq_table.erase(minfreq);
            freq_table[1].push_front(Node(key, value, 1));
            key_table[key] = freq_table[1].begin();
            minfreq = 1;
        } else {
            // 与 get 操作基本一致,除了需要更新缓存的值
            list<Node>::iterator node = it -> second;
            int freq = node -> freq;
            freq_table[freq].erase(node);
            if (freq_table[freq].size() == 0) {
                freq_table.erase(freq);
                if (minfreq == freq) minfreq += 1;
            freq_table[freq + 1].push_front(Node(key, value, freq + 1));
            key_table[key] = freq_table[freq + 1].begin();

方法2. LFU双向链表std::list解法

解题思路:

  1. 最长不使用LFU
  2. 每个节点都有个计数器, 每次put或者get都对应+1
  3. 删除的时候是根据使用频率低的删除, 因此使用min_作为频率最低的标记
  4. temp_存储的是<key, {key, value, freq}>
  5. freqMap存储的是<freq, list<{key, value, freq}>> 所以每次删除都是删除freq最小的list里面的front, 因为新节点是push_back后插

注意
由于每次需要从freqMap中取出对应freqlist, 修改这个list, 因此需要在取出的过程使用&作为引用, 不然修改list也没有修改freqMap的数据;

#include <list>
#include <unordered_map>
#include <iostream>
using namespace std;
struct Node{
    int key, value;
    int freq = 1;
    Node(int k, int v): key(k), value(v){}
class LFUCache {
private:
    unordered_map<int, Node*> temp_;
    // 存储每个频次对应的双向链表 <频次, 相同频次的节点双向链表
    unordered_map<int, list<Node*>> freqMap_; 
    int size_;
    int capacity_;
    int min_; // 当前最小频次
public:
    LFUCache(int capacity) : capacity_(capacity), size_(0), min_(0){
    int get(int key) {
        if(temp_.count(key) == 0){
            return -1;
        freqInc(temp_[key]);
        return temp_[key]->value;
    void put(int key, int value) {
        if(temp_.count(key) != 0){
            temp_[key]->value = value;
            freqInc(temp_[key]);
        } else {
            // 删掉一个缓存
            if(size_ >= capacity_){
                Node *deadNode = removeNode(); 
                temp_.erase(deadNode->key);
                size_--;
            Node *newNode = new Node(key, value);
            temp_[key] = newNode;
            addNode(newNode);
            size_++;
    // 频率计数器, 取出节点, 在频率+1后放到对应的频率里面
    void freqInc(Node* node){
        // 在频率的双向链表中删除, 更新min
        int freq = node->freq;
        // list<Node*> listN = freqMap_[freq]; // 这里是深拷贝给listN而不是把指针给他了
        list<Node*> &listN = freqMap_[freq];  // 引用
        listN.remove(node); 
        if(freq == min_ && listN.size()==0){
            min_ = freq+1;
        // 加入新freq对应链表 (freq+1)的链表
        node->freq++;
        if(freqMap_.count(node->freq) == 0){
            list<Node*> listN;
            listN.push_back(node);
            freqMap_[node->freq] = listN;
        freqMap_[node->freq].push_back(node);
        return;
    // 增加节点
    void addNode(Node *node){
        // 如果没有次数为1的, 创建双向链表, 加入到频率map中
        if(freqMap_.count(1) == 0){
            list<Node*> listN;
            freqMap_[1] = listN; 
        freqMap_[1].push_back(node);
        min_ = 1;
        return;
    // 移除节点
    Node *removeNode(){
        // 注意这里一定要使用引用, 不然修改list并没有修改freqMap_里面的数据
        list<Node*> &minList = freqMap_[min_]; 
        Node* deadNode = minList.front();
        minList.pop_front();
        return deadNode;
    void printTemp(){
        for(auto &a : temp_){
            cout<< a.first << " " << a.second->value << " " << a.second->freq << "; ";
        }cout << endl;
 * Your LFUCache object will be instantiated and called as such:
 * LFUCache* obj = new LFUCache(capacity);
 * int param_1 = obj->get(key);
 * obj->put(key,value);
int main(int argc, char const *argv[])
    LFUCache *lFUCache = new LFUCache(2);
    lFUCache->put(1, 1);   // cache=[1,_], cnt(1)=1
    lFUCache->put(2, 2);   // cache=[2,1], cnt(2)=1, cnt(1)=1
    lFUCache->printTemp();
    lFUCache->get(1);      // 返回 1
    lFUCache->printTemp();
                        // cache=[1,2], cnt(2)=1, cnt(1)=2
    lFUCache->put(3, 3);   // 去除键 2 ,因为 cnt(2)=1 ,使用计数最小
    lFUCache->printTemp();
                        // cache=[3,1], cnt(3)=1, cnt(1)=2
    lFUCache->get(2);      // 返回 -1(未找到)
    lFUCache->get(3);      // 返回 3
    lFUCache->printTemp();
                        // cache=[3,1], cnt(3)=2, cnt(1)=2
    lFUCache->put(4, 4);   // 去除键 1 ,1 和 3 的 cnt 相同,但 1 最久未使用
    lFUCache->printTemp();
                        // cache=[4,3], cnt(4)=1, cnt(3)=2
    lFUCache->get(1);      // 返回 -1(未找到)
    lFUCache->get(3);      // 返回 3
                        // cache=[3,4], cnt(4)=1, cnt(3)=3
    lFUCache->get(4);      // 返回 4
                        // cache=[3,4], cnt(4)=2, cnt(3)=3
    return 0;

4. 经常使用LRU

设计LRU缓存结构,该结构在构造时确定大小,假设大小为K,并有如下两个功能 set(key, value):将记录(key, value)插入该结构 get(key):返回key对应的value值 [要求] set和get方法的时间复杂度为O(1) 某个key的set或get操作一旦发生,认为这个key的记录成了最常使用的。 当缓存的大小超过K时,移除最
不经常使用的记录,即set或get最久远的。 若opt=1,接下来两个整数x, y,表示set(x, y) 若opt=2,接下来一个整数x,表示get(x),若x未出现过或已被移除,则返回-1 对于每个操作2,输出一个答案

方案一: 队列(差)

解题思路:

  1. 使用unordered_map存储数据
  2. 使用list作为queue的存储最常使用放到队列尾部, 也就是list头部
  3. 每次set或者get一个值,先从queue中拿出来, 在放到queue头部, 这样就保证每次处理key , 都能将key置前了
输入:[[1,1,1],[1,2,2],[1,3,2],[2,1],[1,4,4],[2,2]],3
返回值:[1,-1]
第一次操作后:最常使用的记录为("1", 1)
第二次操作后:最常使用的记录为("2", 2),("1", 1)变为最不常用的
第三次操作后:最常使用的记录为("3", 2),("1", 1)还是最不常用的
第四次操作后:最常用的记录为("1", 1),("2", 2)变为最不常用的
第五次操作后:大小超过了3,所以移除此时最不常使用的记录("2", 2),加入记录("4", 4),并且为最常使用的记录,然后("3", 2)变为最不常使用的记录
#include <iostream>
#include <vector>
#include <algorithm> // reverse
#include <sstream> // 为了拆解单词
#include <string> // 为了getline带空格的字符串
#include <unordered_map>
#include <list>
using namespace std;
struct Node{
    int key, val;
    Node(int k, int v):key(k),val(v){}
class Solution{
    list<Node> queue_node;
    // vector<Node> queue_node;
    unordered_map<int, list<Node>::iterator> map_node;  // 这里的map'val是Node的迭代器, 用来保存key, 和迭代器来直接找到queue
    int k;
public:
    Solution(){}
    // 删除
    int remove(list<Node>::iterator &ite){
        int k=ite->key;
        int v=ite->val;
        queue_node.erase(ite);
        // queue_node.erase(ite);
        map_node.erase(k);
        return v;
    // 给队列和map添加值
    void add(int key, int val){
        // cout<< queue_node.begin()->key<<endl;
        // if(queue_node.size()==0) queue_node.push_back(Node(key, val));
        // else queue_node.insert(queue_node.begin(), Node(key, val));
        // cout<< queue_node.begin()->key<<endl;
        queue_node.push_front(Node(key, val));
        map_node[key] = queue_node.begin(); // 相当于把当前这个节点的指针保存到了map中
        if(queue_node.size() > this->k){
            auto last_node = queue_node.end(); // 得到最后一位的下一位 xxx#的#
            --last_node;
            remove(last_node);
    // set
    void set(int key, int val){
        auto ite = map_node.find(key);  // 判断当前key在不在
        if(ite != map_node.end()) remove(ite->second); // 找到了则需要删除在重新在头插入
        add(key, val);
    int get(int key){
        auto ite = map_node.find(key);
        if(ite != map_node.end()){  // 如果当前key已经在map里面, 则删除map在重新插入, 以此提前
            int val = remove(ite->second); 
            add(key, val); 
            return val;
        return -1;
    vector<int> LRU(vector<vector<int>> &operators, int k)
        // write code here
        this->k = k;
        vector<int> ans;
        for (auto &input : operators)
            if (input[0] == 1)
                set(input[1], input[2]);
            else if(input[0] == 2)
                ans.push_back(get(input[1]));
        return ans;
int main(){
    vector<vector<int>> input = {{1,1,1},{1,2,2},{1,3,2},{2,1},{1,4,4},{2,2}};
    // Solution s = new Solution();
    Solution s;
    s.LRU(input, 3);
    return 0;

方案二: 双向链表(优秀)

方案一中, 使用队列进行排序, 但是这样做需要每次都要遍历一遍队列, 而如果利用双向链表作为节点存储, 就可以直接将其中的一个node提前或者删除
解题思路:

  1. 创建双向链表, 其中的key和value对应cache里面的key和value
  2. 双向链表的方法包括
    1. 把一个节点放到双向链表的前面 head←→node←→head→next
    2. 把末尾的节点删除 node→prev←→node←→tail 转为 node→prev←→tail
    3. 移除节点 直接前驱跟后继向连
    4. 移动节点到头部 (先移除节点3, 然后将节点加头部1)
// 双向链表
struct DoubleLinkedNode {
    int key, value;
    DoubleLinkedNode *prev;
    DoubleLinkedNode *next;
    DoubleLinkedNode(): key(0), value(0), prev(nullptr), next(nullptr){}
    DoubleLinkedNode(int k, int v):key(k), value(v), prev(nullptr), next(nullptr){}
// 每个cache保存的是双向链表的节点, temp[key] = DoubleLinkedNode(key, value)
// 这样就可以在get的时候, 将节点key, 从双向链表中, 放到最前面
// 在put时, 将节点key, 直接放到双向链表前面
class LRUCache {
private:
    unordered_map<int, DoubleLinkedNode*> temp_;
    int capacity_;
    DoubleLinkedNode *head_ = nullptr;
    DoubleLinkedNode *tail_ = nullptr;
    int size_;
public:
    LRUCache(int capacity) : capacity_(capacity), size_(0) {
        // 创建个伪头和伪尾部节点
        // head_(0,0)←→tail_(0,0)
        this->head_ = new DoubleLinkedNode();
        this->tail_ = new DoubleLinkedNode();
        this->head_->next = this->tail_;
        this->tail_->prev = this->head_;
    int get(int key) {
        if(temp_.count(key) == 0){
            return -1;
        }else{
            // 更新key
            DoubleLinkedNode* node  = temp_[key];
            moveTohead(node);
            return node->value;
    void put(int key, int value) {
        if(temp_.count(key) == 0){
            DoubleLinkedNode* node = new DoubleLinkedNode(key, value);
            temp_[key] = node;
            addToHead(node);
            size_++;
            if(size_ > capacity_){
                DoubleLinkedNode* removedNode = removeTail();
                temp_.erase(removedNode->key);
                delete removedNode;
                size_--;
        }else{
            DoubleLinkedNode* node = temp_[key];
            node->value = value;
            moveTohead(node);
    // 节点加到最前面
    void addToHead(DoubleLinkedNode* node){
        // head_ ←→ node ←→ head_->next
        node->prev = head_;
        node->next = head_->next;
        head_->next->prev = node;
        head_->next = node;
    // 移除节点
    void removeNode(DoubleLinkedNode *node){
        node->prev->next = node->next;
        node->next->prev = node->prev;
    // 删除尾部节点
    DoubleLinkedNode *removeTail(){
        DoubleLinkedNode *node = tail_->prev;
        removeNode(node);
        return node;
    // 移动节点到头
    void moveTohead(DoubleLinkedNode *node){
        removeNode(node);
        addToHead(node);
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache* obj = new LRUCache(capacity);
 * int param_1 = obj->get(key);
 * obj->put(key,value);
                                    当你在某个缓存中存储数据时,常常需要在运行时调整该缓存的大小,以便能容纳更多的数据。
  下面是一个增加初始缓存大小的例子:
  view plaincopy to clipboardprint?
  // console.cpp : Defines the entry point for the console application.
  #include "stdafx.h"
  #include <iostream>
  #include <algorithm>
  using namespace std;
  int reallocate(int* &p,
                                    如果我们从网络端接收的数据是不定长度的,不能提前分配好网络缓存大小,则可以选择使用链式缓存实现方式。链式缓存主要是分配固定大小的缓存链保存数据,一个管理器去管理这些缓存链。
链式缓存示例:
//一种链式缓冲
//ringbuffer是固定长度,不能够扩展,链式buffer可以任意扩充
 * buffer
 *     *first--chain buffer [1024]
 *  ...
缓存的应用场景和范围十分广泛,下面给出其十分常见的两种应用背景:
首先,在操作系统内部,由于内存资源十分有限,而每个进程又都希望独享一块很大的内存空间。所以诞生了一种“虚拟内存”机制,它将进程的一部分内容暂留在磁盘中,在需要时再进行数据交换将其放入内存,这个过程就需要用到缓存算法机制进行置换。
其次,对于各类应用项目开发而言,在巨大的数据量面前,Cache是不可或缺的。因为无论是针对本地端的浏览器缓存,还是针...
                                    最最最简单的C++缓存实现
在平常的后台开发中,通常中会用到缓存,一般会使用redis等内存数据库来实现,但是在很简单的程序中,其实没必要包含一些额外的依赖,通过C++的map即可实现。
                                    0方式的 cpu_core/L1-dcache-load-misses/  是36,246 , cpu_core/L1-dcache-load/ 是848,148,941,命中率为0.999957265。1方式的 cpu_core/L1-dcache-load-misses/  是38,540 , cpu_core/L1-dcache-load/ 是848,192,764,命中率为0.999954562。所以我们写代码时应该多注意对齐、以及cache这些问题,感兴趣的同学还可以多试试不以64对齐的情况。
缓存的应用场景和范围十分广泛,下面给出其十分常见的两种应用背景:
首先,在操作系统内部,由于内存资源十分有限,而每个进程又都希望独享一块很大的内存空间。所以诞生了一种“虚拟内存”机制,它将进程的一部分内容暂留在磁盘中,在需要时再进行数据交换将其放入内存,这个过程就需要用到缓存算法机制进行置换。
其次,对于各类应用项目开发而言,在巨大的数据量面前,Catch 是不可或缺的。因为无论是针对本地端的浏览器缓存,还...
                                    优化存储访问优化存储访问代码和数据缓存缓存组织一起使用的函数应该存储在一起一起使用的变量应该存储在一起数据对齐动态内存分配数据结构和容器类字符串顺序访问数据大数据结构中的cache冲突显式cache控制
优化存储访问
代码和数据缓存
缓存是主存的代理。缓存是为了以最快的可能访问最常用的数据。
缓存组织
大多数chache以行和集合的方式组织。cache机制的更多细节,参考(en.wikipedia.org/wiki/L2_cache)。
如果程序中包含很多变量和对象,它们又刚好分布在映射到相同cache的内
                                        C++中可以采用stream读取文本文件,基本方式是一次一行,编程简洁易行,比用C方便多了。但是,凡事有利有弊,当文件行数较多时,文件读取IO次数就会随之增加,文件读取的时间会急剧增长。因为文件IO的时间要远大于CPU在内存中处理数据的时间,假如IO时间是毫秒级的,那么CPU在内存处理数据是纳秒级的。    很显然,C++中文本文件读取优化要解决的基本问题之一就是减少IO次数,最常用的