找回密码
 用户注册

QQ登录

只需一步,快速开始

查看: 6595|回复: 3

关于MRu算法的实现研究

[复制链接]
发表于 2010-11-4 14:41:17 | 显示全部楼层 |阅读模式
本帖最后由 freeeyes 于 2010-11-4 14:43 编辑

这两个月家里事情比较多,公司里负责写战斗和任务模块,身体也不是很好,导致自己在这里的时间所余无几,ICE的文章和我的开源服务器现在都处于暂停的状态。不过我并不想荒废这里,我会继续写下去。最近在群里看有朋友讨论了mru算法,再看ICE里面,有一种类似mru的组件,很有兴趣的看了一下,发现ICE里面的代码貌似只提供了一个框架算法框架,扔东西和加载东西完全凭借自己的算法去实现。想到自己也要用到这样的类,其实确实挺常见的。就利用这两天自己写了一个。测试了一下,速度不敢说非常快,但是我自己觉得还可以,放在这里,希望给需要它的大家一些帮助,由于考虑了跨平台以及简单易用的想法,我并没有在这里类里面使用boost,当然如果你喜欢完全可以把我的map替换之。
这里要感谢stone,7cat和modern对我的帮助。当然,我也在尝试优化其中的部分。不得不说,boost库比我想象的优秀的多,我使用boost库后,速度提升了一倍多,不过为了保持简单性,我在这里没有使用。不过推荐大家如果有条件,还是使用boost替换我的map比较好。
我的设计思路是,用户可以使用我这类,实现一个简单的key-value的对应模式,用户一开始指定一个可以允许的大小,当数据达到上限后,将会自动把最不常用的数据剔除。始终保持数据量在一个可以限制的范围内。

首先,我需要一个链表,来管理我的这些数据之间的被调用关系,这个链表的主要作用就是记录谁是最后没有被访问过的哪个,并将这个数据剔除并替换成我的新数据。写链表都是基本功,这点没啥好说的。
  1. class CListNode
  2. {
  3. public:
  4.         CListNode()
  5.         {
  6.                 m_pData           = NULL;
  7.                 m_pNextListNode   = NULL;
  8.                 m_pBeforeListNode = NULL;
  9.         };
  10.         ~CListNode()
  11.         {
  12.                 Close();
  13.         };
  14.         void Close()
  15.         {
  16.                 if(NULL != m_pData)
  17.                 {
  18.                         delete m_pData;
  19.                         m_pData = NULL;
  20.                 }
  21.         };
  22.         void SetNodeData(void* pData)
  23.         {
  24.                 m_pData = pData;
  25.         };
  26.         void SetNodeNext(CListNode* pListNode)
  27.         {
  28.                 m_pNextListNode = pListNode;
  29.         }
  30.         void SetNodeBefore(CListNode* pListNode)
  31.         {
  32.                 m_pBeforeListNode = pListNode;
  33.         }
  34.         void* GetNodeData()
  35.         {
  36.                 return m_pData;
  37.         }
  38.         CListNode* GetNodeNext()
  39.         {
  40.                 return m_pNextListNode;
  41.         }
  42.         CListNode* GetNodeBefore()
  43.         {
  44.                 return m_pBeforeListNode;
  45.         }
  46. private:
  47.         void* m_pData;
  48.         CListNode* m_pNextListNode;
  49.         CListNode* m_pBeforeListNode;
  50. };
复制代码

这是一个简单的双向列表节点,没啥好说的。
  1. class CLinkList
  2. {
  3. public:
  4.         CLinkList()
  5.         {
  6.                 m_pListNode      = NULL;
  7.                 m_pFirstListNode = NULL;
  8.                 m_pLastListNode  = NULL;
  9.         };
  10.         ~CLinkList()
  11.         {
  12.                 Close();
  13.         };
  14.         bool Add(CListNode* pListNode, void* pData)
  15.         {
  16.                 if(NULL == pListNode)
  17.                 {
  18.                         return false;
  19.                 }
  20.                 pListNode->SetNodeData(pData);
  21.                 if(NULL == m_pFirstListNode)
  22.                 {
  23.                         //如果是链表的第一个
  24.                         m_pListNode      = pListNode;
  25.                         m_pFirstListNode = pListNode;
  26.                         m_pLastListNode  = pListNode;
  27.                 }
  28.                 else
  29.                 {
  30.                         pListNode->SetNodeBefore(m_pLastListNode);
  31.                         m_pLastListNode->SetNodeNext(pListNode);
  32.                         m_pLastListNode = pListNode;
  33.                 }
  34.                 return true;
  35.         };
  36.         bool MoveTop(CListNode* pListNode)
  37.         {
  38.                 if(NULL == pListNode)
  39.                 {
  40.                         return false;
  41.                 }
  42.                 if(NULL == m_pFirstListNode)
  43.                 {
  44.                         return false;
  45.                 }
  46.                 if(pListNode == m_pFirstListNode)
  47.                 {
  48.                         return true;
  49.                 }
  50.                 CListNode* pBefore = pListNode->GetNodeBefore();
  51.                 CListNode* pNext   = pListNode->GetNodeNext();
  52.                 if(NULL == pBefore)
  53.                 {
  54.                         //如果就是第一个
  55.                         return true;
  56.                 }
  57.                 pBefore->SetNodeNext(pNext);
  58.                 if(NULL != pNext)
  59.                 {
  60.                         pNext->SetNodeBefore(pBefore);
  61.                 }
  62.                 if(pListNode == m_pLastListNode)
  63.                 {
  64.                         if(NULL != pBefore)
  65.                         {
  66.                                 m_pLastListNode = pBefore;
  67.                         }
  68.                 }
  69.                 pListNode->SetNodeNext(m_pFirstListNode);
  70.                 pListNode->SetNodeBefore(NULL);
  71.                 m_pFirstListNode->SetNodeBefore(pListNode);
  72.                 m_pFirstListNode = pListNode;
  73.                 return true;
  74.         }
  75.         bool DelNode(CListNode* pListNode, bool blDel = true)
  76.         {
  77.                 if(NULL == pListNode)
  78.                 {
  79.                         return false;
  80.                 }
  81.                 CListNode* pBefore = pListNode->GetNodeBefore();
  82.                 CListNode* pNext   = pListNode->GetNodeNext();
  83.                 if(pBefore != NULL)
  84.                 {
  85.                         pBefore->SetNodeNext(pNext);
  86.                 }
  87.                 else
  88.                 {
  89.                         m_pFirstListNode = pListNode->GetNodeNext();
  90.                         m_pFirstListNode->SetNodeBefore(NULL);
  91.                 }
  92.                 if(pNext != NULL)
  93.                 {
  94.                         pNext->SetNodeBefore(pBefore);
  95.                 }
  96.                 else
  97.                 {
  98.                         m_pLastListNode = pListNode->GetNodeBefore();
  99.                         m_pLastListNode->SetNodeNext(NULL);
  100.                 }
  101.                 if(blDel == true)
  102.                 {
  103.                         delete pListNode;
  104.                 }
  105.                 pListNode->SetNodeBefore(NULL);
  106.                 pListNode->SetNodeNext(NULL);
  107.                 return true;
  108.         }
  109.         CListNode* GetFirst()
  110.         {
  111.                 return m_pFirstListNode;
  112.         }
  113.         CListNode* GetLast()
  114.         {
  115.                 return m_pLastListNode;
  116.         }
  117.         void Display()
  118.         {
  119.                 CListNode* pListNode = NULL;
  120.                 if(m_pFirstListNode != NULL)
  121.                 {
  122.                         pListNode = m_pFirstListNode;
  123.                         while(pListNode)
  124.                         {
  125.                                 CListNode* pNextListNode = pListNode->GetNodeNext();
  126.                                 printf("..%s..", pListNode->GetNodeData());
  127.                                 pListNode = pNextListNode;
  128.                         }
  129.                 }
  130.         }
  131.         CListNode* GetLastNode()
  132.         {
  133.                 if(m_pFirstListNode == NULL)
  134.                 {
  135.                         return NULL;
  136.                 }
  137.                 CListNode* pListNode = m_pLastListNode;
  138.                 CListNode* pBefor = m_pLastListNode->GetNodeBefore();
  139.                 if(NULL == pBefor)
  140.                 {
  141.                         m_pFirstListNode = NULL;
  142.                         m_pLastListNode  = NULL;
  143.                 }
  144.                 else
  145.                 {
  146.                         pBefor->SetNodeNext(NULL);
  147.                         m_pLastListNode = pBefor;
  148.                 }
  149.                 pListNode->SetNodeBefore(NULL);
  150.                 pListNode->SetNodeNext(NULL);
  151.                 return pListNode;
  152.         }
  153.         void Close()
  154.         {
  155.                 CListNode* pListNode = NULL;
  156.                 if(m_pFirstListNode != NULL)
  157.                 {
  158.                         pListNode = m_pFirstListNode;
  159.                         while(pListNode)
  160.                         {
  161.                                 CListNode* pNextListNode = pListNode->GetNodeNext();
  162.                                 delete pListNode;
  163.                                 pListNode = pNextListNode;
  164.                         }
  165.                 }
  166.         }
  167. private:
  168.         CListNode* m_pListNode;
  169.         CListNode* m_pFirstListNode;
  170.         CListNode* m_pLastListNode;
  171. };
复制代码
这是一个简单的链表,不同的是我提供了一个moveTop的方法,当我的key被访问的时候,我会调用这个方法把这个数据放在链表的最前面,当然,当指定的容器达到上限的时候,我会从链表尾部删除节点,并插入新节点。
为了保持通用性,我用模板重载了一个stl map类,来满足我的功能这个map完全可以用boost去替换,当然,链表也一样的。
template <class Key, class T>
class CMapTemplate

{
。。。。。
}
在这里我遇到了一个挺烦的难题,那就是我要把我的node和key做一一对应,通过key能找到指定的node,还有就是给定一个node删除指定的key,为了减少循环,我没有采用遍历链表的方法,因为当数据量较大的时候,遍历时间成本是较大的。map的查找比较高效,于是就用它了。用空间换时间。

typedef map<CListNode*, Key> mapNode2Key;      //定义链表和Key的对应关系

typedef map<Key, CListNode*> mapKey2Node;      //定义链表和Key的对应关系

我的这个类实际有3个map,用户初始化的时候,必须指定容器的大小。我会根据这个大小,初始化出一批node节点,当使用时候分配给对应的key,这样的话,当有大量数据需要淘汰的时候,程序不用反复的new和delete。
测试了一下,在我的笔记本下,10万条大概是4秒左右(DEBUG),在服务器上或许能更快,7cat弄出来了一个0.XX秒,不知道是怎么做到的,或许和机器相关吧。一样的代码。
把测试代码和类贴上来,实际就两个.h。
呵呵,希望对大家有所帮助,另外就是看看大家的意见。或许有更优的方法,欢迎讨论。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?用户注册

×
发表于 2011-8-10 17:00:08 | 显示全部楼层
#include <list>
#include <assert.h>
template<class K, class V>
class McMruContainter
{
        typedef list<K*> LIST_KEY;//经常访问队列,经常访问的在前面
        typedef typename LIST_KEY::iterator LIST_KEY_ITERATOR;
        struct MsItem//map元素
        {
                V* data;//数据指针
                LIST_KEY_ITERATOR key_pos;//在经常访问队列中的位置,便于通过元素在经常访问队列中找到相应的元素
        };
        typedef map<K, MsItem> MAP_DATA;
        MAP_DATA map_data;//data表
        LIST_KEY list_key;//经常访问队列
        int m_nMaxCount;//最大元素个数
public:
        McMruContainter(int nCount)
        {
                m_nMaxCount = nCount;
        }
        ~McMruContainter()
        {
                Clear();
        }
        bool AddMapData(const K& k, V* v)
        {
                MAP_DATA::iterator it = map_data.find(k);
                if(it != map_data.end())//已经存在
                        return false;

                int nSize = (int)map_data.size();
                if(nSize >= m_nMaxCount)//超过当前容器的大小
                {
                        //释放最后一个元素
                        K* last = *list_key.rbegin();//最后一个元素的key指针
                        list_key.pop_back();//经常访问队列中删除最后一个元素
                        it = map_data.find(*last);//data表中查找相应的元素
                        assert(it != map_data.end());//一定能找到
                        delete it->second.data;//清除相应的数据
                        map_data.erase(it);//data表中删除相应的元素
                }

                //现在肯定有空间了
                list_key.push_front(NULL);//新增的总是在经常访问队列的头部
                LIST_KEY_ITERATOR new_key_pos = list_key.begin();//获取k在列表中的位置
                MsItem item;
                item.data = v;
                item.key_pos = new_key_pos;
                MAP_DATA::iterator itNew = (map_data.insert(make_pair(k, item))).first;//在data表中增加元素
                *new_key_pos = const_cast<K*>(&(itNew->first));//将新的元素的k的地址,赋值到新的经常访问队列中去
                //一开始用的是NULL,因为那时还不知道key的地址,现在知道了,在data表中,为新元素分配了空间来存放k和v
                return true;
        }
        bool DelMapData(const K& k)
        {
                MAP_DATA::iterator it = map_data.find(k);
                if(it == map_data.end())//不存在
                        return false;

                MsItem& item = it->second;
                delete item.data;//删除相应的元素的数据
                list_key.erase(item.key_pos);//删除经常访问队列中的元素
                map_data.erase(it);//删除data表中的元素
                return true;
        }
        V* SearchMapData(const K& k)
        {
                MAP_DATA::iterator it = map_data.find(k);
                if(it == map_data.end())//不存在
                        return NULL;

                MsItem& item = it->second;
                LIST_KEY_ITERATOR it_key = item.key_pos;
                list_key.push_front(*it_key);//将当前的值插入到队头
                list_key.erase(it_key);//删除当前的元素。这两句相当于将当前元素移到了队头
                item.key_pos = list_key.begin();//修改item中的key_pos,它等于队头

                return item.data;//返回数据指针
        }
        void Clear()
        {
                MAP_DATA::iterator itEnd = map_data.end();
                for(MAP_DATA::iterator it = map_data.begin(); it != itEnd; it++)
                        delete it->second.data;
                map_data.clear();
                list_key.clear();
        }
        int GetSize()
        {
                return (int)map_data.size();
        }
};

上述代码复制到"MapTemplate.h”中,其实是CMapTemplate的一种实现方式。
CMapTemplate大概6.8秒
McMruContainter大概4.0秒
以上是在我的笔记本上的测试结果
 楼主| 发表于 2011-8-11 15:57:58 | 显示全部楼层
很棒!
               MsItem& item = it->second;
                LIST_KEY_ITERATOR it_key = item.key_pos;
                list_key.push_front(*it_key);//将当前的值插入到队头
                list_key.erase(it_key);//删除当前的元素。这两句相当于将当前元素移到了队头
                item.key_pos = list_key.begin();//修改item中的key_pos,它等于队头
这里很巧妙
发表于 2011-10-11 23:48:18 | 显示全部楼层
楼主用的debug模式运行的吧
您需要登录后才可以回帖 登录 | 用户注册

本版积分规则

Archiver|手机版|小黑屋|ACE Developer ( 京ICP备06055248号 )

GMT+8, 2024-5-5 23:48 , Processed in 0.030687 second(s), 6 queries , Redis On.

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表