Обнаруживает ли компилятор переменные, которые рассчитываются более одного раза? - PullRequest
1 голос
/ 24 марта 2020

Мой вопрос касается следующей конструкции:

uint16_t flag = 0x1f;
while (curCase) {
        if ((curCase->i == i) && (curCase->j == j)) flag ^= 0x10;
        if ((curCase->i == i-1) && (curCase->j == j)) flag ^= 0x01;         
        if ((curCase->i == i) && (curCase->j == j+1)) flag ^= 0x02;
        if ((curCase->i == i+1) && (curCase->j == j)) flag ^= 0x04;
        if ((curCase->i == i) && (curCase->j == j-1)) flag ^= 0x08;
        if (!(flag ^ 0xf0)) return; // if none of the neighbors are candidates (ignore first bits)
        curCase = curCase->suiv;
    }

curCase - это просто элемент связанного списка с 2 атрибутами int i и j и следующим элементом suiv;

Зная, что моя программа работает в одном потоке, и поэтому указанные значения не могут измениться во время итерации while;

Мне было интересно, был ли компилятор достаточно умен (и я предполагаю, что ответ - да, но я ' хотелось бы быть уверенным) обнаружить, что код требует для вычисления указателей curCase-> i и curCase-> j 5 раз, когда я мог просто объявить 2 временных указателя и присвоить curCase-> i и curCase-> j в начале цикла;

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

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

Ответы [ 2 ]

2 голосов
/ 24 марта 2020

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

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

https://godbolt.org/z/B_maur

2 голосов
/ 24 марта 2020

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

Чтобы проверить гипотезы, я немного адаптировал ваш код для использования Compile Explorer следующим образом:

struct cur { int i; int j; void * suiv;} typedef cur;
int i;
int j;

void function(cur *curCase) {
    int flag = 0x1f;
    while (curCase) {
        if ((curCase->i == i) && (curCase->j == j)) flag ^= 0x10;
        if ((curCase->i == i-1) && (curCase->j == j)) flag ^= 0x01;         
        if ((curCase->i == i) && (curCase->j == j+1)) flag ^= 0x02;
        if ((curCase->i == i+1) && (curCase->j == j)) flag ^= 0x04;
        if ((curCase->i == i) && (curCase->j == j-1)) flag ^= 0x08;
        if (!(flag ^ 0xf0)) return; // if none of the neighbors are candidates (ignore first bits)
        curCase = curCase->suiv;
    }
}

И похоже, что ассемблерный код для g cc и CLang не изолируют упомянутые вами инструкции.

Анализ кода можно найти здесь: https://godbolt.org/z/afQxxd

...