【问题标题】:How to map 13 bit value to 4 bit code?如何将 13 位值映射到 4 位代码?
【发布时间】:2011-06-26 04:17:06
【问题描述】:

我有一个用于一些数据包处理程序的 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

【问题讨论】:

  • 只需移位和 XOR 3x (12 -> 4 * 3) 并下降一点?或 4x 并称其为“适合以后使用”:-) 现在只需选择 simple 的东西,然后再次分析,看看它是否真的“解决”了问题——或者热点已经转移到哪里. IE。 ((x >> 12) ^ (x >> 8) ^ (x >> 4) ^ x) & 0xF
  • 这更多地取决于您的主要关注点。您担心计算的 CPU 时间。您可以通过使用现在或多或少免费的内存来节省 CPU 时间。查表可能是最好的方法。
  • @thecoshman 但是由于内存局部性/缓存问题(主内存仍然相当“慢”)与一些“便宜”的 CPU 操作......看看两者如何公平会很有趣。

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


【解决方案1】:

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

【讨论】:

  • 感谢您的回答。遗憾的是,我无法完全控制源代码,几乎禁止为此目的创建新数组。
  • 好的。让我们制作查找表。如何最小化内存大小?如果用 10 std::vector 替换地图并使用 (key % 10) 来确定哪个向量具有该值,该怎么办。向量上的线性搜索。性能会显着提高吗?
【解决方案2】:

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

如果您巧妙地选择散列函数,几乎永远不会发生任何冲突。例如,从您的两个示例中,一个这样的哈希是对高字节的低半字节与低字节的高半字节进行异或(并用剩余的第 13 位做你喜欢的事情)。

【讨论】:

  • 谢谢。我会尝试“开放寻址”哈希表。
【解决方案3】:

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

【讨论】:

    【解决方案4】:

    如果您的表中真的只有 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))或哈希表(O(1))时的速度要差得多.但是您不是在谈论处理大量对象,因此渐近估计并不能真正为您带来太多收益。

    此外,std::vector 可以为您提供低开销和参考位置,这是 std::mapstd::list 都无法提供的。因此,较小的std::vector 更有可能完全保留在 L1 缓存中。由于几乎可以肯定是内存瓶颈导致了您的性能问题,因此使用std::vector 甚至我选择糟糕的算法也可能比树或链表更快。当然,只有少数可靠的个人资料可以肯定地告诉您。

    当然有一些算法可能是更好的选择:排序向量可能会提供更好的性能;一个经过良好调整的小型哈希表也可以工作。但是,我怀疑您在尝试改进简单的未排序向量时会很快遇到阿姆达尔定律。很快,您可能会发现自己遇到了函数调用开销或其他类似问题,这是您个人资料的主要贡献者。

    【讨论】:

    • 顺便说一句,我真的很喜欢#include &lt;algorithm&gt; 的想法并使用通用算法而不是手动编码for 循环。但是做很多事情都很痛苦,即使是我的琐碎putget
    【解决方案5】:

    我同意GWW,你最终不会使用那么多内存...... 但是,如果您愿意,您可以使用 11 或 13 个链表的数组,并使用 % 函数对键进行散列。如果键数小于数组大小,复杂度仍为 O(1)。

    【讨论】:

    • 很好的答案,但我不使用链接列表,而是使用更大的表并增加索引,直到找到该项目为止
    【解决方案6】:

    当您总是只有大约十个键时,请使用列表(或数组)。做一些基准测试,看看使用排序列表(或数组)和二分搜索是否会提高性能。

    【讨论】:

    • 我几乎放弃了散列的想法。最大 255 个密钥条目的 8k 查找表会做得更好.. 谢谢。
    【解决方案7】:

    您可能首先想查看是否有任何不必要的密钥查找调用。理想情况下,您只想对每个数据包执行一次 - 每次调用函数时都会产生一些开销,因此摆脱额外的调用是件好事。

    Map 通常很快,但是如果键映射到项目的方式中有任何可利用的模式,您可以使用它并可能做得更好。您能否提供更多有关键和相关 4 位值的信息?例如。它们是连续的吗?有某种模式吗?

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

    【讨论】:

    • 抱歉我的措辞不好。值为 6 字节大小的结构。键是随机的。键与其他键没有明显的映射关系。
    【解决方案8】:

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

    否则我会这样做 -

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

    30 个单元格和 10 个键的冲突应该很少见,但如果你得到一个,它会很快跳到下一个单元格,并且正常的查找只是一个模数和一个相当快的比较操作

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-11-15
      • 1970-01-01
      • 2011-03-27
      • 1970-01-01
      • 1970-01-01
      • 2022-10-13
      • 1970-01-01
      相关资源
      最近更新 更多