Всегда ли проверка состояния эффективна? - PullRequest
1 голос
/ 25 октября 2011
  • Предположим, что необходимо только привязать значение к определенному датаблок определенного объекта, когда bState имеет значение true. Когда bState ложно, это не обязательно, но это также не мешает.

Какой из следующих фрагментов кода будет более эффективным и почему?

(РЕДАКТИРОВАТЬ: обновлено, состояние теперь является членом объекта)

const int x;     
int i;
int iToBind;
Classname pObject[x];

for (; i < x; ++i) {
 if (pObject[i].bState) {
        pObject[i].somedatamember = iToBind;
    }
}

Versus:

for (; i < x; ++i) {
   pObject[i].somedatamember = iToBind;
}

Ответы [ 5 ]

3 голосов
/ 25 октября 2011

Я бы сказал, что последнее определенно быстрее. Первая версия имеет двунаправленный доступ к памяти, последняя имеет однонаправленный доступ к памяти.

В этой версии:

for (; i < x; ++i) {
  if (pObject[x].bState) {
    pObject[x].somedatamember = iToBind;
  }
}

во время оператора if происходит остановка, поскольку ЦП должен ждать, пока данные будут считаны из памяти. Скорость чтения памяти зависит от того, где находятся данные. Чем дальше от процессора, тем больше времени занимает: L1 (самый быстрый), L2, L3, Ram, диск (самый медленный).

В этой версии:

for (; i < x; ++i) {
  pObject[x].somedatamember = iToBind;
}

есть только записи в память. Пишет в память, не тормозит процессор.

Как и время доступа к памяти, последний цикл не имеет условного перехода внутри цикла. Условные циклы являются значительными накладными расходами, особенно если принятое / не принятое решение является фактически случайным.

1 голос
/ 25 октября 2011

Все зависит от того, что вы упростили для поста. Если вы добавляете ветку только для того, чтобы пропустить установку переменной, то вы, вероятно, ничего не получаете и можете потерять, если предсказание ветки не сработало. Я бы снял тест.

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

1 голос
/ 25 октября 2011

Вы когда-нибудь слышали о Движении с инвариантным кодом цикла ?

Это этап оптимизации от компилятора, который перемещает код из тела циклов, когда это возможно.

Например, с помощью следующего кода:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {
  for (int i = 0; i < argc; ++i) {
    if (argc < 100) {
      printf("%d\n", atoi(argv[1]));
    }
  }
}

Clang генерирует следующий IR:

define i32 @main(i32 %argc, i8** nocapture %argv) nounwind {
  %1 = icmp sgt i32 %argc, 0
  br i1 %1, label %.lr.ph, label %._crit_edge

.lr.ph:                                           ; preds = %0
  %2 = icmp slt i32 %argc, 100
  %3 = getelementptr inbounds i8** %argv, i64 1
  br i1 %2, label %4, label %._crit_edge

; <label>:4                                       ; preds = %4, %.lr.ph
  %i.01.us = phi i32 [ %9, %4 ], [ 0, %.lr.ph ]
  %5 = load i8** %3, align 8, !tbaa !0
  %6 = tail call i64 @strtol(i8* nocapture %5, i8** null, i32 10) nounwind
  %7 = trunc i64 %6 to i32
  %8 = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i64 0, i64 0), i32 %7) nounwind
  %9 = add nsw i32 %i.01.us, 1
  %exitcond = icmp eq i32 %9, %argc
  br i1 %exitcond, label %._crit_edge, label %4

._crit_edge:                                      ; preds = %4, %.lr.ph, %0
  ret i32 0
}

Что можно перевести обратно на C:

int main(int argc, char** argv) {
  if (argc == 0) { return 0; }

  if (argc >= 100) { return 0; }

  for (int i = 0; i < argc; ++i) {
    printf("%d\n", atoi(argv[1]));
  }

  return 0;
}

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

EDIT:

Правка радикально изменила вопрос (боже, я ненавижу это: р) LICM больше не применяется, и эти две функции имеют разные функции.

Вывод, однако, остается идентичным. Одна проверка if в цикле for не меняет фундаментальную сложность вашего кода (помните, что условие цикла также проверяется на каждой итерации ...).

0 голосов
/ 25 октября 2011

Я бы сказал, что это действительно зависит от контекста.Если для bState крайне важно быть истинным во время привязки, то дополнительные 1 или 2 инструкции по сборке за каждую итерацию цикла должны быть оплачены.Если нет, пропустите if, если x особенно велико.

0 голосов
/ 25 октября 2011

Насколько я могу сказать, bState не изменяется в цикле в первом фрагменте, поэтому вы можете поместить if снаружи, что, очевидно, более эффективно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...