Правила для использования ограничить ключевого слова в C?

Я пытаюсь понять когда и если не использовать restrict ключевое слово в C и в том, какие ситуации это предоставляет материальное преимущество.

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

foo(int *a, int *b, int *c, int n) {
    for (int i = 0; i

компилятор должен перезагрузить c во втором выражении, потому что, возможно, b и c укажите на то же местоположение. Это также должно ожидать b быть сохраненным, прежде чем это сможет загрузиться a по той же причине. Это затем должно ожидать a быть сохраненным и должно перезагрузить b и c в начале следующего цикла. Если Вы вызываете функцию как это:

int a[N];
foo(a, a, a, N);

затем Вы видите, почему компилятор должен сделать это. Используя restrict эффективно говорит компилятору, что Вы никогда не будете делать этого, так, чтобы он мог отбросить избыточную загрузку c и загрузка a прежде b хранится.

В другом, ТАКИМ ОБРАЗОМ, сообщение, Nils Pipenbrinck, обеспечивает рабочий пример этого сценария, демонстрирующего выигрыш в производительности.

До сих пор я заключил, что это - хорошая идея использовать restrict на указателях Вы передаете в функции, которые не будут встроены. По-видимому, если код встраивается, компилятор может выяснить, что указатели не накладываются.

Теперь вот то, где вещи начинают становиться нечеткими для меня.

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

Однако, когда я компилирую его пример кода или с или без restrict Я получаю идентичные двоичные файлы в обоих случаях. Я использую gcc version 4.2.4 (Ubuntu 4.2.4-1ubuntu4)

Вещь, которую я не могу выяснить в следующем коде, состоит в том, должно ли это быть переписано для создания более широкого применения restrict, или если анализ псевдонима в GCC настолько хорош, что он может выяснить, что ни один из аргументов не искажает друг друга. В чисто образовательных целях, как я могу сделать использование или не использование restrict вопрос в этом коде - и почему?

Для restrict скомпилированный с:

gcc -DCLS=$(getconf LEVEL1_DCACHE_LINESIZE) -DUSE_RESTRICT -Wextra -std=c99 -O3 matrixMul.c -o matrixMul

Просто удалите -DUSE_RESTRICT не использовать restrict.

#include 
#include 
#include 

#ifdef USE_RESTRICT
#else
#define restrict
#endif

#define N 1000
double _res[N][N] __attribute__ ((aligned (64)));
double _mul1[N][N] __attribute__ ((aligned (64)))
    = { [0 ... (N-1)] 
    = { [0 ... (N-1)] = 1.1f }};
double _mul2[N][N] __attribute__ ((aligned (64)))
    = { [0 ... (N-1)] 
    = { [0 ... (N-1)] = 2.2f }};

#define SM (CLS / sizeof (double))

void mm(double (* restrict res)[N], double (* restrict mul1)[N], 
        double (* restrict mul2)[N]) __attribute__ ((noinline));

void mm(double (* restrict res)[N], double (* restrict mul1)[N], 
        double (* restrict mul2)[N])
{
 int i, i2, j, j2, k, k2; 
    double *restrict rres; 
    double *restrict rmul1; 
    double *restrict rmul2; 

    for (i = 0; i < N; i += SM)
        for (j = 0; j < N; j += SM)
            for (k = 0; k < N; k += SM)
                for (i2 = 0, rres = &res[i][j],
                    rmul1 = &mul1[i][k]; i2 < SM;
                    ++i2, rres += N, rmul1 += N)
                    for (k2 = 0, rmul2 = &mul2[k][j];
                        k2 < SM; ++k2, rmul2 += N)
                        for (j2 = 0; j2 < SM; ++j2)
                          rres[j2] += rmul1[k2] * rmul2[j2];
}

int main (void)
{

    mm(_res, _mul1, _mul2);

 return 0;
}

68
задан Community 23 May 2017 в 12:25
поделиться

5 ответов

Это намек на оптимизатор кода. Использование ограничения позволяет ему хранить переменную указателя в регистре CPU и не производить обновление значения указателя в память для обновления псевдонима.

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

.
13
ответ дан 24 November 2019 в 14:23
поделиться
[

] Может быть, оптимизация, проведенная здесь, не зависит от того, что указатели не являются псевдонимами? Если только вы не загрузите несколько mul2-элементов перед записью результата в res2, я не вижу никакой проблемы с псевдонимом.[

]. [

] В первом куске кода, который вы показываете, вполне понятно, с какими псевдонимами может возникнуть проблема. Здесь не так уж и ясно. [

] [

] Перечитывая статью Dreppers, он конкретно не говорит, что ограничение может решить что угодно. Есть даже такая фраза :[

] [
] [

] {В теории ключевое слово ограничения введённый в язык C Пересмотр 1999 года должен решить проблему проблема. Составители не наверстали Тем не менее. Причина в том, что слишком много некорректного кода, который ввел бы в заблуждение компилятор и вызвал бы чтобы сгенерировать некорректный объектный код.}[

] [
] [

] В данном коде оптимизация доступа к памяти уже выполнена в рамках алгоритма. Остаточная оптимизация, судя по всему, выполнена в векторном коде, представленном в приложении. Так что для представленного здесь кода никакой разницы, наверное, нет, так как оптимизация, основанная на ограничении, не производится. Каждый доступ к указателю является источником псевдонимы, но не каждая оптимизация полагается на псевдонимы.[

]. [

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

]
1
ответ дан 24 November 2019 в 14:23
поделиться
[

] Ты работаешь на 32-х или 64-х битном Ubuntu? Если 32-битная, то необходимо добавить []-march=core2 -mfpmath=sse[] (или как там у вас архитектура процессора), иначе он не использует SSE. Во-вторых, чтобы включить векторизацию с помощью GCC 4.2, необходимо добавить опцию []-ftree-vectorize[] (начиная с 4.3 или 4.4 она включена по умолчанию в []-O3[]). Также может понадобиться добавить []-ffast-math[] (или другую опцию, предоставляющую расслабленную семантику с плавающей точкой), чтобы компилятор мог переупорядочивать операции с плавающей точкой.[

]. [

] Также, добавьте опцию [] -ftree-vectorizer-verbose=1[], чтобы проверить, удается ли ему векторизовать цикл или нет; это простой способ проверить эффект от добавления ключевого слова limit.[

].
0
ответ дан 24 November 2019 в 14:23
поделиться

Если есть разница вообще, переместите mm в отдельный DSO (так что gcc больше не может знать все о вызывающем коде) будет способом продемонстрировать это.

1
ответ дан 24 November 2019 в 14:23
поделиться

Кроме того, в GCC 4.0.0-4.4 есть ошибка регрессии, из-за которой ключевое слово restrict игнорируется. Сообщалось, что эта ошибка исправлена ​​в версии 4.5 (хотя номер ошибки я потерял).

14
ответ дан 24 November 2019 в 14:23
поделиться
Другие вопросы по тегам:

Похожие вопросы: