页面置换算法 - FIFO、LFU、LRU

 

 

缓存算法(页面置换算法)-FIFO. LFU. LRU

  在前一篇文章中通过leetcode的一道题目了解了LRU算法的具体设计思路,下面继续来探讨一下另外两种常见的Cache算法:FIFO. LFU

1.FIFO算法

  FIFO(First in First out),先进先出. 其实在操作系统的设计理念中很多地方都利用到了先进先出的思想,比如作业调度(先来先服务),为什么这个原则在很多地方都会用到呢?因为这个原则简单. 且符合人们的惯性思维,具备公平性,并且实现起来简单,直接使用数据结构中的队列即可实现.

  在FIFO Cache设计中,核心原则就是:如果一个数据最先进入缓存中,则应该最早淘汰掉. 也就是说,当缓存满的时候,应当把最先进入缓存的数据给淘汰掉. 在FIFO Cache中应该支持以下操作;

  get(key):如果Cache中存在该key,则返回对应的value值,否则,返回-1;

  set(key,value):如果Cache中存在该key,则重置value值;如果不存在该key,则将该key插入到到Cache中,若Cache已满,则淘汰最早进入Cache的数据.

  举个例子:假如Cache大小为3,访问数据序列为set(1,1),set(2,2),set(3,3),set(4,4),get(2),set(5,5)

  则Cache中的数据变化为:

  (1,1) set(1,1)

  (1,1) (2,2) set(2,2)

  (1,1) (2,2) (3,3) set(3,3)

  (2,2) (3,3) (4,4) set(4,4)

  (2,2) (3,3) (4,4) get(2)

  (3,3) (4,4) (5,5) set(5,5)

  那么利用什么数据结构来实现呢?

  下面提供一种实现思路:

  利用一个双向链表保存数据,当来了新的数据之后便添加到链表末尾,如果Cache存满数据,则把链表头部数据删除,然后把新的数据添加到链表末尾. 在访问数据的时候,如果在Cache中存在该数据的话,则返回对应的value值;否则返回-1. 如果想提高访问效率,可以利用hashmap来保存每个key在链表中对应的位置.

#include <bits/stdc++.h>
using namespace std;


// FIFO 先进先出原则
class Solution
{
public :
    Solution( int si)
    {
        _size = si;
        top_idx = 0; // 队列top的下标
        cache . clear();
        exist . clear();
    }
    int check_page( int k)
    {
        if( exist . count( k) >= 1) //hit the target
            return k;

        // not exist on cache
        if( cache . size() < _size)
        {
            cache . push_back( k);
            exist . insert( k);
        }
        else // replace
        {
            exist . erase( cache [ top_idx ]);
            exist . insert( k);
            cache [ top_idx ] = k;
            ++ top_idx;
            top_idx %= _size;
        }
        return - 1;
    }

private :
    int _size , top_idx;
    vector < int > cache; // 模拟队列
    set < int > exist;
};

/**<
改进:
1.如果页面驻留集(cache)的大小很小的话,没必要使用set来判断是否存在于驻留集中,直接扫一遍来查找,节约了空间
*/

int main()
{
    freopen( "H: \\ Code_Fantasy \\ in.txt" , "r" , stdin);
    int n , page_number;
    while( cin >>n)
    {
        int miss = 0;
        Solution solution( 3); // set the cache size
        for( int i = 0; i <n; ++ i)
        {
            cin >> page_number;
            if( solution . check_page( page_number) ==- 1)
                ++ miss;
        }
        cout << "Total missing page: " << miss << endl;
        cout << "The shooting rate is: " << 1.0 -( 1. * miss /n) << endl;
        cout << "=====================================End." << endl;
    }
    return 0;
}
/*
12
1 2 3 4 1 2 5 1 2 3 4 5

17
7 0 1 2 0 3 0 4 2 3 0 3 2 1 2 0 1
*/

2.LFU算法

  LFU(Least Frequently Used)最近最少使用算法. 它是基于“如果一个数据在最近一段时间内使用次数很少,那么在将来一段时间内被使用的可能性也很小”的思路.

  注意LFU和LRU算法的不同之处,LRU的淘汰规则是基于访问时间,而LFU是基于访问次数的. 举个简单的例子:

  假设缓存大小为3,数据访问序列为set(2,2),set(1,1),get(2),get(1),get(2),set(3,3),set(4,4),

  则在set(4,4)时对于LFU算法应该淘汰(3,3),而LRU应该淘汰(1,1).

  那么LFU Cache应该支持的操作为:

  get(key):如果Cache中存在该key,则返回对应的value值,否则,返回-1;

  set(key,value):如果Cache中存在该key,则重置value值;如果不存在该key,则将该key插入到到Cache中,若Cache已满,则淘汰最少访问的数据.

  为了能够淘汰最少使用的数据,因此LFU算法最简单的一种设计思路就是:利用一个数组存储数据项,用hashmap存储每个数据项在数组中对应的位置,然后为每个数据项设计一个访问频次,当数据项被命中时,访问频次自增,在淘汰的时候淘汰访问频次最少的数据. 这样一来的话,在插入数据和访问数据的时候都能达到O(1)的时间复杂度,在淘汰数据的时候,通过选择算法得到应该淘汰的数据项在数组中的索引,并将该索引位置的内容替换为新来的数据内容即可,这样的话,淘汰数据的操作时间复杂度为O(n).

  另外还有一种实现思路就是利用小顶堆+hashmap,小顶堆插入. 删除操作都能达到O(logn)时间复杂度,因此效率相比第一种实现方法更加高效.

  如果哪位朋友有更高效的实现方式(比如O(1)时间复杂度),不妨探讨一下,不胜感激.

3.LRU算法

  LRU算法的原理以及实现在前一篇博文中已经谈到,在此不进行赘述:

  http://www.cnblogs.com/dolphin0520/p/3741519.html

  参考链接:http://blog.csdn.net/hexinuaa/article/details/6630384

       http://blog.csdn.net/beiyetengqing/article/details/7855933

       http://blog.csdn.net/alexander_xfl/article/details/12993565

       http://outofmemory.cn/wr/?u=http%3A%2F%2Fblog.csdn.net%2Fyunhua_lee%2Farticle%2Fdetails%2F7648549 

 

/**
* -----------------------------------------------------------------
* Copyright (c) 2016 crazyacking.All rights reserved.
* -----------------------------------------------------------------
*       Author: crazyacking
*       Date  : 2016-03-15-20.01
*/
#include <queue>
#include <cstdio>
#include <set>
#include <string>
#include <stack>
#include <cmath>
#include <climits>
#include <map>
#include <cstdlib>
#include <iostream>
#include <list>
#include <vector>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long( LL);
typedef unsigned long long( ULL);
const double eps( 1e-8);

struct Node
{
    int key , value;
    Node( int k , int v) : key( k ), value( v ){}
};

class LRUCache
{
private :
    int max_size;
    list < Node > cacheList;
    unordered_map < int , list < Node >:: iterator > mp;
public :
    LRUCache( int capacity) { max_size = capacity ;}

    int get( int key)
    {
        if( mp . find( key) == mp . end()) // 未命中
            return - 1;
        else
        {
            auto list_it = mp [ key ];
            Node node( key , list_it -> value);
            cacheList . erase( list_it);
            cacheList . push_front( node);
            mp [ key ] = cacheList . begin();
            return node . value;
        }
    }

    void set( int key , int value)
    {
        auto it = mp . find( key);
        if( it == mp . end()) // 未命中
        {
            if( cacheList . size() >= max_size) // 驻留集已满
            {
                mp . erase( cacheList . back (). key);
                cacheList . pop_back();
            }
            Node node( key , value);
            cacheList . push_front( node);
            mp [ key ] = cacheList . begin();
        }
        else // 命中,将加入的结点置于链表头部,表示最近一次使用
        {
            cacheList . erase( mp [ key ]);
            Node node( key , value);
            cacheList . push_front( node);
            mp [ key ] = cacheList . begin();
        }
    }
};

int main()
{
    LRUCache cache( 3);
    cache . set( 1 , 1);
    cache . set( 2 , 2);
    cache . set( 3 , 3);
    cache . set( 4 , 4);

    cout << cache . get( 4) << endl;
    cout << cache . get( 3) << endl;
    cout << cache . get( 2) << endl;
    cout << cache . get( 1) << endl;

    cache . set( 5 , 5);

    cout << cache . get( 1) << endl;
    cout << cache . get( 2) << endl;
    cout << cache . get( 3) << endl;
    cout << cache . get( 4) << endl;
    cout << cache . get( 5) << endl;

    return 0;
}
/*

*/

--------------------------------------------------------- End.

转载请注明:http://www.cnblogs.com/crazyacking/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值