Comparison between custom fixed length map and map,hashmap,unordermap

      For the map of the standard library, there are map, hashmap and unordermap(c++11). Refer to their implementation principle c++11 map and unordered_maphttps://blog.csdn.net/D_Guco/article/details/77507970?spm=1001.2014.3001.5501

      There is a basic consensus on the choice of map. hashmap and unordermap have higher search efficiency than map, but occupy a high memory. Map is orderly, but the other two are disordered. In addition to these differences, they have some common problems:

one   Capacity expansion: when the number of inserted elements exceeds their maximum capacity, the capacity expansion operation will be carried out. The capacity expansion of map is very simple, that is, to apply for additional memory space to accommodate more elements. For hashmap and unordermap, it is not only to apply for additional space (note that this is not to apply for the memory space for storing elements, but to expand the hash table array to reduce hash conflict), Then recalculate the hash values of all elements and arrange them in the hash table. This operation will bring additional performance consumption. When the number of inserted elements reaches an order of magnitude, it will become very slow.

2 memory fragments: each time a new element is inserted, the bottom layer is a new element space to store. When the number of elements is large due to frequent deletion and insertion, a large number of memory fragments will be generated. Of course, this problem can be solved by ourselves_ Alloc.

3 serialization problem:   In our system, the memory binary sequences of many objects will be directly stored in the database. However, due to the discontinuous memory structure of map in the standard library, many objects that need to be stored can not be used, so they can only be replaced by arrays.

4. Memory waste: the memory in our system is applied quantitatively. Almost all objects are in the form of memory pool. There are almost no additional memory application and release operations during operation. The memory space applied by the map in the standard library is doubled during capacity expansion. In fact, the inserted elements are limited, The extra space applied for in the last expansion may not be used up, resulting in waste.

    In order to solve the above problems, we need to implement a fixed length memory continuous map and ensure a certain efficiency. The specific implementation is as follows:  

hash_helper.h

//
//  hash_helper.h
//  limit_hash_map helper class
//  Created by DGuco on 2021/09/13.
//  Copyright  ©  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;
			}
		};
	}

	/**
	 *Single node class
	* */
	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_;						//The index position of the current node in the array
		int 	next_; 						//The index position of the next node in the array
		int 	prev_; 						//The index position of the previous node in the array
		char 	data_[sizeof(CLASS_TYPE)];   //The memory block that really stores the object information. The c++ placement new operator sends the object new to the specified memory location
	};

	/**
	 *Iterator 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 pointer
		node_type* 	array_;			//The array to which the node belongs
		std::size_t size_;			//Array length
	};

	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 pointer
		node_type* 	array_;			//The array to which the node belongs
		std::size_t size_;			//Array length
	};

	/**
	 * memory manager
	 */
	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;

		//Constructor
		node_array()
		{
			clear();
		}
		
		//Destructor
		~node_array()
		{
			clear();
		}

		//clear
		void clear()
		{
			memset(node_array_,0,sizeof(node_type) * MAX_COUNT);

			//Construct free linked list information
			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;
			//There are no used nodes
			used_node_head_ = -1;
			//The first element of the default array is the head node of the list of available nodes
			free_node_head_ = 0;
		}

		//Current size of memory pool
		std::size_t size() const
		{
			return size_;
		}

		//Current capacity of memory pool
		std::size_t cap() const
		{
			return MAX_COUNT;
		}

		//Request an available node
		node_type* allocate_node(const CLASS_TYPE& v)
		{
			node_type* p = allocate(v);
			if (p)
			{
				//Insert to header
				insert_head(get_node(used_node_head_),p);
				//Update header index
				used_node_head_ = p->get_cur();
				return p ;
			}
			return 0;
		}

		node_type* allocate_node()
		{
			node_type* p = allocate();
			if (p)
			{
				//Insert to header
				insert_head(get_node(used_node_head_),p);
				//Update header index
				used_node_head_ = p->get_cur();
				return p;
			}
			return 0;
		}

		//Request an available node
		node_type* allocate_node(const CLASS_TYPE& v,node_type* next_node)
		{
			node_type* p = allocate(v);
			if (p)
			{
				//Insert to node
				insert_node(next_node,p);
				//Update header index
				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 to node
				insert_node(insert_node,p);
				//Update header index
				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_);
			//The current node is the head node
			if(node_ == node_head)
			{
				node_type* new_head = delete_head(node_);
				//Update header node information
				if(new_head)
				{
					used_node_head_ = new_head->get_cur();
				}else //The linked list is deleted
				{
					used_node_head_ = -1;
				}
			}else
			{
				delete_node(node_);
			}
			//Reclaim memory space
			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);
		}

		// The following is the iterator that accesses the assigned object
		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:
		//Application space
		node_type* allocate(const CLASS_TYPE& v)
		{
			if(size_ >= MAX_COUNT)
			{
				return 0;
			}
			node_type* p = get_node(free_node_head_);
			if(p)
			{	
				size_++;
				//First, point the free header node to the next node of the current free header node
				free_node_head_ = p->get_next();
				//call c++ placement new
				new (&(p->data())) CLASS_TYPE(v);
				return p;
			}else
			{
				return 0;
			}
		}

		//Application space
		node_type allocate()
		{
			if(size_ >= MAX_COUNT)
			{
				return 0;
			}
			else
			{
				node_type* p = get_node(free_node_head_);
				if(p)
				{	
					size_++;
					//First, point the free header node to the next node of the current free header node
					free_node_head_ = p->get_next();
					//call c++ placement new
					new (&p->value()) CLASS_TYPE();
					return p;
				}else
				{
					return 0;
				}
			}
		}

		void deallocate(node_type* node_)
		{
			if(node_ == 0)
			{
				return;
			}
			//Call destructor
			node_->value().~CLASS_TYPE();
			//Insert free linked list header
			insert_head(get_node(free_node_head_),node_);
		}

		//Insert a node into the head of the linked list
		void insert_head(node_type* old_head,node_type* new_head)
		{
			if (old_head)
			{
				if(new_head)
				{
					//The pointer in front of the old header node points to the new header node
					old_head->set_prev(new_head->get_cur());
					//After the new header node, the pointer points to the old header node
					new_head->set_next(old_head->get_cur());
					//The new head node front finger pin is set to - 1
					new_head->set_prev(-1);
				}
			}
			else
			{
				if(new_head)
				{
					new_head->dis_from_list();
				}
			}
		}

		//Insert a node before the specified node
		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);
			//With front node
			if(tar_prev_node)
			{
				//The pointer before the target node points to the node to be inserted
				tar_prev_node->set_next(src_node->get_cur());
				//The pointer before the node to be inserted points to tar_prev_node
				src_node->set_prev(tar_prev_node->get_cur());
			}else
			{
				//The front finger of the node to be inserted is set to - 1
				src_node->set_prev(-1);
			}	
			tar_node->set_prev(src_node->get_cur());
			src_node->set_next(tar_node->get_cur());	
		}

		//Delete the head node and return to the new head node
		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;
		}

		//Delete Vertex 
		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_;					//Memory pool used
		int		    	used_node_head_;		//Index of the used node chain header node		
		int 			free_node_head_;		//Index of idle node chain header node
		node_type 		node_array_[MAX_COUNT];
	};
}
#endif


hash_map.h

//
//  hash_map.h
//  Fixed length hash_map
//  Created by DGuco on 2021/09/13.
//  Copyright  ©  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_);

			//The bucket is empty
			if ( it_begin == this->end())
			{	
				//Request a node
				node_type* new_node = hash_array_.allocate_node(v);
				if(!new_node)
				{
					return false;
				}
				//Set the chain head and tail
				buckets_[bucket].head_ = buckets_[bucket].tail_ = new_node->get_cur();
				return true;
			}

			//hash conflict
			iterator it_end  = hash_array_.make_iteator(buckets_[bucket].tail_);
			++it_end;

			//Check whether there is an element to change the key
			while ( it_begin != it_end )
			{
				//The element corresponding to the key already exists, failed to insert
				if (it_begin->first == v.first)
				{
					return false;
				}
				++it_begin;
			}

			//Request a node
			node_type* new_node = hash_array_.allocate_node(v,hash_array_.get_node(buckets_[bucket].head_));
			if( !new_node )
			{
				return false;
			}

			//Update header node information
			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 linked list only has itself
			if ( it == it_begin && it == it_end )
			{
				buckets_[bucket].head_ = buckets_[bucket].tail_ = -1;
			}
			else if (  it == it_begin ) //The hash linked list itself is the head
			{
				++ 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 )//The hash linked list itself is the tail
			{
				-- 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_;  			//Same bucket header node index
			std::size_t tail_;  			//Index of the same bucket tail node
		};

		bucket_type		buckets_[_Cap];     //bucket array
		hash_array	 	hash_array_;		//memory manager
	};
}

#endif // __YQ_HASHMAP_H__

Performance comparison with map, hashmap,unordermap

//  Created by Du Guochao on 20/05/20
//  Copyright  ©  Du Guochao. 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

// Get current microseconds
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 occupied memory size
#define HASH_ CONFLICT_ Rate 4 / / hash collision ratio

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;
}

test result

      It can be seen that the customized map not only ensures the minimum memory consumption and the fastest insertion speed, but also ensures the search efficiency, which is not much different from the map of the standard library.

      Note: there is no big gap when the conflict rate is relatively low. The test result above is that the conflict rate is 4, that is, every four elements share a hash_key, when the conflict rate is high, the performance will decline rapidly. The following is the operation result with a conflict rate of 1000, that is, hash in the test case_ CONFLICT_ The rate value is changed to 1000, which means that every 1000 elements share one hash_key, in extreme cases   HASH_ CONFLICT_ The maximum length of rate is the same as that of map. At this time, the map becomes an out of order array, and the insertion and lookup are traversing the entire array, which also brings performance uncertainty while ensuring the stability of memory.

 

 

 

 

Tags: C++

Posted on Tue, 21 Sep 2021 18:58:32 -0400 by rulian