最快的哈希映射类型

时间:2013-01-01 17:41:56

标签: database hash hashmap

我需要散列从低端2 ^ 12的空间采样的~15000个无符号整数,高端的2 ^ 32。我还需要存储反向查找的索引。使用C ++ STL的一个简单例子是:

std::map<unsigned int, std::set<unsigned int /* unique indices */> > m;

在密集的情况下,我们可以将其视为:

std::vector<std::set<unsigned int /* unique indices */> > v;

现在出现问题。速度是这里最重要的因素,但我仍然在内存方面受到限制。我需要在内存中存储1000个这样的映射,并在低延迟应用程序中以高速率访问。查询应该在订单上纳秒秒。

我目前使用密集方法存储数据。但是,我想将需要散列的键的范围增加到2 ^ 32,这使得密集方法成为问题。请记住,我只需要在地图中存储~15000个键。

从好的方面来说,一旦构建了地图,我将永远不会再插入任何内容。我之后只会查询它。插入仍然需要相当快,但不如查询那么重要。

我尝试过的一些代码是:

Google SparseHash
Google DenseHash
STL unordered_map
STL地图

我不介意编写自己的哈希表。在我自己处理之前,我想获得一些专家意见。

1 个答案:

答案 0 :(得分:0)

平均GET操作应该在1ms以内,范围从189ns,1024个条目(内存为349KB)到888ns,27,648个条目(内存为6MB)。具有27k条目的条目的最大延迟是44,000ns。但是,如果平均时间对您而言非常重要而且经常没有高延迟,那么这可能基本上就是您想要的。我认为它可以进一步优化,但不确定要取得的成果。

typedef unsigned int uintptr;
typedef unsigned int uint32;
typedef unsigned short uint16;
typedef unsigned char uint8;


namespace anything { namespace linklist {
typedef struct _HDR {
    void              *next;
    void              *prev;
} HDR;

void *next(void *ptr) {
    if (ptr == 0) {
        return 0;
    }
    return ((void**)ptr)[0];
}

void add(void **chain, void *toadd) {
    ((void**)toadd)[0] = *chain;
    ((void**)toadd)[1] = 0;         /* set previous */

    /* set previous link if valid pointer */
    if (*chain)
        ((void**)*chain)[1] = toadd;

    *chain = toadd;
}
}}

namespace anything{ namespace hash {
   typedef struct _B {
      MASS_LL_HDR    llhdr;
      uint32         id;
      union {
         struct _B    *chain;
         uintptr      value;
      };
   } B;

   typedef struct _HT {
      B        *buckets;
      uint16   depth;
      uint8    bbl;
   } HT;

   void init(HT *ht, uint8 bbl) {
      ht->buckets = 0;
      ht->bbl = bbl;
   }

   void _free(B **chain, uint16 dcnt, uint16 dcntmax, uint32 *_m) {
      B        *ba, *_ba;

      for (ba = *chain, _ba = 0; ba; ba = _ba) {
         _ba = (B*)mass_ll_next(ba);

         if (dcnt < dcntmax - 1) {
            _free(&ba->chain, dcnt + 1, dcntmax, _m);
            *_m = *_m + 1;
            dfree(ba);
         }
      }

      /* zero the chain out */
      *chain = 0;
   }

   void free(HT *ht) {
      uint32      m;
      uint16      dm;

      dm = (sizeof(uintptr) * 8) / ht->bbl;
      m = 0;

      _free(&ht->buckets, 0, dm, &m);
   }

   int get(HT *ht, uintptr k, uintptr *v) {
      uintptr        a;
      B             *ba, **cur;

      uint16         bi, lcnt;
      uint32         mask;

      lcnt = (sizeof(uintptr) * 8) / ht->bbl;

      cur = &ht->buckets;

      mask = ~(~0 << ht->bbl);

      for (bi = 0; bi < lcnt; ++bi) {

         a = (k >> (bi * ht->bbl)) & mask;

         for (ba = *cur; ba; ba = (B*)mass_ll_next(ba)) {
            if (ba->id == a) {
               break;
            }
         }

         if (!ba) {
            return 0;
         }

         cur = &ba->chain;
      }

      *v = ba->value;
      return 1;
   }

   void put(HT *ht, uintptr k, uintptr v) {
      uintptr       a;
      B             *ba, **cur;

      uint16         bi, lcnt;
      uint32         mask;

      lcnt = (sizeof(uintptr) * 8) / ht->bbl;

      cur = &ht->buckets;

      mask = ~(~0 << ht->bbl);

      for (bi = 0; bi < lcnt; ++bi) {

         a = (k >> (bi * ht->bbl)) & mask;

         for (ba = *cur; ba; ba = (B*)mass_ll_next(ba)) {
            if (ba->id == a) {
               break;
            }
         }

         if (!ba) {
            ba = (B*)dmalloc(sizeof(B));
            ba->id = a;
            ba->chain = 0;
            mass_ll_add((void**)cur, ba);
         }

         cur = &ba->chain;
      }

      ba->value = v;
   }
}}

anything::hash::HT      ht;
anything::hash::init(&ht, 1);
anything::hash::put(&ht, key, value);
if (!anything::hash::get(&ht, key, &value) {
   printf("not found!\n");
}

您可以使用:: hash :: init(&amp; ht,4)将内存使用量减少到每15000个条目大约900kb,但这会增加延迟。