Что люди могут сделать из ограничить спецификатора?

У меня была большая удача с методом, описанным здесь:

http://mail.python.org/pipermail/python-dev/2008-January/076194.html

я понятия не имею, работает ли это над builtins все же.

16
задан Mat 2 August 2012 в 18:07
поделиться

6 ответов

Лучшая «интуиция» относительно ключевого слова restrict состоит в том, что это гарантия (от программиста компилятору), что в течение времени жизни указателя память, доступная через этот указатель, будет ТОЛЬКО можно получить доступ через этот указатель, а не через другой указатель, ссылку или глобальный адрес. Поэтому важно, чтобы указатель был свойством как указателя, так и памяти, связывая их вместе, пока указатель не выйдет за пределы области видимости.

14
ответ дан 30 November 2019 в 15:41
поделиться

Крис Додд имеет правильное описание ключевого слова. На некоторых платформах это может быть очень важно по соображениям производительности, поскольку позволяет компилятору знать, что после загрузки данных через этот указатель в регистр ему не нужно делать это снова. Без этой гарантии компилятор должен перезагружать данные через указатель каждый раз, когда записывается любой другой указатель с возможным псевдонимом, что может вызвать серьезную остановку конвейера, называемую load-hit-store .

const и restrict - разные концепции, и это не тот случай, когда const подразумевает restrict . Все const говорит, что вы не будете писать через этот указатель в рамках этой функции . Указатель const все еще может иметь псевдоним. Например, рассмотрим:

int foo( const int *a, int * b )
{
   *b *= 2;
   return *a + *b; // induces LHS: *a must be read back immediately
                   // after write has cleared the store queue
}

Хотя вы не можете напрямую писать в a в этой функции, для вас будет совершенно законным вызов foo, например:

int x = 3;
foo( &x, &x );  // returns 12

restrict - это другая гарантия: обещание, что a! = B во всех вызовах foo () .

Я подробно писал о ключевом слове restrict и его последствиях для производительности , и Майк Эктон . Хотя мы говорим о конкретном упорядоченном PowerPC, проблема загрузки-попадания-сохранения существует и на x86, но x86 ' Из-за неупорядоченного выполнения становится сложнее изолировать этот срыв в профиле.

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

21
ответ дан 30 November 2019 в 15:41
поделиться

Большая часть того, что вы знаете, неверно!

const не не гарантирует, что что-то не изменится за спиной компилятора. Все, что он делает, это мешает вам писать в это место. Однако что-то еще может записывать в это место, поэтому компилятор НЕ может предполагать, что это константа.

Как говорили другие, квалификатор restrict связан с алиасингом. Фактически, во время первого раунда стандартизации C было предложено ключевое слово noalias. К сожалению, предложение было написано довольно плохо - оно побудило Денниса Ричи к участию в этом процессе в тот единственный раз, когда он написал письмо, в котором говорилось о том, что «noalias должны уйти. Это не открыто для переговоров. "

Излишне говорить, что 'noalias' didn ' t стал частью C. Когда пришло время попробовать еще раз, предложение было написано настолько лучше, что ограничение было включено в стандарт - и хотя noalias, вероятно, было бы для него более значимым названием, это имя было настолько испорченным, что Я сомневаюсь, что кто-то даже рассматривал возможность его использования.

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

void f(int *a, int *b, int *c) { 
    for (int i=0; i<*a; i++)
        *b += c[i];
}

Компилятор действительно хочет поместить i в регистр и загрузить * a в регистр, поэтому, когда приходит время решать, выполнять ли еще одну итерацию цикла, он просто сравнивает значения в тех регистрах друг к другу. К сожалению, он не может этого сделать - если кто-то, кто использовал эту функцию, был совершенно ненормальным, и вызвал его с a == b, каждый раз, когда он записывает в * b внутри цикла, это новое значение также является значением * a - поэтому он должен читать * a из памяти на каждой итерации цикла, на всякий случай , кто бы это ни назвал, был совершенно ненормальным. Использование restrict сообщает компилятору, что он может генерировать код, предполагая, что a и b всегда будут разными, поэтому запись в * a никогда не изменит * b (или наоборот).

8
ответ дан 30 November 2019 в 15:41
поделиться

Вы все правильно поняли. Квалификатор restrict просто указывает, что данные, к которым обращается указатель с таким указателем, доступны только этим указателем. Это применимо как к чтению, так и к записи.

Компилятор не заботится о параллельных потоках, он не собирался генерировать код по-другому, и вы можете затирать свои собственные данные по своему усмотрению. Но при этом необходимо знать, какие операции с указателями могут изменить глобальную память.

Restrict также несет с собой предупреждение API для людей о том, что данная функция реализована с использованием параметров без элайсинга.

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

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

Мы можем увидеть restrict в действии:

void move(int *a, int *b) {     void move(int *__restrict a, int *__restrict b) {
    a[0] = b[0];                    a[0] = b[0];
    a[1] = b[0];                    a[1] = b[0];
}                               }
    movl    (%edx), %eax            movl    (%edx), %edx
    movl    %eax, (%ecx)            movl    %edx, (%eax)
    movl    (%edx), %eax            movl    %edx, 4(%eax)
    movl    %eax, 4(%ecx)

В правый столбец, с restrict , компилятору не нужно было перечитывать b [0] из памяти. Он смог прочитать b [0] и сохранить его в регистре % edx , а затем просто дважды сохраните регистр в памяти. В левом столбце он не знал, изменился ли магазин на a b .

5
ответ дан 30 November 2019 в 15:41
поделиться

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

«Данные не будут изменены за спиной компилятора» звучит скорее как для меня противоположность «летучести».

«const» означает, что данные не будут изменены на глазах у программиста; то есть она не может изменять данные с помощью означающего, помеченного как «const» (я пишу «означающее», потому что в int const * pi имя pi не является const , но * pi есть). Данные могут быть изменены с помощью другого означающего (в конце концов, неконстантные данные могут быть переданы функции как константные данные).

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

1
ответ дан 30 November 2019 в 15:41
поделиться

Это может быть пример из чрезвычайно узкой области, но платформа Altera Nios II представляет собой микроконтроллер с программным ядром, который вы можете настраивать внутри FPGA. Затем в исходном коде C для этого микроконтроллера вы можете использовать инструмент C-to-hardware для ускорения внутренних циклов с использованием специального оборудования, а не программного обеспечения.

Там используется __ restrict __ (которое аналогично C99 restrict ) позволяет инструменту C2H правильно оптимизировать аппаратное ускорение работы указателя параллельно , а не последовательно. По крайней мере, в этом случае ограничение просто не предназначено для потребления человеком. См. Также страницу Sun на restrict , где в первой строке написано

. Использование квалификатора restrict в программах на C может позволить компилятору создавать исполняемые файлы значительно быстрее.

Если кому-то интересно узнать больше о C2H, этот PDF обсуждает оптимизацию результатов C2H. Раздел __ restrict __ находится на странице 20.

1
ответ дан 30 November 2019 в 15:41
поделиться
Другие вопросы по тегам:

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