Производительность для сложного логического значения против маловероятного / вероятного - PullRequest
0 голосов
/ 01 декабря 2018

Делает ли это быстрее?:

return m_bVisible && (_colorTransform.alphaTransform > 0 || _colorTransform.colorOffsetTransform.a > 0) && (!_masked || _maskVisitable);

Чем делает это?в этом я не уверенЯ в порядке с увеличением производительности на 0,01%, так как после выполнения 100 из этих оптимизаций мне удалось значительно повысить производительность (30-40%).Отвечая на вопрос об использовании НЕПРАВИЛЬНОЙ оптимизации по сравнению только с составным логическим значением.Короткое замыкание не очень легко сделать в этом конкретном выражении.

1 Ответ

0 голосов
/ 01 декабря 2018

Я бы сказал, что первое быстрее!

Даже если вы «оптимизируете» предсказание ветвления для второго, это просто означает, что return m_bVisible; появится в коде ASM раньше, чем вашreturn false, вам все равно придется выполнить до четырех сравнений в ваших операторах if.

Однако, поскольку, как вы говорите, они очень НЕВЕРНЫЕ, нет необходимости делать это, прежде чем взглянуть на m_bVisible.В первом примере сначала проверяется m_bVisible, который, похоже, не смещен ни в ЛОЖЬ, ни в ИСТИНА.

Я догадываюсь, что для всех случаев, когда m_bVisible == false, первый будет быстрее.Во всех других случаях не будет большой разницы, за исключением минимальных издержек JMP во втором примере (которые требуют, чтобы вы перепрыгнули через return m_bVisible;, чтобы перейти к фактическому return false).

EDIT

Давайте более подробно рассмотрим сборки x86_64 (gcc 8) обоих вариантов:

Тестовый код:

#include <stdio.h>

bool a=true; int b=0; int c=0; bool d=false; bool e=true;
#define likely(x)       __builtin_expect((x),1)
#define unlikely(x)     __builtin_expect((x),0)

int main()
{
marker1:
    return a && (b > 0 || c > 0) && (!d || e);
}


int alternative()
{
    if (unlikely(b == 0 && c == 0))
    {
        return false;
    }

    if (unlikely(d && !e))
    {
        return false;
    }

    return a;
}

X86_86 Сборка (GCC 8.2) для обеих функций:

main ()

main:
    push    rbp
    mov     rbp, rsp
    movzx   eax, BYTE PTR a[rip]
    test    al, al
    je      .L3
    mov     eax, DWORD PTR b[rip]
    test    eax, eax
    jg      .L4
    mov     eax, DWORD PTR c[rip]
    test    eax, eax
    jle     .L3
.L4:
    movzx   eax, BYTE PTR d[rip]
    xor     eax, 1
    test    al, al
    jne     .L5
    movzx   eax, BYTE PTR e[rip]
    test    al, al
    je      .L3
.L5:
    mov     eax, 1
    jmp     .L6
.L3:
    mov     eax, 0
.L6:
    movzx   eax, al
    pop     rbp
    ret

alternative ()

alternative():
    push    rbp
    mov     rbp, rsp
    mov     eax, DWORD PTR b[rip]
    test    eax, eax
    sete    al
    movzx   eax, al
    test    rax, rax
    je      .L9
    mov     eax, DWORD PTR c[rip]
    test    eax, eax
    sete    al
    movzx   eax, al
    test    rax, rax
    je      .L9
    mov     eax, 0
    jmp     .L10
.L9:
    movzx   eax, BYTE PTR d[rip]
    movzx   eax, al
    test    rax, rax
    je      .L11
    movzx   eax, BYTE PTR e[rip]
    xor     eax, 1
    movzx   eax, al
    test    rax, rax
    je      .L11
    mov     eax, 0
    jmp     .L10
.L11:
    movzx   eax, BYTE PTR a[rip]
    movzx   eax, al
.L10:
    pop     rbp
    ret

В первом варианте, в main (), самый быстрый выход находится на пятой строке,je .L3.Во втором варианте самый быстрый выход происходит намного позже ... в .L9 в je .L11

EDIT2

Просто, чтобы завершить, вот сборка для -O3

main:
        xor     eax, eax
        cmp     BYTE PTR a[rip], 0
        je      .L1
        cmp     DWORD PTR b[rip], 0
        jle     .L10
.L4:
        cmp     BYTE PTR d[rip], 0
        mov     eax, 1
        je      .L1
        movzx   eax, BYTE PTR e[rip]
.L1:
        ret
.L10:
        cmp     DWORD PTR c[rip], 0
        jg      .L4
        ret

И

alternative():
        mov     eax, DWORD PTR b[rip]
        or      eax, DWORD PTR c[rip]
        je      .L11
        cmp     BYTE PTR d[rip], 0
        jne     .L18
.L13:
        movzx   eax, BYTE PTR a[rip]
        ret
.L18:
        cmp     BYTE PTR e[rip], 0
        jne     .L13
        xor     eax, eax
.L11:
        ret

Даже при намотке первый, кажется, быстрее при O3!

...