GCC 4.4: Избежать проверки принадлежности к диапазону на переключателе/операторе выбора в gcc?

Это - только проблема о версиях GCC до 4,4, это было зафиксировано в GCC 4.5.

Действительно ли возможно сказать компилятору, что переменная, используемая в переключателе, соответствует в обеспеченных операторах выбора? В особенности, если это - маленький диапазон и существует сгенерированная таблица переходов.

extern int a;
main()
{
        switch (a & 0x7) {   // 0x7  == 111  values are 0-7
        case 0: f0(); break;
        case 1: f1(); break;
        case 2: f2(); break;
        case 3: f3(); break;
        case 4: f4(); break;
        case 5: f5(); break;
        case 6: f6(); break;
        case 7: f7(); break;
        }
}

Я попробовал xor'ing к младшим битам (как пример), с помощью перечислений, с помощью gcc_unreachable () напрасно. Сгенерированный код всегда проверяет, ли переменная в диапазоне, добавляя бессмысленное условное выражение ответвления и отодвигая код вычисления таблицы переходов.

Примечание: это находится в самом внутреннем цикле декодера, производительность значительно имеет значение.

Кажется, что я не только один.

Нет никакого способа сказать gcc, что ответвление по умолчанию никогда не берется, хотя это опустит ответвление по умолчанию, если может оказаться, что значение никогда не вне диапазона на основе более ранних условных проверок.

Так, как Вы помогаете gcc доказать переменные соответствия и нет никакого ответвления по умолчанию в примере выше? (Не добавляя условный переход, конечно.)

Обновления

  1. Это было на OS X 10,6 Snow Leopard с GCC 4.2 (значение по умолчанию от XCode.) Этого не произошло с GCC 4.4/4.3 в Linux (сообщаемый Nathon и Jens Gustedt.)

  2. Функции в примере там для удобочитаемости, думайте, что они встраиваются или просто операторы. Создание вызова функции на x86 является дорогим.

    Также пример, как упомянуто в примечании, принадлежит в цикле на данных (большие данные.)

    Сгенерированный код с gcc 4.2/OS X:

    [...]
    andl    $7, %eax
    cmpl    $7, %eax
    ja  L11
    mov %eax, %eax
    leaq    L20(%rip), %rdx
    movslq  (%rdx,%rax,4),%rax
    addq    %rdx, %rax
    jmp *%rax
    .align 2,0x90
    L20:
    .long   L12-L20
    .long   L13-L20
    .long   L14-L20
    .long   L15-L20
    .long   L16-L20
    .long   L17-L20
    .long   L18-L20
    .long   L19-L20
    L19:
    [...]
    

    Проблема заключается на cmp $7, %eax; ja L11;

  3. Хорошо, я иду с ужасным решением и добавляю особый случай для gcc версий ниже 4,4 использований другой версии без переключателя и использования goto и &&label расширений gcc.

    static void *jtb[] = { &&c_1, &&c_2, &&c_3, &&c_4, &&c_5, &&c_6, &&c_7, &&c_8 };
    [...]
    goto *jtb[a & 0x7];
    [...]
    while(0) {
    c_1:
    // something
    break;
    c_2:
    // something
    break;
    [...]
    }
    

    Обратите внимание, что массив маркировок статичен, таким образом, он не вычислил каждый вызов.

15
задан Evan Carroll 8 May 2018 в 21:11
поделиться

5 ответов

Я попробовал скомпилировать что-то простое и сравнимое с -O5 и -fno-inline (мои функции f0-f7 были тривиальными), и он выдал следующее:


 8048420:   55                      push   %ebp ;; function preamble
 8048421:   89 e5                   mov    %esp,%ebp ;; Yeah, yeah, it's a function.
 8048423:   83 ec 04                sub    $0x4,%esp ;; do stuff with the stack
 8048426:   8b 45 08                mov    0x8(%ebp),%eax ;; x86 sucks, we get it
 8048429:   83 e0 07                and    $0x7,%eax ;; Do the (a & 0x7)
 804842c:   ff 24 85 a0 85 04 08    jmp    *0x80485a0(,%eax,4) ;; Jump table!
 8048433:   90                      nop
 8048434:   8d 74 26 00             lea    0x0(%esi,%eiz,1),%esi
 8048438:   8d 45 08                lea    0x8(%ebp),%eax
 804843b:   89 04 24                mov    %eax,(%esp)
 804843e:   e8 bd ff ff ff          call   8048400 
 8048443:   8b 45 08                mov    0x8(%ebp),%eax
 8048446:   c9                      leave  

Вы пробовали играть с уровнями оптимизации?

1
ответ дан 1 December 2019 в 04:52
поделиться

Вы пробовали объявить переменную переключателя как битовое поле?

struct Container {
  uint16_t a:3;
  uint16_t unused:13;
};

struct Container cont;

cont.a = 5;  /* assign some value */
switch( cont.a ) {
...
}

Надеюсь, это сработает!

2
ответ дан 1 December 2019 в 04:52
поделиться

Возможно, вы могли бы использовать массив указателей функций вместо переключателя?

#include <stdio.h>

typedef void (*func)(void);

static void f0(void) { printf("%s\n", __FUNCTION__); }
static void f1(void) { printf("%s\n", __FUNCTION__); }
static void f2(void) { printf("%s\n", __FUNCTION__); }
static void f3(void) { printf("%s\n", __FUNCTION__); }
static void f4(void) { printf("%s\n", __FUNCTION__); }
static void f5(void) { printf("%s\n", __FUNCTION__); }
static void f6(void) { printf("%s\n", __FUNCTION__); }
static void f7(void) { printf("%s\n", __FUNCTION__); }

int main(void)
{
    const func f[8] = { f0, f1, f2, f3, f4, f5, f6, f7 };
    int i;

    for (i = 0; i < 8; ++i)
    {
        f[i]();
    }
    return 0;
}
5
ответ дан 1 December 2019 в 04:52
поделиться

Может быть, просто использовать метку по умолчанию для первого или последнего случая?

1
ответ дан 1 December 2019 в 04:52
поделиться

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

При этом я должен признать, что я весьма скептически отношусь к тому, что эта дополнительная инструкция когда-либо приведет к ощутимой разнице в производительности на практике, особенно на новом mac. Если у вас есть сколько-нибудь значительный объем данных, вы будете связаны вводом-выводом, и одна инструкция никогда не станет вашим узким местом. Если у вас крошечный объем данных, то вам придется выполнить много-много-много вычислений, прежде чем одна инструкция станет узким местом.

Не могли бы вы опубликовать какой-нибудь код, чтобы показать, что разница в производительности действительно есть? Или опишите код и данные, с которыми вы работаете?

1
ответ дан 1 December 2019 в 04:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: