基于共享内存多级hash设计

Feature list:

1.     支持Set/Get/Replace/Update, 优化编译下默认16级冲突下单进程下Set/Get可达170w/s。(理论上只要不达到内容的带宽限制,而且是多核的机器下性能是随线进程数陪增)

2.     支持多程下操作(可预见低的出错概率)

3.     支持CAS弱一致性检测(需要编译时开启)

4.     支持二进制文件导出/导入

5.     支持遍历处理(回调)

6.     可以自定义最大可冲突级数/单个桶最大长度(需要重新编译库)

注意:下面版本是32位机上实现的,64位机需要做些兼容修改

提供运维工具

a)     支持Bindump/Binrestore:共享内存保存到二进制文件和从二进制文件恢复到内存(支持跨机器操作)

b)     查看当前使用率与各级桶的使用率( 未实现 )

c)      修改为只读、只写模式( 未实现 )

d)     支持远程恢复与同步( 未实现 )

实现

1.     预设N个大素数(PRIMER_TABLE,目前取值是1000037~1001323,100个,最大数为1000,000*100 = 1亿个元素,默认初始化为16hash

2.     第一个素数作为一个桶长度,存储hash后的value,就是以%素数来取模

3.     存储共享内存是一个线性空间,所以要先计算好每列的偏移量

(PRIMER_TABLE_OFFSET[i] = PRIMER_TABLE[i])

4.     Value需要定义固定长度,不能是指针指向外一个内存块

5.     Hash值计算 ( key + Factor*k  )%L, Factor初设为一个素数作为hash因子,用于扩散hash 值,减少冲突发生 1 11%10(后继可以考虑更好的hash算法 )

6.     支持char* hash( time33 hash算法)

7.     每次set都要先遍历k阶是否key存在,插入中O(k)

8.     为了确保性能,可设置共享内存块不允许换出

9.     大小限制,32位机器受单个进程的进程空间限制,最大为4G。共享内在需要映射到进程空间,考虑到进程本身也需要使用到相应的内存,所以尽量限制在2G以下


设计理念:1)尽可能高的性能 2)能够提供尽可能的多线程安全 3)数据持久化

冲突处理

a)     先从第一个桶hash,如果当前位置已设置

(第一个桶hash + maxline*factor )%1000037

b)     再向下一个桶hash,按下一个桶的大素数来取余

c)      最大可冲突次数为hash级数可冲突次数越多,占满率越高,但是影响性能


优点:

1.非常地快!基于共享内存可以多进程共享

2.基于一维线性空间实现可从外部copy数据备份/恢复

3.空间利用率高,可达90%

4.Set/Get在优化编译后性能高


缺点:

1.     在设定的多次hash级内都冲突的话就插入不成功,但是机率很少。但是要慎重选择hash级数(建议起始级数设定16级,下面有数据说明)

2.     由于对同一个key的取value操作不是原子的,在非常低的概率下,可能在取一个key的同时在对这个keyset,导致只取值不完整(但是需要对一个key的操作在1/170w s内 )。除非是key很少,而且频繁访问(这种操作是建议加锁)

3.     单进程共享内在需要限制在4G(最好是2G)以内

4.     Key不能为0

 

测试数据

4字节的key, 12个字节value,直接rand(),成生的随机数可能不够足够精确)

测试环境:

Intel(R) Xeon(R) CPU E5405  @ 2.00GHz4核)

16G内存DDR3,内存带宽 =(1066/8)×64×8=68224Mbit/8 ~ 8GB/s

测试期间CPU 只使用到一个核 100%

 

10

16

20

32

总容量

1kw~100M

1.6kw(~200M)

2kw(~250M)

3.2kw(400M)

插入失败时的Rand次数

6,539,556

12,786,141

16,604,767

28,811,470

插入元素

6,529,720

12,748,311

16,540,688

28,618,666

耗时(未开-O3

15,536,487us

49,602,499us

81,243,290us

210,149,843us

存取率(未开-O3

~42w/s

~25.7w/s

~20w/s

~13w/s

耗时(打开-O3

2,910,194us

7,379,382us

15,267,184us

35,694,707us

存取率(打开-O3

~225w/s

~175w/s

~109w/s

~80w/s

利用率

0.6529

0.796655

0. 826898

0.894123

是否达上限


16( 10次测试时间)



从测试数据可得出结论:

1) 在正式使用时要打开O3的标记编译,可以编译成一个独立库来使用

2) 使用级数越多,使用率越高,性能也随之下降

3) 由于优化编译后性能非常高,所以可以尽量使用多的冲突级,减少因冲突无法插入的情况,同时提高hash空间的使用率真

4) 初始化桶是取从1000037后素数,可以扩大桶的长度,从而减少级数

5) 默认使用16级,根据需要自己再提高使用级数,可预计处理量约为1.2kw

 

冲突处理

问题:不同的字符串,可能hash出同一个值

Time33的算法,大概是 100w数据,有100~150个冲突

测试:

try set times:1000000

conflict times:105

conflict rate:0.000105

 

不同hash算法的测试结果:

测试条件:随机 32位数,%lu-%i-%j 的形式生成字符串,100w(排除因级数不够无法插入的情况,因开启测速gprof/-g, 所以性能有很大影响 )

Hash 函数

来源

结果

说明

time33_ hash

gcc/redis

hash total size:10001108

use time: 9940793us

 

try set times:1000000

conflict times:112

conflict rate:0.000112

最简单实现

bobjenkins_hash

memcache

hash total size:10001108

use time: 9407349us

 

try set times:1000000

conflict times:128

conflict rate:0.000128

通过多次rotkey散列,但是从测试效果来说不太明显

blizzard_hash

网上找的一个one-way hash

hash total size:10001108

use time: 9491830us

 

try set times:1000000

conflict times:125

conflict rate:0.000125

通过预生成一个查找表来生成hash,但是多了一层函数调用,在频率调用下反而速度有少少影响

murmur_hash

levelDB

hash total size:10001108

use time: 8885562us

 

try set times:1000000

conflict times:120

conflict rate:0.00012

支持设置初始值可以生成多个不同的hash值,较为简洁

结论:

1.       从多个开源软件用到的hash函数抽出测试来看,对同一个字符串结果集做hash出的key冲突率都差不多(这里的冲突是指两个不同的字符串hash出同一个一样的32

2.       综合来说选择 murmur_hash 比较合适

3.       同样需要解决的一个问题:怎么处理两个不同的字符串hash出同一个值的情况?

解决:

1)  以同一个字符串str, 同时hash出三个值

h1 = murmur_hash( str, 0 );

h2 = murmur_hash( str, 1 );

h3 = murmur_hash( str, 2 );

2)  h1 用作key的定位,计算hash要保存的位置。h2/h3作是比较值,即:对于一个key,需要满足三个hash值都一致时才认为是需要找的key

3)  对于不同的str1/str2, 如果三个值都一样发生的机率是 (150/1000000 )^3 ~ 0.000000000003375。即约为 1/30b的机率,基本可以忽略了!


多线、进程下一致性处理(未实现)

1.     CAS机制:

a)     为每一个NodeSet的时候分配一个cas值(返回的Node和存储Nodecas值一样,每次要更新这个Node时要检查是否cas与取出来时一致

b)     如果打开了这项检查,需要强制传入模块带上一个unsigned int cas;字段

c)      只有在Update一个keyvalue时才会造成多线程冲突,只是Set/Get是不会的。单线程也不会

d)     增加步进的概念:cas每次自增每个线程都不一样,这样可以每个线程有一个确定变量,如果是由其它线程修改的一定与本线程的cas不一样

                 i.          每个线程/进程有一个初始化,如果有10个进程就是0 ~9

               ii.          每次cas值增加都是按进程数来加,step[0] += 10,因为每个进程的cas都不会一样

              iii.          缺点是需要额外的初始化

场景:线程T1key1、线程T2Key1并发Get更新了Value值后想Set回去,可能会出现后一个操作覆盖前一个操作值,而且这个值是涉及到事务性的。正确是应该是T1 Set完后,T2才能取,串行化操作

=CAS就是解决这个问题,如果发现cas值不一样了,就会Set失败,需要重取再设置(假定某时刻T1 cas值为9T2 cas值为10。如果没有步进时,T1处理后cas值为10T2再处理就认为没有改变过)

=》一般情况下都不需要这样的事务性,所以是弱线程安全的(很少概率出现,即使出现这样的冲突也不会造成很在原影响

 

2.     弱线程安全

a)     在多线程使用下为每个线程生成一个成员对象操作)在很低的概率下,可能会出现两个不同的keyhash出同一个值,并同时操作同一个valuenode位置

b)     操作系统对同一个内存位置的操作不保证是串行化的,

c)      概率计算:由于每一个桶的取模素数都不一样,所以大约出现冲突概率是1/N(默认桶长取为100w,所以出现概率为 1/100w,而我们的并发处理不可能达到这个数量级)


3.     强线程安全

a)     情景1Write Lock

                 i.          为了达到强线程安全,引入加锁机制

               ii.          每个Node增加一个字段char bLock;( 可设置flag的一个位),每次在写的时候都设置为1,写完后再设置为0

              iii.          当另一个线程对这个Node处理里就需要先判断是否被锁

b)     情景2Update Lock

                

处理的Scale

1.     在默认16级的情况下大约能处理1.2kw的用户量,如果想提高处理能力可以

a)     扩展每个桶的长度 100w->1000kw

b)     增加级数

c)      注意系统内存量是否足够:容量*Value len。单进程共享内在需要限制在2G以内

d)     分布式处理,考虑到单机单进程受限于内存大小,所以数据一致性尽量考虑由上层处理。如多机按取模处理,每个机器只负责部分号段,或者实现一致性的算法为减少扩容影响

库级支持定时遍历处理

       有一种使用场景,需要遍历Hash表里的所有元素并进行相关的操作。基本实现就是传入一个预设参数的函数指针:

初始版本:

       Foreach( void (*callback)( const unsigned long _key, valueType& _value) );

但是发现,只能处理全局的一些外部变量,无法处理指定类成员。所以添加一个外部传入的param指针,使得可以传入到回调函数里处理

第二版:

       Foreach( void (*callback)( const unsigned long _key, valueType& _value, void* param_out ), void* param_in );

解决了参数问题,还有一个运行时问题:如果每次遍历触发的元素较多,可能一次过处理量会很大!这里需要使用者注意,应该多加一个上次检查时间来触发。

定时扫描器功能

TimerCallBack( fn, timer_id, set_timeout, set_excute_time_interval );

a)     设置一个为定时器设定的回调函数fn与处理id timer_id, 在每个set_timeout触发时遍布所有元素,为每个超过set_excute_time_intervalNode调用一下回调函数fn

b)     每个Node需要有一个dwLastCheckTs字段

 

后续功能构想

2.     支持共享内存/内存/内在映射文件

3.     动态自动扩展=rehash( redis实现 )

4.     运维工具

a)     格式化输出指定key-Value/全输出


ChsDB(Cola’s hash shm DB )基于以上版本的扩展版( 未实现 )

理念:数据先存共享内存再同步,实现一个轻量级高速key-value 数据库

1.     支持只读、只写模式(需要由提供的运维工具修改)

2.     支持定时数据快照snapshot(写本地文件)

3.     支持主-从(多从),主-主模式的数据同步

4.     库级支持定时遍历处理(回调)


错误处理

1Share memory get failed

1) 检查系统共享内存量设置是否足够大减少级数或者

2) 是否有足够内存(级数*100w*sizeof(TemplateNode) 

3) 是未初始成每个线程一个对象(使用全局对象来操作,确保只操加载到进程空间一次)

共享内存操作

Ipcs –l 列出所有共享内存相关的信息

ipcs-m 列出本机使用的共享内存列表

ipcrm –M id 删除一个共享内存块

使用说明(附源码)

1.     初始化

1)定义存储的数据结构

struct HASH_MSG_INFO

{

    unsigned int dwUin;

    unsigned int dwLastCheck;

    unsigned char ucFlag;

    char ucResvered[7];

};

 

2) 生成对象

CHashShm< HASH_MSG_INFO > ht(123);

if( !ht. IsInitOK() )

return false;

 

(1)默认16级冲突,可自定义级数: CHashShm< HASH_MSG_INFO,32 > ht( key_t(123));

(2)设置步进:CHashShm< HASH_MSG_INFO,32,1 > ht(key_t(123));用于多线/进程下弱冲突处理

(3)使用全局变量的方式,确保只操加载到进程空间一次

 

2.     Set/Get/Replace/Update

Set:如果Key不存在,则插入hash表,如果存在就返回失败(支持key类型为unsigned long,char*+len,string)

 

HASH_MSG_INFO hMsg;

hMsg.dwUin = 12345;

hMsg.dwLastCheck= 12345;

if(ht.Set( 12345, hMsg  ) != HASHSHM_OK )

{…}

Else{…}

 

Get

HASH_MSG_INFO hMsg;

if(ht.Get( 12345, hMsg  ) != HASHSHM_OK )

 

Replace: 如果Key不存在,则插入hash表,如果存在就替换

  Update:在编译时打开CAS开关,会做线程数据检查,需要先Get。如果数据已经被修改则update失败

 

注:由于不支持从value查到key,所以value里最好有一个相同的key字段。如果是char/string类型的key,还需要是定长,不能是STL对象

 

3.     设置进程退出是detach共享内存

ht. SetBehavior(HASH_ENABLE_DETACH )

 

4.     遍历

//遍历hash shm中的所有在线列表

g_ht.Foreach( ProcMsgCheck, this );

 

ProcMsgCheck 是回调函数

void ProcMsgCheck( unsigned long key, ST_ON_STATE& value,void* param )

注意 value 直接引用共享内在的位置!!




头文件:

#ifndef _COLALIANG_HASH_SHM_H_
#define _COLALIANG_HASH_SHM_H_

//-------------------------------------------
//Cola's Hash Shm Library 1.03
//colaliang( SNG Instant Messaging Application Department )
//last update: 2012-12-27

//Makefile
//	g++ -O3 -c hash_shm.cpp
// ar cq libchs.a hash_shm.o

//Enable extra function:
//1. CAS
// -DHASH_SHM_ENABLE_CAS

//2. TIMER_CALL_BACK
//-DHASH_SHM_ENABLE_TCB

//多级hash实现解释
//
//
//					i
//PRIMER_TABLE:	2	3	5	7	11	13	17	( bucket size, using primes )
//PRIMER_TABLE_TATAL:	0	2	5	10	17	28	41	( line address begin position )
//
//1. 冲突解决:通过检查 ( _key + lines*factor ) % PRIMER_TABLE[i]; 的位置是否已经设置, 否则检查下一个位置
//2. hash 多级取模, 通过计算素数来尽量散列
//3. lines 指定了最多的可能冲突次数
//4. maxline 确定共享内级的冲突最大级数
//5.初始化时需要指定 valueType, 元素固定长度, 不能为stl

//------------------------------------------

#include<iostream>
#include<cstdlib>
#include<cmath>
#include<sys/shm.h>

#include<fstream>
#include<vector>
#include<string>
using  std::cout;
using  std::cerr;
using  std::string;
using  std::vector;
using  std::ifstream;
using  std::ofstream;
using  std::ios;

extern const int PRIMER_TABLE_LEN;
extern const int PRIMER_TABLE[ ] ;

enum HASH_RETURN_CODE
{
    HASHSHM_OK = 0,
    HASHSHM_KEYEXIST,
    HASHSHM_ERROR,
    HASHSHM_NOTFOUND,
    HASHSHM_INSERTERROR,
    HASHSHM_OUTOFMEM,
    HASHSHM_UPDATE_ERROR,
};

enum HASH_STATUS
{
    HASH_STATUS_NORMAL			= 0x1,
    HASH_STATUS_WRITE_ONLY	= 0x2,
    HASH_STATUS_READ_ONLY		= 0x4
};

enum HASH_ENABLE_FLAG
{
    HASH_ENABLE_NONE = 0x0,
    HASH_ENABLE_DETACH = 0x1,		//detach from shm, when all process detach,the shm will release

};

//max conflict time, max items = MAX_LINES * BaseBucketLen ( default 100w )
const int MAX_LINES = 32;

//hash factor, use for better hash distrubition
const int FACTOR = 5381;

//use for thread magic id
const int THREAD_STEP = 1;

//string hash time33
unsigned long hash_time33(char const *str, int len =-1 );

template< typename valueType, int lines = MAX_LINES, int thread_step = THREAD_STEP >
class CHashShm
{
    public:

        CHashShm():bInitOk(false){};
        virtual ~CHashShm();

        //init with the share memory key,it will get share memory
        //if fail,exit
        bool Init( key_t shm_key );

    public:
        //set node into shm
        //	1) if the _key exists,return HASHSHM_KEYEXIST
        //	2) if set success,return HASHSHM_OK
        //	3) if fail return HASHSHM_ERROR
        int Set( const unsigned long _key ,const valueType &_value);
        int Set( const char* skey, const int len ,const valueType &_value );
        int Set( const string& strkey ,const valueType &_value );

        //get node from shm 
        int Get( const unsigned long _key,  valueType& _value );
        int Get( const char* skey, const int len, valueType& _value );
        int Get(  const string& strkey , valueType &_value );

        int Replace( const unsigned long _key ,const valueType& _value );
        int Replace( const char* skey, const int len,const valueType& _value );
        int Replace(  const string& strkey ,const valueType &_value );

        //if _key not in the table,return HASHSHM_NOTFOUND, else remove the node,set the node key 0 and return HASHSHM_OK
        int Remove( const unsigned long _key ); 
        int Remove( const char* skey, const int len );
        int Remove( const string& strkey );

        //callback function/param for execute, param_in will be pass to callback function as param_out
        void Foreach( void (*callback)( const unsigned long _key, valueType& _value, void* param_out ), void* param_in );

        //remove all the data
        void Clear(); 

        //operation enable behavior
        int SetBehavior( unsigned int iflag );
        int UnsetBehavior( unsigned int iflag );

        bool IsInitOK(){ return bInitOk; }
    public:
        bool BinDump( char* filename = "./chsbin" );
        bool BinRestore( char* filename = "./chsbin" );

    public:
        //the rate of the space used
        double GetFullRate() const;

        //the bucket size( begin 0 )
        void GetBucketSize( unsigned int index ) const ;

        //get one bucket's item count
        int GetBucketUseSize( unsigned int index ) const ;

        unsigned long GetCurSize() { return m_hashHead->currentSize; }       

        unsigned long GetSize() { return maxSize; };

    private:
        //the start position of the share memory
        //  1) the begin mem space used to storage the runtime data, reserved 16 byte
        //  2) currentSize = (unsigned long *)((long)mem)
        void *mem;

        //current size of the table ,the pointer of the shm begin 
        struct hash_head{
            unsigned long currentSize;  
            unsigned long status;  
            unsigned long reservered2;  
            unsigned long reservered3;  
        };
        hash_head * m_hashHead;

        //the size of the share memory
        unsigned long memSize;   

        //PRIMER_TABLE_TATAL[i] is the summary of the PRIMER_TABLE when x<=i 
        unsigned long PRIMER_TABLE_TATAL[lines];    

        //the size of the table
        unsigned long maxSize;        

        //write by the find function,record the last find place
        void *lastFound;        

        //Init flag
        bool bInitOk;

        //enable operation flag
        unsigned int flag;

        //the node of the hash table
        //	1) when key==0,the node is empty
        //	2) name-value pair
        struct hash_node{        
            unsigned long key;
            valueType value;    
        };

    private:
        //if _key in the table,return HASHSHM_OK,and set lastFound the position,otherwise return HASHSHM_NOTFOUND
        int find( const unsigned long _key );   

        //get share memory,used by the constructor
        bool getShm( key_t shm_key );    

        //get the positon with the (row,col), map to line pos
        void *getPos( const unsigned int _row,  const unsigned long _col )
        {
            //calculate the positon from the start
            unsigned long pos =  PRIMER_TABLE_TATAL[_row] + _col;

            if ( pos >= maxSize + sizeof(hash_head))
                return NULL;

            return (void *)((long)mem+ sizeof(hash_head) + pos*sizeof(hash_node));
        }

};

template< typename vT, int lines, int thread_step >
bool CHashShm<vT,lines,thread_step>::Init( key_t shm_key )
{   
    if( lines > PRIMER_TABLE_LEN )
       return false; 

    //constructor with get share memory
    maxSize=0;

    int i;
    for(i=0;i<lines;i++)
    {    
        //caculate the PRIMER_TABLE_TATAL
        maxSize+=PRIMER_TABLE[i];
        if(i!=0)
            PRIMER_TABLE_TATAL[i] = PRIMER_TABLE_TATAL[i-1]+PRIMER_TABLE[i-1];
        else 
            PRIMER_TABLE_TATAL[i]=0;   
    }

    //extra 16byte for use
    memSize=sizeof(hash_node)*maxSize + sizeof(hash_head);     
    if(!getShm( shm_key ))
        bInitOk = false;
    else
    {

        m_hashHead = (hash_head*)((long)mem );
        m_hashHead->currentSize = 0;

        //initialize as normal
        m_hashHead->status = HASH_STATUS_NORMAL;

        //init operation enable to default none
        flag = HASH_ENABLE_NONE;

        bInitOk = true;
    }

    return bInitOk;
}    

template< typename vT, int lines, int thread_step >
CHashShm<vT,lines,thread_step>::~CHashShm()
{
    //detach from share mem if HASH_ENABLE_DETACH setd. Because 
    if( flag& HASH_ENABLE_DETACH )
        shmdt( mem );
}

template< typename vT, int lines, int thread_step >
void CHashShm<vT,lines,thread_step>::Clear()
{
    memset(mem,0,memSize);
    m_hashHead->currentSize=0;
}

template< typename vT, int lines, int thread_step >
bool CHashShm<vT,lines,thread_step>::getShm( key_t shm_key )
{
    int shm_id=shmget(shm_key,memSize,0666);

    //check if the shm exists
    if( shm_id==-1 )   
    {
        //create the shm
        shm_id=shmget(shm_key,memSize,0666|IPC_CREAT);
        if(shm_id==-1){
            cerr<<"Share memory get failed\n";
            return false;
        }

        //create the shm
        mem=shmat(shm_id,NULL,0);

        memset(mem,0,memSize);

        if(int(mem)==-1){
            cerr<<"shmat system call failed\n";
            return false;
        }
    }
    else
    {
        //exist, point to the shm
        mem=shmat(shm_id,NULL,0);

        if(int(mem)==-1){
            cerr<<"shmat system call failed\n";
            return false;
        }
    }

    return true;
}

template< typename vT, int lines, int thread_step >
int CHashShm<vT,lines,thread_step>::find( const unsigned long _key)
{
    unsigned long hash;
    hash_node *pH=NULL;
    for(int i=0;i<lines;i++)
    {
        //calculate the col position
        hash = ( _key + lines * FACTOR ) % PRIMER_TABLE[i];   
        pH = ( hash_node *)getPos( i, hash );

        //position exceed the shm size, just break
        if( NULL == pH )
            break;

        if( pH->key == _key )
        {
            lastFound=pH;
            return HASHSHM_OK;
        }
    }

    return HASHSHM_NOTFOUND;
}

template< typename vT, int lines, int thread_step >
int CHashShm<vT,lines,thread_step>::Set( const unsigned long _key,const vT&_value)
{
    //if the key exists
    if( find(_key )== HASHSHM_OK )
        return HASHSHM_KEYEXIST;

    unsigned long hash;
    hash_node *pH=NULL;

    for(int i=0;i<lines;i++)
    {    
        //minimize conflict using primes
        // 1) firs hash pos( calculate the col position )
        hash=( _key + lines * FACTOR ) % PRIMER_TABLE[i];

        // 2) second hash pos( row, col )
        pH=(hash_node *)getPos( i,hash );

        // insert position exceed the shm size
        if( NULL == pH )
            return HASHSHM_OUTOFMEM;

        //find the insert position,insert the value
        if( pH->key== 0 )
        {      

            pH->key = _key;
            pH->value = _value;

            m_hashHead->currentSize++;

            return HASHSHM_OK;
        }
    }

    //all the appropriate position filled
    return HASHSHM_ERROR;   
}

template< typename vT, int lines, int thread_step >
int CHashShm<vT,lines,thread_step>::Set( const char* skey, const int len, const vT &_value )
{
    unsigned long ulHashKey = hash_time33( skey, len );
    return Set( ulHashKey, _value );

}

template< typename vT, int lines, int thread_step >
int CHashShm<vT,lines,thread_step>::Set( const string& strkey, const vT &_value )
{
    unsigned long ulHashKey = hash_time33( strkey.data(), strkey.size() );
    return Set( ulHashKey, _value );

}

template< typename vT, int lines, int thread_step >
int CHashShm<vT,lines,thread_step>::Get( const unsigned long _key, vT& _value )
{
    if( find( _key ) != HASHSHM_OK )
        return HASHSHM_NOTFOUND;

    //memset( &_value, &((hash_node*)lastFound)->value, sizeof(_value) );
    //Do I need memset?( c++' bitwise copy )
    _value = ((hash_node*)lastFound)->value;

    return HASHSHM_OK;
}

template< typename vT, int lines, int thread_step >
int CHashShm<vT,lines,thread_step>::Get( const char* skey, const int len, vT&_value )
{
    unsigned long ulHashKey = hash_time33( skey, len );
    return Get( ulHashKey, _value );

}

template< typename vT, int lines, int thread_step >
int CHashShm<vT,lines,thread_step>::Get( const string& strkey, vT&_value )
{
    unsigned long ulHashKey = hash_time33( strkey.data(), strkey.size() );
    return Get( ulHashKey, _value );

}

template< typename vT, int lines, int thread_step >
int CHashShm<vT,lines,thread_step>::Replace( const unsigned long _key,const vT &_value)
{
    //if the key exists, replace the value
    if( find(_key )== HASHSHM_OK )
    {
        ((hash_node*)lastFound)->value = _value;
        return HASHSHM_OK;
    }

    //if not found, the find a hash place
    unsigned long hash;
    hash_node *pH=NULL;

    for(int i=0;i<lines;i++)
    {    
        //minimize conflict using primes
        // 1) firs hash pos( calculate the col position )
        hash=( _key + lines * FACTOR ) % PRIMER_TABLE[i];

        // 2) second hash pos( row, col )
        pH=(hash_node *)getPos( i,hash );

        // insert position exceed the shm size
        if( NULL == pH )
            return HASHSHM_OUTOFMEM;

        //find the insert position,insert the value
        if( pH->key== 0 )
        {       
            pH->key = _key;
            pH->value = _value;
            m_hashHead->currentSize++;
            return HASHSHM_OK;
        }
    }

    //all the appropriate position filled
    return HASHSHM_ERROR;   
}

template< typename vT, int lines, int thread_step >
int CHashShm<vT,lines,thread_step>::Replace( const char* skey, const int len, const vT&_value )
{
    unsigned long ulHashKey = hash_time33( skey, len );
    return Replace( ulHashKey, _value );

}

template< typename vT, int lines, int thread_step >
int CHashShm<vT,lines,thread_step>::Replace( const string& strkey, const vT&_value )
{
    unsigned long ulHashKey = hash_time33( strkey.data(), strkey.size() );
    return Replace( ulHashKey, _value );

}

template< typename vT, int lines, int thread_step >
int CHashShm<vT,lines,thread_step>::Remove( const unsigned long _key)
{
    //not found
    if( find(_key) != HASHSHM_OK )
        return HASHSHM_NOTFOUND;

    hash_node *pH=(hash_node *)lastFound;

    //only set the key 0
    pH->key=0; 
    m_hashHead->currentSize--;

    return HASHSHM_OK;
}

template< typename vT, int lines, int thread_step >
int CHashShm<vT,lines,thread_step>::Remove( const char* skey, const int len )
{
    unsigned long ulHashKey = hash_time33( skey, len );
    return Remove( ulHashKey );

}

template< typename vT, int lines, int thread_step >
int CHashShm<vT,lines,thread_step>::Remove( const string& strkey )
{
    unsigned long ulHashKey = hash_time33( strkey.data(), strkey.size() );
    return Remove( ulHashKey );

}

template< typename vT, int lines, int thread_step >
void CHashShm<vT,lines,thread_step>::Foreach(void (*callback)( const unsigned long _key,vT &_value,void * param_out ), void* param_in )
{
    typedef  unsigned long u_long;
    u_long beg=(u_long)mem + sizeof(hash_head);
    u_long end=(u_long)mem+ sizeof(hash_head) + sizeof(hash_node)*(PRIMER_TABLE[lines-1]+PRIMER_TABLE_TATAL[lines-1]);

    hash_node *p=NULL;
    for(u_long pos=beg;pos<end;pos+=sizeof(hash_node))
    {
        //directly referece the actual memory place, so value can be modify outside directly
        p=(hash_node *)pos;
        if(p->key!=0)
            callback( p->key,p->value, param_in );
    }
}

template< typename vT, int lines, int thread_step >
bool CHashShm<vT,lines,thread_step>::BinDump( char* filename)
{

    ofstream os( filename, ios::out | ios::binary );
    if( !os )
        return false;

    os.write( (char*)mem, memSize ); 
    os.close();

    return true;
}

    template< typename vT, int lines, int thread_step >
bool CHashShm<vT,lines,thread_step>::BinRestore( char* filename)
{

    ifstream ios( filename, ios::binary );
    if( !ios )
        return false;

    // get length of file:
    ios.seekg (0, ios::end);
    unsigned long   file_length = ios.tellg();
    ios.seekg (0, ios::beg);

    if( file_length != memSize )
        return false;

    ios.read( (char*)mem, memSize ); 

    ios.close();
    return true;
}

//the rate of the space used
template< typename vT, int lines, int thread_step >
double CHashShm<vT,lines,thread_step>::GetFullRate() const
{ 
    return double( m_hashHead->currentSize )/maxSize;
};

//the bucket size( begin 0 )
template< typename vT, int lines, int thread_step >
void CHashShm<vT,lines,thread_step>::GetBucketSize( unsigned int index ) const
{ 
    return index>=lines?0:PRIMER_TABLE[index]; 
} ;

//get one bucket's item count
template< typename vT, int lines, int thread_step >
int CHashShm<vT,lines,thread_step>::GetBucketUseSize( unsigned int index )  const
{
    if(  index>=lines )
        return 0;

    int sum = 0;
    hash_node * pNode = (hash_node *)((long)mem+ sizeof(hash_head) + index*sizeof(hash_node));

    for( int i=0; i<PRIMER_TABLE[index]; i++ )
    {
        if( pNode->key != 0 )
            ++sum;
    }

    return sum;
}

//set operation enable flag
    template< typename vT, int lines, int thread_step >
int CHashShm<vT,lines,thread_step>::SetBehavior( unsigned int iflag ) 
{
    flag |= iflag;
    return flag;
}

    template< typename vT, int lines, int thread_step >
int CHashShm<vT,lines,thread_step>::UnsetBehavior( unsigned int iflag ) 
{
    flag &= ~iflag;
    return flag;
}

#endif


Cpp文件:

//-------------------------------------------
//Cola's Hash Shm Library 1.03
//colaliang( SNG Instant Messaging Application Department )
//last update: 2012-12-27

#include "hash_shm.h"


const int PRIMER_TABLE_LEN = 64;
const int PRIMER_TABLE[ PRIMER_TABLE_LEN ] = { 
    1511,1523,1531,1543,1549,1553,1559,1567,1571,1579,1583,1597,1601,1607,1609,1613,1619,1621,1627,1637,1657,1663,1667,1669,1693,
	1697,1699,1709,1721,1723,1733,1741,1747,1753,1759,1777,1783,1787,1789,1801,1811,1823,1831,1847,1861,1867,1871,1873,1877,1879,
	1889,1901,1907,1913,1931,1933,1949,1951,1973,1979,1987,1993,1997,1999
};

unsigned long hash_time33(char const *str, int len  ) 
{ 
	//get from php
	unsigned long hash = 5381; 
	
	//variant with the hash unrolled eight times
	// if len not specify, use default time33
	char const *p = str; 
	if( len < 0 )
	{ 
		for(; *p; p++) 
		{ 
			hash = hash * 33 + *p; 
		}
	
		return hash; 
	}

#define TIME33_HASH_MIXED_CH() hash = ((hash<<5)+hash) + *p++
	//use eighe alignment
	for (; len >= 8; len -= 8) 
	{ 
		TIME33_HASH_MIXED_CH();	// 1
		TIME33_HASH_MIXED_CH(); // 2 
		TIME33_HASH_MIXED_CH();	// 3 
		TIME33_HASH_MIXED_CH(); // 4 
		TIME33_HASH_MIXED_CH(); // 5 
		TIME33_HASH_MIXED_CH(); // 6 
		TIME33_HASH_MIXED_CH(); // 7 
		TIME33_HASH_MIXED_CH(); // 8 
	} 
	switch (len) 
	{ 
		case 7: TIME33_HASH_MIXED_CH();
		case 6: TIME33_HASH_MIXED_CH();
		case 5: TIME33_HASH_MIXED_CH();
		case 4: TIME33_HASH_MIXED_CH();
		case 3: TIME33_HASH_MIXED_CH();
		case 2: TIME33_HASH_MIXED_CH();
		case 1: TIME33_HASH_MIXED_CH(); break; 
		case 0: break; 
	} 
	
	return hash; 
}


  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值