关于标准库的map有map,hashmap,unordermap(c++11),关于他们的实现原理参考c++11 map和unordered_maphttps://blog.csdn.net/D_Guco/article/details/77507970?spm=1001.2014.3001.5501https://blog.csdn.net/D_Guco/article/details/77507970?spm=1001.2014.3001.5501
对于map 的选择有一个基本的共识,hashmap和unordermap的查找效率要比map高但是占用内存偏高,map是有序的但另外两个是无序的,除了这些不同点外,他们有一些共同的问题:
1 扩容问题:当插入的元素数量超过他们的容量上限时,会进行扩容操作,对于map 的扩容很简单就是申请额外的内存空间以容纳更多的元素,而对于hashmap和unordermap不光要申请额外的空间(注意这里并不是申请存放元素的内存空间而是扩大hash表数组减少hash冲突),然后对所有元素重新计算hash值然后排列在hash表中,这个操作会带来额外的性能消耗,当插入元素数量达到一个量级发生扩容的时候会变得很慢。
2 内存碎片:每次插入新的元素底层都是现new出一个元素的空间来存放,当频繁的删除和插入,元素数量大的时候会产生大量的内存碎片,当然这个问题可以通过实现自己的_Alloc来解决。
3 序列化问题: 我们系统中会直接把很多对象的的内存二进制序列直接存入数据库,但是由于标准库中map的非连续的内存结构导致很多需要存库的对象无法使用,只能采用数组来代替。
4 内存浪费:我们系统中的内存都是定量申请的,几乎所有的对象都是采用内存池的形式,运行过程中几乎不会有额外的内存申请和释放操作,标准库中的map在扩容时申请的内存空间是翻倍的,实际上插入的元素是有限的,最后一次扩容时申请的额外空间很有可能用不完造成浪费。
为了解决以上问题,需要自己实现一个固定长度的内存连续的map,并且保证一定的效率。具体实现如下:
此版本的实现存在一个问题,当hash冲突率很高的时候会退化为一个链表查找效率直线下降,因为是固定长度所以冲突率会比标准库高,优化后的实现不存在此问题,跳转:红黑树hashmap-RbtHashMap实现https://blog.csdn.net/D_Guco/article/details/124908719?spm=1001.2014.3001.5502
hash_helper.h
//
// hash_helper.h
// limit_hash_map 辅助类
// Created by DGuco on 2021/09/13.
// Copyright © 2016年 DGuco. All rights reserved.
//
#ifndef __HASH_HELPER_H__
#define __HASH_HELPER_H__
#include <cstddef>
#include <new>
#include <memory>
#include <string.h>
namespace my
{
namespace hash_function
{
template<class _Key>
struct hash
{
};
template<>
struct hash<short>
{
size_t operator()(short value) const
{
return value;
}
};
template<>
struct hash<unsigned short>
{
size_t operator()(unsigned short value) const
{
return value;
}
};
template<>
struct hash<int>
{
size_t operator()(int value) const
{
return value;
}
};
template<>
struct hash<unsigned int>
{
size_t operator()(unsigned int value) const
{
return value;
}
};
template<>
struct hash<long>
{
size_t operator()(long value) const
{
return value;
}
};
template<>
struct hash<unsigned long>
{
size_t operator()(unsigned long value) const
{
return value;
}
};
}
/**
*单个节点类
* */
template <class CLASS_TYPE>
struct node
{
public:
typedef CLASS_TYPE& reference;
typedef const CLASS_TYPE& const_reference;
typedef CLASS_TYPE* pointer;
typedef const CLASS_TYPE* const_pointer;
node()
{
cur_ = -1;
prev_ = -1;
next_ = -1;
memset(data_,0,sizeof(CLASS_TYPE));
}
~node()
{
cur_ = -1;
prev_ = -1;
next_ = -1;
memset(data_,0,sizeof(CLASS_TYPE));
}
void set_next(int value)
{
next_ = value;
}
int get_next()
{
return next_;
}
void set_prev(int value)
{
prev_ = value;
}
int get_prev()
{
return prev_;
}
void set_cur(int value)
{
cur_ = value;
}
int get_cur()
{
return cur_;
}
void dis_from_list()
{
set_next(-1);
set_prev(-1);
}
reference data()
{
return *(reinterpret_cast<pointer>(data_));
}
const_reference data() const
{
return *(reinterpret_cast<const_pointer>(data_));
}
private:
int cur_; //当前节点在数组中的索引位置
int next_; //下一个节点在数组中的索引位置
int prev_; //前一个节点在数组中的索引位置
char data_[sizeof(CLASS_TYPE)]; //真正存放对象信息的内存块 c++ placement new operator把对象new到指定的内存位置
};
/**
*迭代器class
* */
template <typename CLASS_TYPE>
class node_list_iterator
{
public:
typedef node<CLASS_TYPE> node_type;
typedef node_list_iterator<CLASS_TYPE> iterator_type;
typedef CLASS_TYPE* pointer;
typedef CLASS_TYPE& reference;
node_list_iterator(const iterator_type& other)
: ptr_(other.ptr_),array_(other.array_),size_(other.size_)
{}
explicit node_list_iterator(node_type* pptr_,node_type* parray_,std::size_t psize_)
: ptr_(pptr_),array_(parray_),size_(psize_)
{}
node_list_iterator() : ptr_(0),array_(0),size_(0) {}
reference operator* () const
{
return ptr_->data();
}
pointer operator->() const
{
return &(operator*());
}
iterator_type& operator++()
{
ptr_ = get_node(ptr_->get_next());
return *this;
}
iterator_type operator ++(int)
{
iterator_type __tmp(*this);
++*this;
return __tmp;
}
iterator_type& operator--()
{
ptr_ = get_node(ptr_->prev());
return *this;
}
iterator_type operator --(int)
{
iterator_type __tmp(*this);
--*this;
return __tmp;
}
bool operator == (const iterator_type & other) const
{
return ptr_ == other.ptr_;
}
bool operator != (const iterator_type & other) const
{
return !(*this == other);
}
iterator_type& operator = (const iterator_type& other)
{
if (this != &other)
{
ptr_ = other.ptr_;
array_ = other.array_;
size_ = other.size_;
}
return * this;
}
node_type* get_node(std::size_t index)
{
if (index >= 0 && index < size_)
{
return &array_[index];
}
return 0;
}
private:
node_type* ptr_; //节点指针
node_type* array_; //节点所属的数组
std::size_t size_; //数组长度
};
template <typename CLASS_TYPE>
struct node_list_const_iterator
{
public:
typedef node<CLASS_TYPE> node_type;
typedef node_list_const_iterator<CLASS_TYPE> iterator_type;
typedef const CLASS_TYPE* pointer;
typedef const CLASS_TYPE& reference;
node_list_const_iterator(const iterator_type& other)
: ptr_(other.ptr_),array_(other.array_),size_(other.size_)
{}
explicit node_list_const_iterator(node_type* pptr_,node_type* parray_,int psize_)
: ptr_(pptr_),array_(parray_),size_(psize_)
{}
node_list_const_iterator() : ptr_(0),array_(0),size_(0) {}
reference operator* () const
{
return ptr_->data();
}
pointer operator->() const
{
return &(operator*());
}
iterator_type& operator++()
{
ptr_ = get_node(ptr_->next());
return *this;
}
iterator_type operator ++(int)
{
iterator_type __tmp(*this);
++*this;
return __tmp;
}
iterator_type& operator--()
{
ptr_ = get_node(ptr_->prev());
return *this;
}
iterator_type operator --(int)
{
iterator_type __tmp(*this);
--*this;
return __tmp;
}
bool operator == (const iterator_type & other) const
{
return ptr_ == other.ptr_;
}
bool operator != (const iterator_type & other) const
{
return !(*this == other);
}
iterator_type& operator = (const iterator_type& other)
{
if (this != &other)
{
ptr_ = other.ptr_;
array_ = other.array_;
size_ = other.size_;
}
return * this;
}
node_type* get_node(int index)
{
if (index >= 0 && index < size_)
{
return &array_[index];
}
return 0;
}
private:
node_type* ptr_; //节点指针
node_type* array_; //节点所属的数组
std::size_t size_; //数组长度
};
/**
* 内存管理器
*/
template <class CLASS_TYPE, std::size_t MAX_COUNT = 0>
class node_array
{
public:
typedef node<CLASS_TYPE> node_type;
typedef node_list_iterator<CLASS_TYPE> iterator;
typedef node_list_const_iterator<CLASS_TYPE> const_iterator;
typedef CLASS_TYPE* pointer;
typedef const CLASS_TYPE* const_pointer;
typedef CLASS_TYPE& reference;
typedef const CLASS_TYPE& const_reference;
//构造函数
node_array()
{
clear();
}
//析构函数
~node_array()
{
clear();
}
//清理
void clear()
{
memset(node_array_,0,sizeof(node_type) * MAX_COUNT);
//构造空闲链表信息
node_array_[0].set_cur(0);
node_array_[0].set_prev(-1);
for(std::size_t i = 1;i < MAX_COUNT;i++)
{
node_array_[i - 1].set_next(i);
node_array_[i].set_prev(i - 1);
node_array_[i].set_cur(i);
}
node_array_[MAX_COUNT - 1].set_next(-1);
size_ = 0;
//没有已用的节点
used_node_head_ = -1;
//默认数组首个元素即可用节点链表的头结点
free_node_head_ = 0;
}
//内存池当前大小
std::size_t size() const
{
return size_;
}
//内存池当前容量
std::size_t cap() const
{
return MAX_COUNT;
}
//申请一个可用节点
node_type* allocate_node(const CLASS_TYPE& v)
{
node_type* p = allocate(v);
if (p)
{
//插入到头结点
insert_head(get_node(used_node_head_),p);
//更新头结点索引
used_node_head_ = p->get_cur();
return p ;
}
return 0;
}
node_type* allocate_node()
{
node_type* p = allocate();
if (p)
{
//插入到头结点
insert_head(get_node(used_node_head_),p);
//更新头结点索引
used_node_head_ = p->get_cur();
return p;
}
return 0;
}
//申请一个可用节点
node_type* allocate_node(const CLASS_TYPE& v,node_type* next_node)
{
node_type* p = allocate(v);
if (p)
{
//插入到结点
insert_node(next_node,p);
//更新头结点索引
used_node_head_ = p->get_cur();
return p;
}
return 0;
}
node_type* allocate_node(node_type* insert_node)
{
node_type* p = allocate();
if (p)
{
//插入到结点
insert_node(insert_node,p);
//更新头结点索引
used_node_head_ = p->get_cur();
return p;
}
return 0;
}
void deallocate_node(node_type* node_)
{
node_type* node_head = get_node(used_node_head_);
//当前节点是头结点
if(node_ == node_head)
{
node_type* new_head = delete_head(node_);
//更新头结点信息
if(new_head)
{
used_node_head_ = new_head->get_cur();
}else //链表删空了
{
used_node_head_ = -1;
}
}else
{
delete_node(node_);
}
//回收内存空间
deallocate(node_);
}
node_type* get_node(std::size_t index)
{
if (index >= 0 && index < MAX_COUNT)
{
return &node_array_[index];
}
return 0;
}
iterator make_iteator(std::size_t index)
{
return iterator(get_node(index),node_array_,MAX_COUNT);
}
const_iterator make_const_iteator(std::size_t index)
{
return const_iterator(get_node(index),node_array_,MAX_COUNT);
}
// 下面为访问已经分配对象的iterator
iterator begin()
{
if(used_node_head_ != -1)
{
return iterator(get_node(used_node_head_),node_array_,MAX_COUNT);
}else
{
return end();
}
}
iterator end()
{
return iterator();
}
const_iterator begin() const
{
if(used_node_head_ != -1)
{
return const_iterator(get_node(used_node_head_),node_array_,MAX_COUNT);
}else
{
return end();
}
}
const_iterator end() const
{
return const_iterator();
}
private:
//申请空间
node_type* allocate(const CLASS_TYPE& v)
{
if(size_ >= MAX_COUNT)
{
return 0;
}
node_type* p = get_node(free_node_head_);
if(p)
{
size_++;
//先把空闲头结点指向当前空闲头结点的下一个节点
free_node_head_ = p->get_next();
node_type* new_head = get_node(free_node_head_);
if(new_head)
{
new_head->set_prev(-1);
}
//call c++ placement new
new (&(p->data())) CLASS_TYPE(v);
return p;
}else
{
return 0;
}
}
//申请空间
node_type allocate()
{
if(size_ >= MAX_COUNT)
{
return 0;
}
else
{
node_type* p = get_node(free_node_head_);
if(p)
{
size_++;
//先把空闲头结点指向当前空闲头结点的下一个节点
free_node_head_ = p->get_next();
node_type* new_head = get_node(free_node_head_);
if(new_head)
{
new_head->set_prev(-1);
}
//call c++ placement new
new (&p->value()) CLASS_TYPE();
return p;
}else
{
return 0;
}
}
}
void deallocate(node_type* node_)
{
if(node_ == 0)
{
return;
}
//调用析构函数
node_->value().~CLASS_TYPE();
//插入空闲链表头部
insert_head(get_node(free_node_head_),node_);
size_--;
free_node_head_ = node_->get_cur();
}
//插入一个节点到链表头部
void insert_head(node_type* old_head,node_type* new_head)
{
if (old_head)
{
if(new_head)
{
//老的头结点前指针指向新的头结点
old_head->set_prev(new_head->get_cur());
//新的头结点后指针指向老的的头节点
new_head->set_next(old_head->get_cur());
//新的头结点前指针置为-1
new_head->set_prev(-1);
}
}
else
{
if(new_head)
{
new_head->dis_from_list();
}
}
}
//插入一个节点到指定节点前
void insert_node(node_type* tar_node,node_type* src_node)
{
if(tar_node == 0 || src_node == 0)
{
return;
}
int tar_prev = tar_node->get_prev();
node_type* tar_prev_node = get_node(tar_prev);
//有前节点
if(tar_prev_node)
{
//目标节点前指针指向要插入的节点
tar_prev_node->set_next(src_node->get_cur());
//要插入的节点前指针指向tar_prev_node
src_node->set_prev(tar_prev_node->get_cur());
}else
{
//要插入的节点前指针置为-1
src_node->set_prev(-1);
}
tar_node->set_prev(src_node->get_cur());
src_node->set_next(tar_node->get_cur());
}
//删除头结点,返回新的头结点
node_type* delete_head(node_type* old_head)
{
if(old_head == 0)
{
return 0;
}
node_type* new_head = get_node(old_head->get_next());
if(new_head)
{
new_head->set_prev(-1);
}
old_head->dis_from_list();
return new_head;
}
//删除结点
void delete_node(node_type* node_)
{
if(node_ == 0)
{
return;
}
int next = node_->get_next();
int prev = node_->get_prev();
node_type* prev_node = get_node(node_->get_prev());
node_type* next_node = get_node(node_->get_next());
if (prev_node)
{
prev_node->set_next(next);
}
if (next_node)
{
next_node->set_prev(prev);
}
node_->dis_from_list();
}
private:
std::size_t size_; //内存池已用数量
int used_node_head_; //已用的节点链表头节点的索引
int free_node_head_; //空闲的节点链表头节点的索引
node_type node_array_[MAX_COUNT];
};
}
#endif
hash_map.h
//
// hash_map.h
// 固定长度的hash_map
// Created by DGuco on 2021/09/13.
// Copyright © 2016年 DGuco. All rights reserved.
//
#ifndef __HASH_MAP_H__
#define __HASH_MAP_H__
#include <map>
#include "hash_helper.h"
using namespace std;
namespace my
{
template <class KEY_TYPE, class VALUE_TYPE, std::size_t _Cap>
class hash_map
{
public:
typedef std::pair<KEY_TYPE, VALUE_TYPE> value_type;
typedef node_array<value_type, _Cap> hash_array;
typedef typename hash_array::iterator iterator;
typedef typename hash_array::const_iterator const_iterator;
typedef typename hash_array::reference reference;
typedef typename hash_array::const_reference const_reference;
typedef typename hash_array::node_type node_type;
iterator begin()
{
return hash_array_.begin ();
}
iterator end()
{
return hash_array_.end();
}
const_iterator begin() const
{
return hash_array_.begin();
}
const_iterator end() const
{
return hash_array_.end();
}
std::size_t cap() const
{
return hash_array_.cap();
}
std::size_t size() const
{
return hash_array_.size();
}
bool insert(const value_type& v)
{
hash_function::hash<KEY_TYPE> hash_func;
std::size_t bucket = hash_func(v.first) % _Cap;
iterator it_begin = hash_array_.make_iteator(buckets_[bucket].head_);
//该bucket是空的
if ( it_begin == this->end())
{
//申请一个节点
node_type* new_node = hash_array_.allocate_node(v);
if(!new_node)
{
return false;
}
//设置链表头和尾
buckets_[bucket].head_ = buckets_[bucket].tail_ = new_node->get_cur();
return true;
}
//hash冲突
iterator it_end = hash_array_.make_iteator(buckets_[bucket].tail_);
++it_end;
//检查是否存在改key的元素
while ( it_begin != it_end )
{
//该key对应的元素已经存在,插入失败
if (it_begin->first == v.first)
{
return false;
}
++it_begin;
}
//申请一个节点
node_type* new_node = hash_array_.allocate_node(v,hash_array_.get_node(buckets_[bucket].head_));
if( !new_node )
{
return false;
}
//更新头结点信息
buckets_[bucket].head_ = new_node->get_cur();
return true;
}
bool insert(const KEY_TYPE& k,const VALUE_TYPE& v)
{
return insert(std::make_pair(k,v));
}
iterator find(const KEY_TYPE& k)
{
hash_function::hash<KEY_TYPE> hash_func;
std::size_t bucket = hash_func(k) % _Cap;
iterator it_begin = hash_array_.make_iteator(buckets_[bucket].head_);
if ( it_begin == this->end() )
{
return this->end();
}
iterator it_end = hash_array_.make_iteator(buckets_[bucket].tail_);
++it_end;
while ( it_begin != it_end )
{
if ( it_begin->first == k )
{
return it_begin;
}
++it_begin;
}
return this->end();
}
const_iterator find(const KEY_TYPE& k) const
{
hash_function::hash<KEY_TYPE> hash_func;
std::size_t bucket = hash_func(k) % _Cap;
const_iterator it_begin = hash_array_.make_const_iteator(buckets_[bucket].head_);
if ( it_begin == this->end() )
{
return this->end();
}
const_iterator it_end = hash_array_.make_const_iteator(buckets_[bucket].tail_);
++it_end;
while ( it_begin != it_end )
{
if ( it_begin->first == k )
{
return it_begin;
}
++it_begin;
}
return this->end();
}
void erase( iterator it )
{
if ( it == this->end() )
{
return;
}
hash_function::hash<KEY_TYPE> hash_func;
std::size_t bucket = hash_func(it->first) % _Cap;
iterator it_begin = hash_array_.make_iteator(buckets_[bucket].head_);
iterator it_end = hash_array_.make_iteator(buckets_[bucket].tail_);
//hash链表只有自己
if ( it == it_begin && it == it_end )
{
buckets_[bucket].head_ = buckets_[bucket].tail_ = -1;
}
else if ( it == it_begin ) //hash链表自己是头
{
++ it_begin;
buckets_[bucket].head_ = hash_array_.get_node(hash_array_.get_node(buckets_[bucket].head_)->get_next())->get_cur();
}
else if ( it == it_end )//hash链表自己是尾
{
-- it_end;
buckets_[bucket].tail_ = hash_array_.get_node(hash_array_.get_node(buckets_[bucket].tail_)->get_prev())->get_cur();
}
hash_array_.deallocate_node( &*it);
}
std::size_t erase( const KEY_TYPE& k )
{
erase (find(k));
return size ();
}
void clear()
{
for( std::size_t t = 0; t < _Cap; ++t )
{
buckets_[t].head_ = -1;
buckets_[t].tail_ = -1;
}
hash_array_.clear();
}
hash_map() {clear();}
~hash_map() {clear();}
private:
hash_map( const hash_map& other );
private:
struct bucket_type
{
std::size_t head_; //相同bucket头节点索引
std::size_t tail_; //相同bucket尾节点索引
};
bucket_type buckets_[_Cap]; //bucket array
hash_array hash_array_; //内存管理器
};
}
#endif // __YQ_HASHMAP_H__
和map,hashmap,unordermap,性能比较
// Created by 杜国超 on 20/05/20.
// Copyright © 2019年 杜国超. All rights reserved.
//
#include <iostream>
#include <stdio.h>
#include <execinfo.h>
#include <sys/time.h>
#include <stdlib.h>
#include <unordered_map>
#include <map>
#include <hash_map>
#include <zconf.h>
#include "hash_map.h"
#define VMRSS_LINE 22
// 获取当前微秒
time_t GetUSTime()
{
struct timeval tmval = {0};
int nRetCode = gettimeofday(&tmval, NULL);
if (nRetCode != 0)
{
return 0;
}
return ((tmval.tv_sec * 1000 * 1000) + tmval.tv_usec);
}
// get specific process physical memeory occupation size by pid (MB)
inline float GetMemoryUsage(int pid)
{
char file_name[64] = { 0 };
FILE* fd;
char line_buff[512] = { 0 };
sprintf(file_name, "/proc/%d/status", pid);
fd = fopen(file_name, "r");
if (nullptr == fd)
return 0;
char name[64];
int vmrss = 0;
for (int i = 0; i < VMRSS_LINE - 1; i++)
fgets(line_buff, sizeof(line_buff), fd);
fgets(line_buff, sizeof(line_buff), fd);
sscanf(line_buff, "%s %d", name, &vmrss);
fclose(fd);
// cnvert VmRSS from KB to MB
return vmrss / 1024.0;
}
#define TEST_COUNT 5000000
#define MAP_VALUE_SIZE 100 //map value 占用内存大小
#define HASH_CONFLICT_RATE 4 //hash冲突倍率
using namespace my;
using namespace __gnu_cxx;
struct MapValue
{
MapValue(int _a)
{
a = _a;
}
int a;
char data[MAP_VALUE_SIZE];
} ;
int main()
{
time_t start = 0;
time_t end = 0;
unsigned long long res = 0;
int curpid = getpid();
float memstart = GetMemoryUsage(curpid);
float memend = 0.0f;
printf("------------------------------------------------------------------------\n");
my::hash_map<int,MapValue,TEST_COUNT>* pMyMap = new my::hash_map<int,MapValue,TEST_COUNT>();
memend = GetMemoryUsage(curpid);
start = GetUSTime();
for(int i = 0;i < TEST_COUNT;i++)
{
pMyMap->insert(i * HASH_CONFLICT_RATE,MapValue(i * HASH_CONFLICT_RATE));
}
end = GetUSTime();
printf("my::hash_map<int,int,TEST_COUNT> insert use %ld ms\n",(end - start) / 1000);
// my::hash_map<int,MapValue,TEST_COUNT>* pMyMap1 = new my::hash_map<int,MapValue,TEST_COUNT>();
// memcpy(pMyMap1,pMyMap,sizeof(my::hash_map<int,int,TEST_COUNT>));
start = GetUSTime();
for(int i = 0;i < TEST_COUNT;i++)
{
res += pMyMap->find(i * HASH_CONFLICT_RATE)->second.a;
}
end = GetUSTime();
printf("my::hash_map<int,int,TEST_COUNT> find use %ld ms,res = %llu,cap = %lu,mem = %f MB\n",
(end - start) / 1000,res,pMyMap->cap(),memend - memstart);
printf("------------------------------------------------------------------------\n");
memstart = GetMemoryUsage(curpid);
std::map<int,MapValue> testMap;
start = GetUSTime();
for(int i = 0;i < TEST_COUNT;i++)
{
testMap.insert(std::make_pair(i * HASH_CONFLICT_RATE,MapValue(i * HASH_CONFLICT_RATE)));
}
memend = GetMemoryUsage(curpid);
end = GetUSTime();
printf("std::map<int,int> insert use %ld ms\n",(end - start) / 1000);
start = GetUSTime();
res = 0;
for(int i = 0;i < TEST_COUNT;i++)
{
res += testMap.find(i * HASH_CONFLICT_RATE)->second.a;
}
end = GetUSTime();
printf("std::map<int,int> find use %ld ms,res = %llu,cap = %lu,mem = %f MB\n",
(end - start) / 1000,res,testMap.max_size(),memend - memstart);
printf("------------------------------------------------------------------------\n");
memstart = GetMemoryUsage(curpid);
__gnu_cxx::hash_map<int,MapValue> testGnuMap;
start = GetUSTime();
for(int i = 0;i < TEST_COUNT;i++)
{
testGnuMap.insert(std::make_pair(i * HASH_CONFLICT_RATE,MapValue(i * HASH_CONFLICT_RATE)));
}
memend = GetMemoryUsage(curpid);
end = GetUSTime();
printf("__gnu_cxx::hash_map<int,int> insert use %ld ms\n",(end - start) / 1000);
start = GetUSTime();
res = 0;
for(int i = 0;i < TEST_COUNT;i++)
{
res += testGnuMap.find(i * HASH_CONFLICT_RATE)->second.a;
}
end = GetUSTime();
printf("__gnu_cxx::hash_map<int,int> find use %ld ms,res = %llu,cap = %lu,mem = %f MB\n",
(end - start) / 1000,res,testGnuMap.max_size(),memend - memstart);
printf("------------------------------------------------------------------------\n");
memstart = GetMemoryUsage(curpid);
std::unordered_map<int,MapValue> testUnorderMap;
start = GetUSTime();
for(int i = 0;i < TEST_COUNT;i++)
{
testUnorderMap.insert(std::make_pair(i * HASH_CONFLICT_RATE,MapValue(i * HASH_CONFLICT_RATE)));
}
memend = GetMemoryUsage(curpid);
end = GetUSTime();
printf("std::unordered_map<int,int> insert use %ld ms\n",(end - start) / 1000);
start = GetUSTime();
res = 0;
for(int i = 0;i < TEST_COUNT;i++)
{
res += testUnorderMap.find(i * HASH_CONFLICT_RATE)->second.a;
}
end = GetUSTime();
printf("std::unordered_map<int,int> find use %ld ms,res = %llu,cap = %lu,mem = %f MB\n",
(end - start) / 1000,res,testUnorderMap.max_size(),memend - memstart);
printf("------------------------------------------------------------------------\n");
return 0;
}
测试结果
可以看到自定义的map在保证了占用最少内存和最快的插入速度的前提下也保证了查找效率并没有标准库的map相差太多。
注意:这里是在冲突率比较低的时候没有很大的差距,上面的测试结果是冲突率为4也就是每4个元素共用一个hash_key,当冲突率较高的时候性能会急速下降,下面是冲突率为1000的运行结果,即把测试用例中的HASH_CONFLICT_RATE的值改为1000,表示每1000个元素共用1个hash_key,极限情况下 HASH_CONFLICT_RATE和map的最大长度一样,这个时候map变成一个乱序的数组,插入和查找都在遍历整个数组,这也是在保证内存的稳定性的同时带来的性能的不稳定性。