Избегайте проверки того же условия каждый шаг в цикле в C ++ - PullRequest
3 голосов
/ 24 июня 2019

Я проверяю условие внутри цикла и, если оно выполняется, что-то предпринимаю.

for (i = 0; i < n; i++)
{
    // do lots of work here
    .
    .
    .
    if (constant_condition)
        do_something(n);
}

Условие не зависит от n, поэтому необходимо каждый раз проверять его. Я мог бы вместо этого сделать это:

if (constant_condition)
    for (i = 0; i < n; i++)
    {
        // do lots of work here
        .
        .
        .

        do_something(n);
    }
else
    for (i = 0; i < n; i++)
    {
        // do lots of work here
        .
        .
        .    
    }

Этот новый код более эффективен, но мне пришлось скопировать и вставить тот же код в мою программу. Есть ли эффективный способ сделать это без повторения одного и того же блока кода?

Редактировать: условие не известно во время компиляции, но оно будет дано во время выполнения и не изменится.

1 Ответ

7 голосов
/ 24 июня 2019

Прежде всего, профиль, чтобы увидеть, если это имеет значение. Если это так, у вас есть несколько вариантов:

  • Кэшировать константу вне цикла, если компилятор еще не сделал этого. Это самое простое и в большинстве случаев достаточно:

    const bool constant_condition = ...;
    for (...) {
       ...
       if (constant_condition) do_something(...);
    }
    
  • Если вам действительно нужно избежать ветвления, типичным подходом является определение вспомогательной функции или локальной лямбды (C ++ 11) для выделения общих блоков кода. Тем не менее, это по-прежнему дублирует код и, в зависимости от случая, может выглядеть совсем не так:

    auto main_work = [...](...) { ... };
    if (constant_condition)
        for (...) { main_work(...); }
    else
        for (...) { main_work(...); do_something(...); }
    
  • Определение шаблона и параметризация по мере необходимости. Компилятор обычно оптимизирует должным образом, поэтому вы можете просто скопировать и вставить код. Если вы действительно хотите убедиться, что ветвь удалена, вы можете заставить ее специализироваться на шаблоне или воспользоваться if constexpr (C ++ 17) и т. Д. Однако остерегайтесь раздувания кода и времени компиляции.

    template <bool constant_condition>
    void f(...) { ... }
    
    if (constant_condition)
        f<true>(...);
    else
        f<false>(...);
    

Наконец, не забудьте снова заняться профилем. Иногда удаление ветки может выглядеть хорошо, но в целом может быть вредным. Это особенно верно, если код сильно меняется, и то, что первоначально выглядело как небольшое дублирование инструкций, теперь на нескольких страницах памяти полно дублированного кода.

Другая альтернатива - попытаться выяснить, можно ли вместо этого написать алгоритм / код без ветвей; однако это не общее решение.

...