如何将13位值映射到4位代码?

时间:2011-02-12 18:42:22

标签: c++ c algorithm data-structures hash

我有一个用于某些数据包处理程序的std :: map。

我在分析之前没有注意到,但不幸的是,这个地图查找单独消耗大约10%的CPU时间(称为太多时间)。

通常输入数据中最多只有10个键。所以我试图在地图前面实现一种密钥缓存。

键值为13位整数。我知道只有8192个可能的键,8192个项目的数组可以提供恒定的时间查找,但我觉得已经感到羞愧,不想使用这种天真的方法:(

现在,我只是猜测一些散列方法,可以非常快速地为13位整数产生4位代码值。

有什么好主意吗?

提前致谢。

更新

除了我的耻辱之外,我无法完全控制源代码,并且几乎禁止为此目的制作新阵列。

项目经理说(谁运行了探查器)链表显示小的性能增益,建议使用std :: list而不是std :: map。

更新

密钥的值是随机的(没有关系),并且没有良好的分布。

样品:
1)0x100,0x101,0x10,0x0,0xffe
2)0x400,0x401,0x402,0x403,0x404,0x405,0xff

8 个答案:

答案 0 :(得分:3)

假设您的哈希表包含一些基本类型 - 它几乎没有内存。即使在64位系统上,它也只有64kb的内存。使用像这样的查找表没有任何耻辱,它具有一些你可以获得的最佳性能。

答案 1 :(得分:2)

除非您正在为8K非常重要的某种嵌入式系统编写,否则只需使用该阵列并继续。如果你真的坚持做其他事情,你可能会考虑一个完美的哈希生成器(例如,gperf)。

答案 2 :(得分:2)

如果表中确实只有10个活动条目,您可能会认真考虑使用未排序的向量来保存此映射。像这样:

typedef int key_type;
typedef int value_type;
std::vector<std::pair<key_type, value_type> > mapping;

inline void put(key_type key, value_type value) {
    for (size_t i=0; i<mapping.size(); ++i) {
        if (mapping[i].first==key) {
            mapping[i].second=value;
            return;
        }
    }
    mapping.push_back(std::make_pair(key, value));
}    

inline value_type get(key_type key) {
    for (size_t i=0; i<mapping.size(); ++i) {
        if (mapping[i].first==key) {
            return mapping[i].second;
        }
    }
    // do something reasonable if not found?
    return value_type();
}

现在,这些算法的渐近速度(每个O(n))比使用红黑树(如std::map O(log n))或散列的情况要差得多表格1}})。但是你不是在谈论处理大量的物体,所以渐近的估计并没有真正为你带来太大的收获。

此外,O(1)会为您带来低开销和引用位置,std::vectorstd::map都无法提供。因此,小std::list更有可能完全保留在L1缓存中。由于几乎可以肯定是导致性能问题的内存瓶颈,使用std::vector即使我选择不当的算法也可能比树或链表更快。当然,只有少数可靠的配置文件会告诉您。

当然算法可能是更好的选择:有序矢量可能会提供更好的性能;一个调整良好的小哈希表可能也可以。我怀疑你会很快遇到Amdahl定律试图改进一个简单的未分类矢量。很快你就会发现自己遇到了函数调用开销或其他一些问题,作为你的个人资料的一个重要贡献者。

答案 3 :(得分:2)

您可能希望使用中间解决方案和open addressing技术:一个大小为256的数组。数组的索引是一些简单的散列函数,如两个字节的XOR。数组的元素是struct {key,value}。通过将碰撞元素存储在下一个可用索引处来处理冲突。如果需要从数组中删除元素,并且删除很少,则只需重新创建数组(从剩余元素创建临时列表,然后从此列表创建数组)。

如果您巧妙地选择哈希函数,几乎不会发生任何冲突。例如,从你的两个例子中,一个这样的散列就是XOR低字节的高字节和低字节的高半字节(并且在剩下的第13位做你喜欢的事情)。

答案 4 :(得分:1)

我同意GWW,你最终没有使用这么多内存...... 但是如果需要,可以使用11或13个链表的数组,并使用%函数对键进行散列。如果密钥数小于数组大小,复杂性帐篷仍然是O(1)。

答案 5 :(得分:1)

如果您总是只有十个键,请使用列表(或数组)。做一些基准测试,以确定是否使用排序列表(或数组)和二进制搜索将提高性能。

答案 6 :(得分:0)

您可能首先想知道是否有任何不必要的密钥查找调用。理想情况下,你只希望每个数据包执行一次 - 每次调用一个函数时都会有一些开销,所以摆脱额外的调用是好的。

地图通常非常快,但如果按键映射到项目的方式中有任何可利用的模式,您可以使用它并可能做得更好。您能否提供有关密钥和相关4位值的更多信息?例如。他们是顺序的,是否有某种模式?

最后,正如其他人所提到的,查找表非常快,8192个值* 4位只有4kb,确实是一个很小的内存。

答案 7 :(得分:0)

我会使用查找表。除非你使用微控制器或其他东西,否则它很小。

否则我会这样做 -

生成一个包含30个元素的表格。 对于每个查找计算哈希值(键%30)并将其与表中该位置的存储密钥进行比较。如果钥匙在那里,那么你找到了你的价值。如果插槽为空,则添加它。如果密钥错误,则跳到下一个空闲单元格并重复。

30个单元格和10个键碰撞应该是罕见的,但如果你得到一个,它可以快速跳到下一个单元格,而正常的查找只是模数和比较操作,所以相当快#/ p>