2

最近、JVisualVMを使用していくつかのコードのプロファイルを作成しましたが、特定のメソッドの1つが、頻繁に呼び出されることと実行時間が遅いことの両方から、多くの実行時間を費やしていることがわかりました。このメソッドは、次のようにifステートメントの大きなブロックで構成されています:(実際のメソッドには、これらが約30個あります)

    EcState c = candidate;

    if (waypoints.size() > 0)
    {
        EcState state = defaultDestination();
        for (EcState s : waypoints)
        {
            state.union(s);
        }
        state.union(this);
        return state.isSatisfied(candidate);
    }

    if (c.var1 < var1)
        return false;
    if (c.var2 < var2)
        return false;
    if (c.var3 < var3)
        return false;
    if (c.var4 < var4)
        return false;
    if ((!c.var5) & var5)
        return false;
    if ((!c.var6) & var6)
        return false;
    if ((!c.var7) & var7)
        return false;
    if ((!c.var8) & var8)
        return false;
    if ((!c.var9) & var9)
        return false;

    return true;

これらのifステートメントを書くためのより良い方法はありますか、それとも効率を改善するために他の場所を探す必要がありますか?

編集:プログラムは、進化科学を使用して、特定の結果へのパスを開発します。具体的には、StarcraftIIの注文を作成します。このメソッドは、特定の進化が特定の結果の条件を満たすかどうかを確認します。

4

4 に答える 4

4

まず、&&の代わりに&を使用しているため、短絡評価を利用していません。つまり、&演算子では、&の両側の両方の条件を評価する必要があります。本当にビットAND演算を実行している場合、これは適用されませんが、そうでない場合は、以下を参照してください。

条件が満たされない場合にtrueを返すと仮定すると、次のように書き直すことができます(&を&&に変更しました)。

return 
     !(c.var1 < var1 ||
       c.var2 < var2 ||
       c.var3 < var3 ||
       c.var4 < var4 ||
      ((!c.var5) && var5) ||
      ((!c.var6) && var6) ||
      ((!c.var7) && var7) ||
      ((!c.var8) && var8) ||
      ((!c.var9) && var9));

次に、真である可能性が最も高い条件を式チェーンの最上位に移動しようとします。そうすることで、残りの式の評価を節約できます。たとえば、c1.var4 <var4が99%の確率で真である可能性が高い場合は、それを一番上に移動できます。

それを除けば、これらの条件がデータベースなどにぶつからない限り、このメソッドにかなりの時間が費やされるのは少し奇妙に思えます。

于 2010-10-25T00:09:28.977 に答える
2

まず、ステートメントのシーケンスをif1つのステートメントに書き直してみてください(@dcpの回答ごとに)。

それでも大きな違いがない場合は、ボトルネックがwaypointsコードである可能性があります。いくつかの可能性は次のとおりです。

  • waypoints.size()高価なコレクションタイプを使用しています。
  • waypoints.size()多数です
  • defaultDestination()高いです
  • state.union(...)高いです
  • state.isSatisfied(...)高いです

これを調査するための手っ取り早い方法の1つは、そのすべてのコードを別のメソッドに移動し、プロファイラーがボトルネックであると通知するかどうかを確認することです。

それが問題ではない場合、あなたの問題は手に負えないものであり、それを回避する唯一の方法は、それほど多くのテストを行う必要を回避するための賢い方法を見つけることです。

  • falseより早く返される可能性が高い注文がある場合は、テスト注文を並べ替えると役立つ場合があります。
  • thiscが同じオブジェクトである可能性が非常に高い場合は、の初期テストthis == cが役立つ場合があります。
  • すべてのEcStateオブジェクトが繰り返し比較され、それらが不変である場合hashCode、その戻り値をキャッシュするように実装hashCodeし、同等性テストを高速化するために使用できる可能性があります。(これはロングショットです...これを支援するには、多くのことが「正しく」なければなりません。)
  • 多分あなたはhashCode平等の代用として平等を使うことができます...
于 2010-10-25T01:47:41.443 に答える
2

いつものように、最善の方法は自分で測定することです。このコードを呼び出しでインストルメント化しSystem.nanotime()て、非常にきめ細かい期間を取得できます。開始時間を取得してから、メソッドのさまざまな大きなチャンクが実際にかかる時間を計算します。最も遅いチャンクを取得し、さらにnanotime()呼び出しをその中に入れます。あなたが見つけたものも教えてください。それはあなたの質問を読んでいる他の人々に役立つでしょう。

だからここに私のズボンの座席が推測されます...

ifステートメントを最適化しても、測定可能な効果はほとんどありません。これらの比較はすべて非常に高速です。

それで、問題がここにあると仮定しましょう:

if (waypoints.size() > 0)
{
    EcState state = defaultDestination();
    for (EcState s : waypoints)
    {
        state.union(s);
    }
    state.union(this);
    return state.isSatisfied(candidate);
}

ウェイポイントはリストであり、size()メソッドをオーバーライドしていないと思います。この場合、List.size()はインスタンス変数にアクセスしているだけです。したがって、ifステートメントについて心配する必要はありません。

forステートメントはリストの要素を非常に高速に反復するため、for自体はそうではありませんが、問題は実行するコードにある可能性があります。割り当てと返品には時間がかかりません。

これにより、次の潜在的なホットスポットが残ります。

  • への1回の呼び出しdefaultDestination()
  • へのすべての呼び出しEcState.union()
  • への1回の呼び出しEcState.isSatisfied()

あなたのホットスポットがunion()にあることは間違いありません。特に、ある種のウェイポイントのコレクションがどんどん増えているからです。

ただし、最初にnanotime()で測定します。

于 2010-10-25T02:35:19.550 に答える
1

あなたは実際にそれをスピードアップするためにあまり多くの方法を見つけるつもりはありません。&2つの主なものは、すでに述べたように、に切り替えて短絡評価を利用し&&、条件の順序が効率的であることを確認することです。たとえば、可能性の90%を破棄する条件が1つある場合は、その1つの条件をメソッドの最初に配置します。

于 2010-10-25T00:26:49.547 に答える