Как я могу реорганизовать большой блок операторов if в Java? - PullRequest
2 голосов
/ 25 октября 2010

Недавно я профилировал некоторый код, используя 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 илиЯ смотрю в другом месте, чтобы повысить эффективность?

РЕДАКТИРОВАТЬ: Программа использует эволюционные науки для разработки путей к конкретному результату.В частности, сборка заказов для Starcraft II.Этот метод проверяет, удовлетворяет ли конкретная эволюция условиям данного результата.

Ответы [ 4 ]

4 голосов
/ 25 октября 2010

Во-первых, вы используете & вместо &&, поэтому вы не пользуетесь оценкой короткого замыкания.То есть оператор & будет требовать, чтобы оба условия обеих сторон & были оценены.Если вы действительно выполняете побитовую 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

Если не считать, кажется немного странным, что вы получитеосновное время, затрачиваемое на этот метод, если только эти условия не затрагивают базу данных или что-то в этом роде.

2 голосов
/ 25 октября 2010

Как всегда, лучшее, что нужно сделать, это измерить это самостоятельно.Вы можете использовать этот код с вызовами на 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 выполняет итерацию по элементам вашего List довольно быстро, так что для себя это не так, хотя проблема может заключаться в коде, который он выполняет.Задания и возвраты не требуют времени.

Это оставляет следующие потенциальные горячие точки:

  • Один вызов defaultDestination().
  • Все вызовы EcState.union().
  • Единственный вызов EcState.isSatisfied().

Я бы хотел поспорить, что ваша точка доступа находится в union (), тем более что она собирает все большую и большую коллекцию путевых точек.

Сначала измерьте с помощью nanotime ().

2 голосов
/ 25 октября 2010

Сначала попробуйте переписать последовательность операторов if в один оператор (для каждого ответа @ dcp).

Если это не имеет большого значения, узким местом может быть код waypoints.Некоторые возможности:

  • Вы используете какой-либо тип коллекции, для которой waypoints.size() дорог.
  • waypoints.size() - большое число
  • defaultDestination() -дорогой
  • state.union(...) дорогой
  • state.isSatisfied(...) дорогой

Один быстрый и грязный способ исследовать это - переместить весь этот код вотдельный метод и посмотрите, говорит ли вам профилировщик, что это узкое место.

Если проблема не в этом, то ваша проблема неразрешима, и единственный способ обойти это - найти какой-то умный способ избежать необходимости делатьтак много тестов.

  • Может помочь реорганизация тестового заказа, если есть заказ, который скорее всего вернет false.
  • Если существует значительная вероятность того, что this и c - это один и тот же объект, тогда может помочь начальный тест this == c.
  • Если все ваши объекты EcState сравниваются многократно и они неизменны, то вы могли бы потенциально реализовать hashCode для кэширования его возвратазначение, и используйте hashCode, чтобы ускорить тестирование на равенство.(Это длинный выстрел ... многие вещи должны быть "правильными", чтобы это помогло.)
  • Может быть, вы могли бы использовать hashCode равенство как прокси для равенства ...
1 голос
/ 25 октября 2010

Вы не найдете слишком много способов ускорить процесс. Как уже было сказано, двумя основными из них будет использование преимущества оценки короткого замыкания путем переключения & на &&, а также обеспечение эффективности порядка условий. Например, если есть одно условие, которое отбрасывает 90% возможностей, сначала поместите это условие в метод.

...