4

次のコードは、私のプログラムで最もホットなスポットのようです。

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 のケースを個別に処理するという考えは好きではありません。

4

2 に答える 2

3

あなたはそれ自体で何も悪いことをしていません。プリミティブを処理するときのパフォーマンスではなく、使いやすさを目的としたコレクションを使用しているだけです。

これをスピードアップしたい場合は、配列と while ループの使用に切り替えることで最大のブーストが得られます。256 を超えるアイテムを持つサブネットを持つことができるため、IPv6 形式で保存された IPv4 アドレスを除いて、あなたが書いたコードが IPv6 でも機能することは完全には明らかではありません。また、長さをテストすることで、同じアドレスの 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 倍になるはずです。

于 2011-09-05T16:22:35.057 に答える
1

このコードでは、正しく検証されません。

例えば:

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 に分けました) 。

これについてのブログ投稿も作成しました。

于 2012-12-13T01:21:05.497 に答える