【问题标题】:Hash function to hash ip fragments散列函数对 ip 片段进行散列
【发布时间】:2017-02-08 19:33:27
【问题描述】:

我必须将传入的 ipv4 片段散列到大小为 20 的结构的静态数组中。用于散列的字段是 IP-ID(16 位)、协议(8 位)、源 IP 地址(32 位)和目标 IP 地址(32 位)。散列应该很快而且在 C 中实现起来不是很复杂。在这种情况下,什么是好的散列函数?

【问题讨论】:

  • 考虑到总的密钥空间,这听起来非常小,您要将 88 位的密钥空间粉碎成不到 5 位的“索引空间”。这听起来好像很可能会发生碰撞。如果项目会一直存在和/或快速出现,则哈希表可能会非常满(或具有非常长的外部链)。
  • 记录一些真实的实时数据并分析不同的散列函数,如果它们均匀分布在你的 20 个数组条目上。但是,由于数组非常小,您会遇到很多冲突。
  • 你说的是 20 字节或位的数组吗?如果它是 20 个字节,则您不必散列,但您可以 memcpy 他们。
  • 20 我的意思是没有条目是 20

标签: c algorithm function hash


【解决方案1】:

% 20 确实是次优的。像这样的简单哈希:

uint_32_t ip_fragment_hash(uint16_t ip_id, uint8_t ip_proto, uint32_t ip_src, uint32_t ip_dst)
{
  return (ip_id << 16 | ip_proto) ^ ip_src ^ ip_dst;
}

是 5 条 amd64 指令(shift、mov、xor、xor、or、xor)。但是加上% 20,你会得到 14 条指令,包括一个扩展乘法。将其设为 32,然后将其折叠:

uint32_t ip_fragment_hash(uint16_t ip_id, uint8_t ip_proto, uint32_t ip_src, uint32_t ip_dst)
{
  uint32_t h = (ip_id << 16 | ip_proto) ^ ip_src ^ ip_dst;
  h = h ^ (h >> 5) ^ (h >> 10) ^ (h >> 15)  ^ (h >> 20) ^ (h >> 25) ^ (h >> 30);
  return h % 32;
}

【讨论】:

  • 您的折叠添加了 12 条说明。但确实这是一个很好的技术。但是,使用ip_src ^ ip_dst 可能不是一个好主意,它会将双向流的两个方向映射到同一个哈希条目。
【解决方案2】:

如果您可以假设所有参数不相关且分布均匀,那么您可以将您感兴趣的字段的所有字节一起异或,最后将结果取模 20。但是,如果您有双向流,这意味着两个方向都被散列到相同的值(因为在这个简单的算法中交换字段并不重要)。 您可能应该看看像 MurmurHash 这样的快速通用哈希算法。

但是,由于您的哈希表中只有 20 个条目,您很可能会遇到冲突。如果接收到的数据包之间在时间上存在很强的相关性,例如,您很可能连续接收到许多具有相同标头的数据包,那么您最好只使用一个(或几个)只记录的缓存条目最后(唯一)接收到的标头,然后您立即将下一个数据包与那些缓存条目进行比较,而不进行任何散列。

不要忘记测量您正在使用的哈希或缓存方法的实际性能。

【讨论】:

    【解决方案3】:

    如果我理解正确并且您只需要 20 个可能的哈希值,您可以直接在您的数据上使用模运算符 (%)。

    如果您的数据分布良好并且您将它们存储为整数,您可能会使用hash = ip_fragment % 20

    似乎无论如何都会发生很多冲突,所以你可以保持简单。

    【讨论】:

      猜你喜欢
      • 2014-01-08
      • 2015-01-26
      • 1970-01-01
      • 2013-07-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-02
      • 1970-01-01
      相关资源
      最近更新 更多