2

我最近使用 JVisualVM 分析了一些代码,发现一个特定的方法占用了大量的执行时间,包括经常调用和执行时间缓慢。该方法由一大块 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 语句,或者我应该寻找其他地方来提高效率?

编辑:该计划使用进化科学来开发达到给定结果的途径。具体来说,为星际争霸 II 建立订单。此方法检查特定进化是否满足给定结果的条件。

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

首先,尝试将语句序列重写if为一个语句(根据@dcp 的回答)。

如果这没有太大区别,那么瓶颈可能是waypoints代码。一些可能性是:

  • waypoints.size()您正在使用一些昂贵的集合类型。
  • waypoints.size()是一个很大的数字
  • defaultDestination()太贵了
  • state.union(...)太贵了
  • state.isSatisfied(...)太贵了

一种快速而简单的调查方法是将所有代码移到一个单独的方法中,并查看分析器是否告诉您这是一个瓶颈。

如果这不是问题,那么您的问题是棘手的,解决它的唯一方法是找到一些聪明的方法来避免必须进行如此多的测试。

  • 如果有可能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);
}

我猜 waypoints 是一个 List 并且你没有覆盖 size() 方法。在这种情况下, List.size() 只是访问一个实例变量。所以不用担心 if 语句。

for 语句迭代 List 的元素非常快,所以 for 本身不是它,尽管问题很可能是它执行的代码。分配和返回不需要时间。

这留下了以下潜在热点:

  • 的一个呼叫defaultDestination()
  • 所有对EcState.union().
  • 的一个呼叫EcState.isSatisfied()

我愿意打赌你的热点在 union() 中,特别是因为它正在建立某种越来越大的航点集合。

不过先用 nanotime() 测量。

于 2010-10-25T02:35:19.550 回答
1

您不会找到太多方法来实际加快速度。正如已经说过的,两个主要的将利用短路评估,通过切换&&&,并确保条件的顺序是有效的。例如,如果有一个条件抛弃了 90% 的可能性,则将该条件放在方法中的首位。

于 2010-10-25T00:26:49.547 回答