множественный доступ к битовым полям без изменения значения, производительности? - PullRequest
2 голосов
/ 15 марта 2019

дана структура с битовыми полями, например:

struct
{   
    long f0: 6;
    long f1: 6;
    long f2: 2;
    long f3: 2;
} S;

в случае разницы в производительности между следующими кодами: 1-

const int f0=S.f0,f1=S.f1,....;
//multiple calculations that use f0,f1,...., for example
int x=f0*f0;

по сравнению с 2- * +1007 *

//multiple calculations that use S.f0,S.f1,... directly, for example
x=S.f0*S.f0

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

1 Ответ

0 голосов
/ 16 марта 2019

Ожидайте высочайшую компетентность от вашего оптимизатора

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

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

Практический эксперимент:

Возьмем последовательность:

x=S.f0*S.f0;
y=S.f0*S.f1;

(соответствующая часть) код, сгенерированный с помощью GCC 8.3:

  1)  movzx   eax, BYTE PTR S[rip]     ; load 8 bit of data data and make it 16 bits
  2)  sal     eax, 2                   ;  get rid of the 2 bytes to keep only the 6
  3)  sar     al, 2
  4)  movsx   edx, al                  ; clone the value in second register
  5)  mov     eax, edx
  6)  imul    eax, edx
  7)  mov     DWORD PTR x[rip], eax

  8)  movzx   eax, WORD PTR S[rip]     ; load 16 bits
  9)  sal     eax, 4                   ; get rid of 4 bits (so remain 12)
 10)  sar     ax, 10                   ; get rid of 10 bits on other side (so remain 6 next)
 11)  movsx   eax, al
 12)  imul    eax, edx                 ; reuse register previously loaded
 13)  mov     DWORD PTR y[rip], eax

Теперь для другой альтернативы:

const int f0=S.f0,f1=S.f1;
x=f0*f0;
y=f0*f1;

вы получите следующий код :

  1)  movzx   eax, BYTE PTR S[rip]
  8)  movzx   edx, WORD PTR S[rip]   ; but in another register
  2)  sal     eax, 2
  9)  sal     edx, 4                 ; but other register
  3)  sar     al, 2
 10)  sar     dx, 10                 ; but other register
  4)  movsx   eax, al
 11)  movsx   edx, dl                ; but other register
  5)  mov     ecx, eax               ; but other register
  6)  imul    ecx, eax               ; but other register
 12)  imul    eax, edx
  7)  mov     DWORD PTR x[rip], ecx  ; but other register
 13)  mov     DWORD PTR y[rip], eax

Дополнительные эксперименты показывают, что этоТо же самое для оптимизации цикла: компилятор может извлечь код доступа к битовому полю из цикла и повторно использовать извлеченные значения несколько раз.

Подсказка: ради тестов, учитывая возможности оптимизатора, вам нужно немного обмануть, чтобы ваш тестовый код не работалОптимизировать.Для этого я сделал x и y volatiles (чтобы они записывались в событие, если значение никогда не используется).Я также инициализировал структуру с несуществующей внешней функцией, чтобы компилятор не мог выполнять постоянное распространение.

Конечно, это верно только в том случае, если компилятор уверен, что S не меняется между последовательными выражениями.Если компилятор не может предположить, что (например, вы вызываете функцию, используя ссылку или указатель на S), ему придется каждый раз перезагружать S.fx, делая его медленнее, чем первоначальная загрузка, в константное значение.

...