Что быстрее, switch или if else?

Я пишу функцию для перемещения угла прямоугольника. Будет ли разница между этими двумя вариациями по скорости?

template<rect_corner Corner>
void move_corner(const coords2<value_type>& coords) noexcept {
    if constexpr (Corner == rect_corner::left_top) {
        left = coords.x;
        top = coords.y;
        return;
    }
    if constexpr (Corner == rect_corner::left_bottom) {
        left = coords.x;
        bottom = coords.y;
        return;
    }
    if constexpr (Corner == rect_corner::right_top) {
        right = coords.x;
        top = coords.y;
        return;
    }
    if constexpr (Corner == rect_corner::right_bottom) {
        right = coords.x;
        bottom = coords.y;
        return;
    }
}

void move_corner(const rect_corner corner, const coords2<value_type>& coords) noexcept {
    switch (corner) {
    case rect_corner::left_top:
        left = coords.x;
        top = coords.y;
        break;
    case rect_corner::left_bottom:
        left = coords.x;
        bottom = coords.y;
        break;
    case rect_corner::right_top:
        right = coords.x;
        top = coords.y;
        break;
    default: // corner == right_bottom.
        right = coords.x;
        bottom = coords.y;
        break;
    }
}

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


Ответы (1 шт):

Автор решения: James

Поток управления в инструкциях if-elseif-else является линейным: вычисля­ется условие if и, если оно истинно, выполняется первый блок. В противном случае продолжается вычисление условий каждого else if и выполнение первого же блока, условие которого имеет значение t rue. Проверка переменной на равенство для каж­дого из п значений приводит к последовательности if-then-elseifс п блоками. Если все возможные значения равновероятны, такая последовательность if-then-e lseif выполняет О(n) сравнений. При очень частом выполнении (например, при диспетче­ризации событий) стоимость такого кода возрастает многократно.

Инструкция switch также сравнивает переменную с каждым из n значений, но оператор switch, сравнивающий значение с рядом констант, позволяет компилятору выполнять ряд полезных оптимизаций.

В часто встречающемся случае, когда требуется проверка на равенство константам, взятым из множества последовательных или почти последовательных значений, инструкция switch компилируется в таблицу переходов, индексируемую тестиру­емым значением или выражением, производным от него. И инструкция switch вы­полняет одну операцию индексации и переходит по указанному в таблице адресу. Стоимость сравнения равна 0(1), независимо от количества констант для проверки. Такие переходы в таблице не обязаны находиться в последовательном порядке; ком­пилятор может по-своему сортировать таблицу переходов. Когда константы образуют последовательность с большими разрывами, таблица переходов становится слишком большой. Компилятор по-прежнему может отсортировать тестируемые константы и сгенерировать код, который выполняет бинарный поиск. Для инструкции switch, выполняющей сравнение с n значениями, наихудшая стоимость поиска составляет O(log2n). В любом случае компилятор никогда не сге­нерирует для инструкции switch код, более медленный, чем для эквивалентной кон­струкции if-else.

Иногда вероятность одной ветви if-elseif-else оказывается гораздо выше про­чих. В этом случае амортизированная производительность инструкции if может приближаться к константе, если первым проверяется самый вероятный случай.

Отрывок из книги "Оптимизация программ на C++. Проверенные методы повышения производительности | Курт Гантерот"

→ Ссылка