hashmap::begin() 坑

因业务需要, 使用stl中的list+hashmap实现了一个容器做缓存;支持lru特性、  支持O(1)时间的push_back, pop_head, find/delete。


大概实现是 将hashmap的iterator的关键部分作为list的元素; 将list的iterator的关键部分作为hashmap的value的其中一部分。 hashmap是真正存放数据的。

这样能够狠容易的从hashmap::iterator中解析得到对应的list::iterator, 也能够狠容易的从list::iterator解析得到对应的hashmap::iterator。

其中后者代码如下:

inline typename HashType::iterator LitToHit(typename ListType::iterator &it)
		{
				//static 
                                typename HashType::iterator hit = m_hash.begin();//只是重用其中的_M_ht
		
				_List_node<ListKeyType> * lnode = static_cast<_List_node<ListKeyType> *>(it._M_node);
				hit._M_cur = lnode->_M_data;
		
				return hit;
		}

这个函数用的非常频繁。 测试中发现这个函数非常影响性能,当时十分不理解, 以为就是很简单的O(1)而已。 仔细看代码之后才发现其中玄机。

hashmap一维是个指针数组, 数组元素指向冲突处理链表。begin遍历该数组, 返回第一个指针非空的数组下标指向的数据节点。 当元素较少时, begin可能会非常慢、一直遍历到数组尾部才能找到第一个数据节点。

解决方法是使用static变量即可。



代码如下,以后会有很多地用到,贴出以做备忘。

#pragma once

//queue。 插入时key唯一。 O(1)的pushback, pophead, find/delete

#include <list>
#include <ext/hash_map>

using namespace std;
using namespace __gnu_cxx;

template <typename Key, typename Value> class UniqQueue
{
public:
	class ValueNode;

	typedef hash_map<Key, ValueNode>                         HashType;
	typedef _Hashtable_node<pair<const Key, ValueNode> > *   ListKeyType;
	typedef list<ListKeyType>                                ListType;
	typedef Key KeyType;
	typedef Value ValueType;
	typedef UniqQueue ThisType;

	class ValueNode{
	public:
			_List_node<ListKeyType> *list;      // lru链表中的位置
			Value                    value;    // 元素
	
			ValueNode():list(NULL), value() 
			{
			}
			ValueNode(const ValueNode &r):list(r.list), value(r.value)
		  {
			}

	private:
			ValueNode &operator=(const ValueNode &r);//disable
	};


private:	  
	HashType 					 m_hash;
	ListType 					 m_list;
	pthread_mutex_t    m_lock;
	pthread_cond_t 		 m_cond;
	
public:
	UniqQueue():m_hash(1000000)
	{
		  pthread_cond_init(&m_cond, NULL);
		  pthread_mutex_init(&m_lock, NULL);
	}
	~UniqQueue()
	{
		  pthread_cond_destroy(&m_cond);
		  pthread_mutex_destroy(&m_lock);
	}
	
	pthread_mutex_t & lock()
	{
			return m_lock;
	}
	
	void push_back(const Key & k, const Value &content)
	{		  
			pthread_mutex_lock(&m_lock);
			
			
			pair<typename HashType::iterator, bool> it = m_hash.insert(make_pair(k, ValueNode()));
				
			it.first->second.value = content;
			
			if(it.second == true)//cache中新加的数据
			{   
				typename ListType::iterator lii = m_list.insert(m_list.end(), GetListNode(it.first));
				SetList(it.first, lii);
			}
			else
			{
			}
			
			
			pthread_cond_signal(&m_cond);
	    pthread_mutex_unlock(&m_lock);
	
			return;
	}
	
	Value peak_head()
	{
			Value vn;
			
			typename ListType::iterator lii;
			typename HashType::iterator hit;

		//	pthread_cleanup_push(Cleanup, this);

		  pthread_mutex_lock(&m_lock);

		  while(m_list.size() == 0)
		  {
				pthread_cond_wait(&m_cond, &m_lock);
		  }


			lii = m_list.begin();
			hit = LitToHit(lii);
				
			//can not define Value vn; here.. because of cond_wait??	
			vn = hit->second.value;
			

			
			pthread_mutex_unlock(&m_lock);

      // pthread_cleanup_pop(0);
		  
			return vn;
			
	}
	
	Value peak_head_nolock()
	{
			Value vn;
        
			typename ListType::iterator lii = m_list.begin();
			typename HashType::iterator hit = LitToHit(lii);
				
			vn = hit->second.value;
         
			return vn;
	}
	
	Value find(const Key &k)
	{
		  Value v;
		  	
			pthread_mutex_lock(&m_lock);
			
			typename HashType::iterator it = m_hash.find(k);
				
			if(it == m_hash.end()) 
				;
			else
				v = it->second.value;
			
			
	    pthread_mutex_unlock(&m_lock);
	
			return v;
	}
	
	void pop_head(bool want = true)
	{
		  if(want)  pthread_mutex_lock(&m_lock);
        
			typename ListType::iterator lii = m_list.begin();
			typename HashType::iterator hit = LitToHit(lii);
     
			m_list.erase(lii);
			m_hash.erase(hit);
			
			if(want) pthread_mutex_unlock(&m_lock);
	}

	
	
private://lock helper
    static void Cleanup(void *arg)
    {
        ThisType *tis = (ThisType *)arg;
        pthread_mutex_unlock(&(tis->m_lock));
    }
	
	
private: //iterator tool functions
		inline ListKeyType GetListNode(typename HashType::iterator &v)
		{
				return v._M_cur;
		}
		inline typename HashType::iterator LitToHit(typename ListType::iterator &it)
		{
				static typename HashType::iterator hit = m_hash.begin();//只是重用其中的_M_ht
		
				_List_node<ListKeyType> * lnode = static_cast<_List_node<ListKeyType> *>(it._M_node);
				hit._M_cur = lnode->_M_data;
		
				return hit;
		}
		inline void SetList(typename HashType::iterator &v, typename ListType::iterator it)
		{
				v->second.list = static_cast<_List_node<ListKeyType>  * >(it._M_node);
		}
};



当时测试场景:

1写线程从1开始递增key,不停得push_back;  1读线程不停的peak_head+pop_head。

异常现象:

1)push_back线程的pthread_mutex_lock获取锁操作时间越来越长, 从0-1us变为几十个ms。

2)peak_head+pop_head非常消耗cpu,几乎吃满cpu。 而写线程只占30%左右的cpu。

3)内存用完后变得更慢, push_back大概每秒只能push 30个左右。

4)其他等等

当时百思不得其解,甚至怀疑pthread contidion的效率、 系统环境有问题。

其实1和2都已经明确的指向了同一个问题了。 1获取锁慢, 肯定是其他线程持有锁的时间长导致的。 2吃满cpu,正好说明获取锁后做大量运算需要时间长。

就是peadk_head慢。 慢在哪, 仔细分析就是hashmap::begin慢



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值