>
>
>
Об оптимизациях

Владимир Татарчевский
Статей: 1

Об оптимизациях

Проверка рекомендации "не берите на себя работу компилятора" из книги "Главный вопрос программирования, рефакторинга и всего такого".

Примечание. Статья впервые была опубликована на русском языке на сайте livejournal.com. Статья и её перевод размещаются на нашем сайте с согласия автора.

В блоге PVS-Studio можно найти много примеров ошибок, совершаемых программистами, и много рекомендаций о том, как их избежать (http://www.viva64.com/ru/b/0391/).

Первый совет в этом документе: не берите на себя работу компилятора. Не оптимизируйте вручную то, что и так будет оптимизировано. В качестве примера приведён код из MySQL, в котором программист вручную развернул цикл, допустив при этом ошибку. Далее в статье написано, что если написать эту же функцию через цикл, то компилятор, скорее всего, её развернёт.Давайте проверим это с помощью онлайн-компилятора. Я скопировал исходники функции из статьи, лишь немного подправив их так, чтобы они компилировались без дополнительных файлов.Компиляция выполнялась компилятором clang 3.9 для таргета arm с уровнем оптимизации -O2 (при котором компилятор разворачивает циклы).

Итак, оригинальный исходник:

int rr_cmp(unsigned char *a, unsigned char *b)
{
  if (a[0] != b[0])
    return (int) a[0] - (int) b[0];
  if (a[1] != b[1])
    return (int) a[1] - (int) b[1];
  if (a[2] != b[2])
    return (int) a[2] - (int) b[2];
  if (a[3] != b[3])
    return (int) a[3] - (int) b[3];
  if (a[4] != b[4])
    return (int) a[4] - (int) b[4];
  if (a[5] != b[5])
    return (int) a[5] - (int) b[5];
  if (a[6] != b[6])
    return (int) a[6] - (int) b[6];
  return (int) a[7] - (int) b[7];
}

Результат для arm:

rr_cmp(unsigned char*, unsigned char*):
@ @rr_cmp(unsigned char*, unsigned char*)
        ldrb    r2, [r1]
        ldrb    r3, [r0]
        cmp     r3, r2
        bne     .LBB0_7
        ldrb    r2, [r1, #1]
        ldrb    r3, [r0, #1]
        cmp     r3, r2
        bne     .LBB0_7
        ldrb    r2, [r1, #2]
        ldrb    r3, [r0, #2]
        cmp     r3, r2
        bne     .LBB0_7
        ldrb    r2, [r1, #3]
        ldrb    r3, [r0, #3]
        cmp     r3, r2
        bne     .LBB0_7
        ldrb    r2, [r1, #4]
        ldrb    r3, [r0, #4]
        cmp     r3, r2
        bne     .LBB0_7
        ldrb    r2, [r1, #5]
        ldrb    r3, [r0, #5]
        cmp     r3, r2
        bne     .LBB0_7
        ldrb    r2, [r1, #6]
        ldrb    r3, [r0, #6]
        cmp     r3, r2
        ldrbeq  r1, [r1, #7]
        ldrbeq  r0, [r0, #7]
        subeq   r0, r0, r1
        bxeq    lr
.LBB0_7:
        sub     r0, r3, r2
        bx      lr

Исходник, предлагаемый автором из PVS:

int rr_cmp(unsigned char *a,unsigned char *b)
{
  for (int i = 0; i < 7; ++i)
  {
    if (a[i] != b[i])
      return a[i] - b[i]; 
  }
  return a[7] - b[7];
}

Результат:

rr_cmp(unsigned char*, unsigned char*):
@ @rr_cmp(unsigned char*, unsigned char*)
        ldrb    r2, [r1]
        ldrb    r3, [r0]
        cmp     r3, r2
        bne     .LBB0_7
        ldrb    r2, [r1, #1]
        ldrb    r3, [r0, #1]
        cmp     r3, r2
        bne     .LBB0_7
        ldrb    r2, [r1, #2]
        ldrb    r3, [r0, #2]
        cmp     r3, r2
        bne     .LBB0_7
        ldrb    r2, [r1, #3]
        ldrb    r3, [r0, #3]
        cmp     r3, r2
        bne     .LBB0_7
        ldrb    r2, [r1, #4]
        ldrb    r3, [r0, #4]
        cmp     r3, r2
        bne     .LBB0_7
        ldrb    r2, [r1, #5]
        ldrb    r3, [r0, #5]
        cmp     r3, r2
        bne     .LBB0_7
        ldrb    r2, [r1, #6]
        ldrb    r3, [r0, #6]
        cmp     r3, r2
        ldrbeq  r1, [r1, #7]
        ldrbeq  r0, [r0, #7]
        subeq   r0, r0, r1
        bxeq    lr
.LBB0_7:
        sub     r0, r3, r2
        bx      lr

Как говорится, найдите 10 отличий.

Всё идентично.

Однако, для x86-64 результат будет несколько иной, как ни странно. Но это уже другая история.