【问题标题】:Optimal IP subnet matching最佳 IP 子网匹配
【发布时间】:2011-11-10 17:33:58
【问题描述】:

以下代码似乎是我的程序中最热门的地方。

JAVA_OPTS=-Xprof 输出:

     Compiled + native   Method
  5.7%   173  +     0    scala.collection.IndexedSeqOptimized$class.slice
  5.1%   156  +     0    scala.collection.IndexedSeqOptimized$class.foreach
  2.9%    87  +     0    java.util.regex.Pattern$BmpCharProperty.match
  2.5%    76  +     0    scala.collection.IndexedSeqOptimized$class.sameElements
  2.4%    73  +     0    trafacct.SubNet.contains

Slice、sameElements 甚至 foreach 调用似乎也是从这里最常用的。有人可以就如何优化contains() 方法提供一两个建议吗?也许一些技术允许字节分析而不将它们转换为整数?还是没有切片的可靠全序列方法?

函数 SubNet.contains() 将 IP 地址与子网匹配。

object SubNet {
    def toInts(bytes: Seq[Byte]): Seq[Int] = bytes.map(_.toInt & 0xFF)
}

case class SubNet(ip:InetAddress,  maskLength:Int) extends HostCategory {
    import SubNet.toInts
    private val bytes: Int = maskLength / 8
    private val subnet = toInts(ip.getAddress)
    private val bits = bytes * 8 - maskLength
    def contains(host: Host) = {
        if (host.ip == null && ip == null) {
            true
        } else if (this.ip == null) {
            false
        } else {
            val address = toInts(host.ip.getAddress)
            if (address.length != subnet.length) {
                false
            } else {
                if (address.slice(0, bytes) != subnet.slice(0, bytes)) {
                    false
                } else {
                    ((address(bytes) >> (8-bits) ^ subnet(bytes) >> (8-bits)) & 0xFF) == 0
                }
            }
        }
    }
}

我明白,这种优化不会给我带来更好的吞吐量,我只是觉得我在这个简单的函数中花费了这么多时间做错了。

这段代码应该兼容 IPv6(16 字节),我不喜欢单独处理 IPv4 大小写的想法。

【问题讨论】:

标签: scala ip subnet


【解决方案1】:

你本身并没有做错什么;在处理原语时,您只是在使用旨在易于使用而不是性能的集合。

如果您想加快速度,您将通过切换到使用数组和 while 循环获得最大的提升。我并不完全清楚您编写的代码甚至适用于 IPv6,但以 IPv6 格式存储的 IPv4 地址除外,因为您可能有一个包含超过 256 个项目的子网。此外,通过测试长度,您假设同一地址没有混合 IPv6/IPv4 表示。

我会忘记整个“toInts”,只存储字节数组;然后做类似(警告,未经测试)

def contains(host: Host): Boolean = {
//...
  if (address.length != subnet.length) false
  else {
    var i = 0
    while (i<address.length-1) {
      if (address(i) != subnet(i)) return false
      i += 1
    }
    (address(i)&0xFF) >> (8-bits) ^ (subnet(i)&0xFF) >> (8-bits) == 0
  }
}

它实际上并不比您原来的解决方案复杂,运行速度应该快 10 倍左右。

【讨论】:

  • subnet(i)&&0xFF for Bytes 困扰着我。字节是有符号的,负数的移位行为很奇怪:-1>>2 == -1。这不会影响比较吗?
  • 我不太明白您关于子网中 255 个项目的说法。为什么不能有更多?
  • 确实,消除 toInt 映射和序列切片带来了巨大的改进。谢谢。
  • 至于子网的最后一个字节,我必须把它转换成:((address(bytes).toInt >> (8-bits) ^ subnet(bytes).toInt >> (8-bits) )) & 0xFF) == 0
  • @Basilevs - 字节已签名,但所有数学都是 Int 数学。所以b &amp; 0xFF 是一个从 0 到 255 的 Int。&amp;&amp; 是一个错字。你想在班次之前做位掩码。
【解决方案2】:

使用此代码,无法正确验证。

例如:

scala> val ip = java.net.InetAddress.getByName("::ffff:1.2.176.0")
ip: java.net.InetAddress = /1.2.176.0

scala> val prefix = new InetPrefix(ip, 20)
prefix: InetPrefix = InetPrefix@6febf6f9

scala> prefix.contains(java.net.InetAddress.getByName("::ffff:1.2.176.20"))
res11: Boolean = true

scala> prefix.contains(java.net.InetAddress.getByName("::ffff:1.2.191.20"))
res12: Boolean = false

但是如果你计算那个网络:(1.2.176.0/20)

$ sipcalc 1.2.176.0/20

-[ipv4 : 1.2.176.0/20] - 0

[CIDR]
Host address        - 1.2.176.0
Host address (decimal)  - 16953344
Host address (hex)  - 102B000
Network address     - 1.2.176.0
Network mask        - 255.255.240.0
Network mask (bits) - 20
Network mask (hex)  - FFFFF000
Broadcast address   - 1.2.191.255
Cisco wildcard      - 0.0.15.255
Addresses in network    - 4096
Network range       - 1.2.176.0 - 1.2.191.255
Usable range        - 1.2.176.1 - 1.2.191.254

-

我在 Scala 中重写了这两个(IPv4 和 IPv6),并将其放在 GitHub 上供所有人使用。它现在也在范围内验证(因此 /20 等将被视为,旧的没有这样做。)

您可以在https://github.com/wasted/scala-util/blob/master/src/main/scala/io/wasted/util/InetPrefix.scala 找到代码(我将其分为 IPv4 和 IPv6)

我还创建了一个blogpost about this

【讨论】:

  • 我从未声称它确实验证正确。我只是向 OP 展示如何让他们的(可能损坏的)代码运行得更快。如果你能解决它,那将是一个很好的答案——否则你的“答案”有点像评论!
  • 我必须编写 2 种检查方式,IPv6 是您的方式,而 IPv4 依赖于将范围的开始/结束计算为 Long,然后比较值。两者都非常直截了当。请随时提交建议。 :)
猜你喜欢
  • 2014-09-13
  • 2012-03-31
  • 1970-01-01
  • 1970-01-01
  • 2016-07-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-11
  • 1970-01-01
相关资源
最近更新 更多