Могут ли массивы указателей функций использоваться для удаления ветвей - PullRequest
0 голосов
/ 07 сентября 2018

Могут ли массивы указателей на функции повысить производительность, удалив ветви?

Есть

(function[number])();

быстрее, чем

if(number == 0)
    function1();
else if (number == 1)
    function2();

за производительность? (при условии, что это был чрезвычайно критичный для производительности код)

РЕДАКТИРОВАТЬ: число 0 или 1 (иначе используется, потому что это должно быть только 0 или 1)

Ответы [ 3 ]

0 голосов
/ 07 сентября 2018

вряд ли поможет. Указатели на функции вызывают такие же задержки *, как и условия. В настоящее время оба имеют предикторы, но условные предикторы имеют лучшие показатели успеха. Не удивительно, поскольку им нужно предсказать только один бит.

* Задержка: задержка в обработке команд, вызванная конвейерным характером декодера команд в современных процессорах. Декодирование команд начинается за несколько циклов до фактического выполнения, но имеет несколько умозрительный характер относительно того, какая команда будет выполняться в будущем. Когда это предположение неверно, необходимо отбросить несколько декодированных инструкций, и выполнение инструкций задерживается до тех пор, пока декодер не догонит.

0 голосов
/ 07 сентября 2018

Как MSalters уже очень жестко заявил, это вряд ли поможет. Кроме того, почти невозможно предсказать, поможет ли это. Я создал для вас два примера, в которых вы можете это видеть - один с использованием массива указателей, а второй - с условием if-else.

Пример 1

#include <array>
#include <functional>



int test1(int* i)
{
    return *i * *i  * *i;
}

int test2(int* i)
{
    return *i * *i* *i* *i* *i* *i;
}

std::array<int (*)(int*), 2> funcs{test1, test2};

int testing(int v)
{
    int testme = v + 4 ;
    return funcs[v](&testme);
}

со сборкой

test1(int*):                             # @test1(int*)
    movl    (%rdi), %ecx
    movl    %ecx, %eax
    imull   %ecx, %eax
    imull   %ecx, %eax
    retq
test2(int*):                             # @test2(int*)
    movl    (%rdi), %ecx
    movl    %ecx, %eax
    imull   %ecx, %eax
    imull   %ecx, %eax
    imull   %eax, %eax
    retq
testing(int):                            # @testing(int)
    pushq   %rax
    leal    4(%rdi), %eax
    movl    %eax, 4(%rsp)
    movslq  %edi, %rax
    leaq    4(%rsp), %rdi
    callq   *funcs(,%rax,8)
    popq    %rcx
    retq
funcs:
    .quad   test1(int*)
    .quad   test2(int*)

Вот Пример2

int test1(int* i)
{
    return *i * *i  * *i;
}

int test2(int* i)
{
    return *i * *i* *i* *i* *i* *i;
}


int testing(int v)
{
    int testme = v + 4 ;
    return (v == 0) ? test1(&testme) : test2(&testme);
}

с соответствующей сборкой

test1(int*):                             # @test1(int*)
    movl    (%rdi), %ecx
    movl    %ecx, %eax
    imull   %ecx, %eax
    imull   %ecx, %eax
    retq
test2(int*):                             # @test2(int*)
    movl    (%rdi), %ecx
    movl    %ecx, %eax
    imull   %ecx, %eax
    imull   %ecx, %eax
    imull   %eax, %eax
    retq
testing(int):                            # @testing(int)
    leal    4(%rdi), %eax
    movl    %eax, %ecx
    imull   %eax, %ecx
    imull   %eax, %ecx
    testl   %edi, %edi
    movl    $1, %eax
    cmovnel %ecx, %eax
    imull   %ecx, %eax
    retq

Подсчет инструкций в том смысле, что они очень похожи (может быть, эксперт может помочь нам здесь и объяснить это далее). Оператор if-else не соответствует этой инструкции cmovnel. Я сомневаюсь, что это будет иметь большое значение.

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

Здесь не будет возможного истинного или ложного ответа .

0 голосов
/ 07 сентября 2018

Предполагая, что ваш number имеет тип unsigned int, ваш массив должен содержать UINT_MAX записей со всеми одинаковыми, кроме function[0]. Если это не так, ваш первый код (function[number])(); пропускает условие, чтобы не быть неопределенным поведением: (function[number != 0])();

Подводя итог: # 1 (правильно написано) условие и две косвенные. Другой вариант гигантского массива уничтожит локальность кэша. # 2 - это одно условие и прямой вызов функции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...