netch80 (netch80) wrote,
netch80
netch80

О разуме компиляторов

Исходник:

bool FixMessage::isResendable() const
{
    unsigned type = getType();
    return type != FIX_MT_LOGON && type != FIX_MT_HEARTBEAT &&
           type != FIX_MT_TEST_REQUEST && type != FIX_MT_RESEND_REQUEST &&
           type != FIX_MT_SEQ_RESET && type != FIX_MT_LOGOUT;
}


Выхлоп gcc:

0000000000438570 <_ZNK10FixMessage12isResendableEv>:
  438570:       8b 4f 0c                mov    0xc(%rdi),%ecx
  438573:       b8 01 00 00 00          mov    $0x1,%eax
  438578:       83 e9 30                sub    $0x30,%ecx
  43857b:       83 f9 11                cmp    $0x11,%ecx
  43857e:       77 12                   ja     438592 <_ZNK10FixMessage12isResendableEv+0x22>
  438580:       b8 37 00 02 00          mov    $0x20037,%eax
  438585:       48 d3 e8                shr    %cl,%rax
  438588:       83 e0 01                and    $0x1,%eax
  43858b:       48 83 f0 01             xor    $0x1,%rax
  43858f:       83 e0 01                and    $0x1,%eax
  438592:       f3 c3                   repz retq 


Кто-то догадался, что сократить проверку диапазона близких значений вычитанием базы - отлично.
Беззнаковое сравнение результата вычитания - очень остроумно.
xor с rax, когда достаточно с eax - чего пытаются добиться?
Финальный and с константой - разве из предыдущих команд не ясно, что там останется один бит?

Считаем контрольную сумму:

unsigned checksum(const char *m_begin, const char *m_before_csum)
{
    unsigned csum = 0;
    for (const char *p = m_begin; p != m_before_csum; ++p) {
        csum += *p;
    }
    csum &= 0xff;
    return csum;
}


Генерируем с векторизацией. Не буду приводить всю сумасшедшую простыню, только основной цикл:

        pxor    %xmm0, %xmm0
        addq    %rdi, %r9
        xorl    %edi, %edi
        pxor    %xmm6, %xmm6
        pxor    %xmm4, %xmm4
.L9:
        movdqa  (%r9), %xmm3
        movdqa  %xmm6, %xmm1
        movdqa  %xmm4, %xmm5
        addq    $1, %rdi
        pcmpgtb %xmm3, %xmm1
        movdqa  %xmm3, %xmm2
        addq    $16, %r9
        cmpq    %rdi, %rdx
        punpcklbw       %xmm1, %xmm2
        punpckhbw       %xmm1, %xmm3
        pcmpgtw %xmm2, %xmm5
        movdqa  %xmm2, %xmm1
        punpckhwd       %xmm5, %xmm2
        punpcklwd       %xmm5, %xmm1
        movdqa  %xmm4, %xmm5
        pcmpgtw %xmm3, %xmm5
        paddd   %xmm1, %xmm0
        paddd   %xmm2, %xmm0
        movdqa  %xmm3, %xmm2
        punpcklwd       %xmm5, %xmm2
        paddd   %xmm2, %xmm0
        movdqa  %xmm0, %xmm1
        movdqa  %xmm3, %xmm0
        punpckhwd       %xmm5, %xmm0
        paddd   %xmm1, %xmm0
        ja      .L9


Кто может объяснить, чем нуль в xmm4 принципиально отличается от нуля в xmm6, и почему их нельзя было объединить?

В пропущенной части: до начальной границы 16 байт - короткий простой цикл по байту, но после последней такой границы - цикл развёрнут в 15 отдельных групп со сравнением в каждой на достижение конца входного массива.

Замена char на unsigned char убирает только сравнения (pcmpgt*). Два разных нуля остаются.

За окном шёл снег и два нулевых регистра.

This entry was originally posted at http://netch80.dreamwidth.org/46641.html. Please comment there using OpenID.
Subscribe
Comments for this post were disabled by the author