gcc / clang использование ключевого слова restrict для локальных переменных и структурных полей - PullRequest
7 голосов
/ 10 марта 2020

Не могу понять, как убедить gcc / clang, что мои указатели не пересекаются; для того, что я вижу, похоже, что restrict учитывается только если указано в аргументах функции и игнорируется в противном случае. Вот мой код:

#if defined(_MSC_VER) || defined(__cplusplus)
#define restrict __restrict
#endif

struct s {
    int sz;
    int *a;
    int *b;
};

struct s_r {
    int sz;
    int *restrict a;
    int *restrict b;
};

void foo_dumb_struct(struct s *s, int c) {
    int sz = s->sz;
    for(int i = 0; i != sz; ++i) {
        s->a[i] = s->b[0] + c;
    }
}

void foo_restricted_arrays(int sz,
        int *restrict a, int *restrict b,
        int c) {
    for(int i = 0; i != sz; ++i) {
        a[i] = b[0] + c;
    }
}

void foo_restricted_struct(struct s_r *s, int c) {
    int sz = s->sz;
    for(int i = 0; i != sz; ++i) {
        s->a[i] = s->b[0] + c;
    }
}

void foo_restricted_subcall(struct s *s, int c) {
    foo_restricted_arrays(s->sz, s->a, s->b, c);
}

void foo_restricted_cast(struct s *s, int c) {
    int sz = s->sz;
    int *restrict a = s->a;
    int *restrict b = s->b;
    for(int i = 0; i != sz; ++i) {
        a[i] = b[0] + c;
    }
}

I cc подходит для этого кода, но gcc / clang генерирует повторное чтение b[0] на каждой итерации для foo_restricted_struct и foo_restricted_cast для всех Архитектуры, которые я мог проверить с помощью Godbolt. Каждый раз, когда он используется в аргументах функций (включая вложенные функции или лямбда-выражения C ++), все в порядке, и дополнительная нагрузка снимается. https://cellperformance.beyond3d.com/articles/2006/05/demystifying-the-restrict-keyword.html предполагает, что на самом деле это работает так, как я хочу, но я не уверен, что их g cc не был специально настроен для ячейки.

Использует ли я ограничение неправильно, или gcc / clang реализует только ограничение для аргументов функции и ничего больше?

Ответы [ 2 ]

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

для того, что я вижу, похоже, что restrict учитывается только в том случае, если указано в аргументах функции

Эта характеристика звучит так, как вы думаете, restrict квалификация несет в себе какую-то обязательство оптимизировать более агрессивно. Это явно не так:

Переводчик вправе игнорировать любые или все намеки на последствия использования restrict.

(C стандарт, пункт 6.7. 3.1 / 6)

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

Является ли мое использование restrict неправильным, или gcc / clang реализует ограничение только для аргументов функции и ничего больше?

Несмотря на то, что стандарт определяет семантику для restrict -квалифицированных переменных в области блока, они не могут быть использованы для многих. Ограниченная квалификация - это средство передачи некоторой ответственности за анализ зависимостей от компилятора к программисту, но у программиста нет больше информации, которую можно было бы применить, чем у компилятора в случае, таком как пример foo_restricted_cast(). Я бы сказал, что да, ваше использование там (семантически) неверно, потому что у вас нет надежной основы для неявной гарантии того, что локальные переменные a и b не будут псевдонимами друг друга. Я оцениваю поведение G CC и Кланга в этом свете благоразумно и уместно, а я CC немного ra sh.

Что касается restrict -квалифицированных членов структуры, я не согласен с утверждение другого ответа, что семантика для них не определена. Это правда, что идентификаторы членов структуры не являются «обычными идентификаторами», но формулировка определения семантики restrict в стандарте, по-видимому, специально разработана с целью охвата элементов структуры через объявления обычных идентификаторов объектов структуры. содержащие их. Язык, безусловно, может быть прочитан таким образом, и это более чем обычно чревато, если подразумевается иначе.

Таким образом, я думаю, что случай foo_restricted_struct() имеет четко определенную семантику и, кроме того, icc оправдано использование преимуществ сглаживания, передаваемых квалификацией restrict членов структуры аргумента, так же, как если бы они были прямыми параметрами функции. Я не могу сказать, почему gcc и Clang также не используют преимущества опций оптимизации, но опять же, они не обязаны это делать.

С другой стороны, foo_restricted_subcall() демонстрирует семантическую c проблему, аналогичную проблеме в foo_restricted_cast(). Я полагаю, что есть внешняя вероятность, что именно по этой причине G CC и / или Clang избегают более агрессивной оптимизации foo_restricted_struct(), которую foo_restricted_subcall() вызывает с семантически проблемным аргументом c. Однако, вероятно, эти компиляторы просто не выполняют достаточно глубокий анализ, чтобы увидеть возможность оптимизации в этом случае.

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

restrict не определяется стандартом C для элементов конструкций.

Формальное определение restrict в 6.7.3.1 начинается с «Пусть D будет объявление обычного идентификатора… »

6.2.3 1 определяет, определяет« обычные идентификаторы »для исключения членов структур или объединений:

… Таким образом, существуют разные пространства имен для различных категории идентификаторов, такие как:

- имена меток (устранены неоднозначно по синтаксису объявления и использования меток);

- теги структур, союзов и перечислений (неоднозначных, следуя любому из ключевых слов struct , union или enum );

- the членов структур или союзов; каждая структура или объединение имеет отдельное пространство имен для своих членов (неоднозначно по типу выражения, используемого для доступа к члену через оператор . или -> );

- все остальные идентификаторы, называемые обычные идентификаторы (объявленные в обычных деклараторах или в качестве констант перечисления).

Сноска 126 в 6.7.2.1 явно говорит нам, что члены структуры не являются обычными идентификаторы:

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

...