ограничивать
На языке программирования Си , restrict
— это ключевое слово , введенное стандартом C99 , [1] который можно использовать в объявлениях указателей . Добавляя этот квалификатор типа , программист намекает компилятору , что за время существования указателя никакой другой указатель не будет использоваться для доступа к объекту, на который он указывает. Это позволяет компилятору выполнять оптимизацию (например, векторизацию), которая в противном случае была бы невозможна.
restrict
ограничивает эффекты псевдонимов указателей , способствуя оптимизации . Если декларация о намерениях не соблюдается и к объекту осуществляется доступ по независимому указателю, это приведет к неопределенному поведению .
Оптимизация
[ редактировать ]Если компилятор знает, что на блок памяти существует только один указатель, он может создать более оптимизированный код. Например:
void updatePtrs(size_t *ptrA, size_t *ptrB, size_t *val)
{
*ptrA += *val;
*ptrB += *val;
}
В приведенном выше коде указатели ptrA
, ptrB
, и val
может ссылаться на одну и ту же ячейку памяти , поэтому компилятор может сгенерировать менее оптимальный код:
; Hypothetical RISC Machine.
ldr r12, [val] ; Load memory at val to r12.
ldr r3, [ptrA] ; Load memory at ptrA to r3.
add r3, r3, r12 ; Perform addition: r3 = r3 + r12.
str r3, [ptrA] ; Store r3 to memory location ptrA, updating the value.
ldr r3, [ptrB] ; 'load' may have to wait until preceding 'store' completes.
ldr r12, [val] ; Have to load a second time to ensure consistency.
add r3, r3, r12
str r3, [ptrB]
Однако, если restrict
используется ключевое слово, и указанная выше функция объявлена как
void updatePtrs(size_t *restrict ptrA, size_t *restrict ptrB, size_t *restrict val);
тогда компилятору разрешено предполагать , что ptrA
, ptrB
, и val
указывают на разные места, и обновление ячейки памяти, на которую ссылается один указатель, не повлияет на области памяти, на которые ссылаются другие указатели. Программист, а не компилятор, несет ответственность за то, чтобы указатели не указывали на идентичные места. Компилятор может, например, перестроить код, сначала загрузив все ячейки памяти, затем выполнив операции, прежде чем зафиксировать результаты обратно в память.
ldr r12, [val] ; Note that val is now only loaded once.
ldr r3, [ptrA] ; Also, all 'load's in the beginning ...
ldr r4, [ptrB]
add r3, r3, r12
add r4, r4, r12
str r3, [ptrA] ; ... all 'store's in the end.
str r4, [ptrB]
Приведенный выше ассемблерный код короче, поскольку val
загружается только один раз. Кроме того, поскольку компилятор может более свободно перестраивать код, он может генерировать код, который выполняется быстрее. Во второй версии приведенного выше примера store
все операции происходят после load
операций, гарантируя, что процессору не придется блокироваться в середине кода, чтобы дождаться, пока store
операции завершены.
Обратите внимание, что реальный сгенерированный код может иметь другое поведение. Выгода от приведенного выше мини-примера, как правило, невелика, и в реальных случаях ограничение действительно помогает в больших циклах, выполняющих интенсивный доступ к памяти.
Как упоминалось выше, поведение неправильного кода не определено , компилятор гарантирует правильную работу сгенерированного кода только в том случае, если код соответствует декларации о намерениях.
Поддержка компиляторов C++.
[ редактировать ]C++ не имеет стандартной поддержки restrict
, но у многих компиляторов есть эквиваленты, которые обычно работают как на C++, так и на C, например GCC и Clang . __restrict__
и Visual C ++ __declspec(restrict)
. Кроме того, __restrict
поддерживается этими тремя компиляторами. Точная интерпретация этих альтернативных ключевых слов зависит от компилятора:
- В компиляторах в стиле Unix, таких как GCC и Clang,
__restrict
и__restrict__
означают точно то же самое, что и их аналог в C. Расширения позволяют применять их к ссылочным типам иthis
. [2] - В Visual C++ предусмотрено несколько квалификаторов без псевдонимов:
__declspec(restrict)
применяется к объявлению функции и указывает на то, что возвращаемый указатель не является псевдонимом.__restrict
используется там же, где иrestrict
, но подсказка без псевдонимов не распространяется, как вrestrict
. Он также расширен для типов объединения .
Предупреждения компилятора
[ редактировать ]Чтобы предотвратить неправильный код, некоторые компиляторы и другие инструменты пытаются обнаружить, когда перекрывающиеся аргументы были переданы функциям с отмеченными параметрами. restrict
. [3] рассматривает Стандарт кодирования CERT C неправильное использование restrict
и помеченные им библиотечные функции (EXP43-C) являются вероятным источником ошибок программного обеспечения, хотя по состоянию на ноябрь 2019 года не известно, какие уязвимости были вызваны этим. [4]
Ссылки
[ редактировать ]- ^ Дреппер, Ульрих (23 октября 2007 г.). «Память, часть 5: Что умеют программисты» . Что должен знать каждый программист о памяти . lwn.net .
...Правила псевдонимов по умолчанию в языках C и C++ не помогают компилятору принимать эти решения (если не используется ограничение, все обращения к указателям являются потенциальными источниками псевдонимов). Вот почему Фортран по-прежнему является предпочтительным языком числового программирования: он упрощает написание быстрого кода. (Теоретически ключевое слово ограничения, введенное в язык C в версии 1999 года, должно решить проблему. Однако компиляторы еще не справились с этой задачей. Причина главным образом в том, что существует слишком много неправильного кода, который может ввести в заблуждение компилятор и заставить его генерировать неправильные объектный код.)
- ^ «Ограниченные указатели» . Использование коллекции компиляторов GNU (GCC) .
- ^ «Параметры предупреждения: -Ограничить» . ССЗ . Проверено 19 ноября 2019 г.
- ^ «EXP43-C. Избегайте неопределенного поведения при использовании указателей с ограничением» . SEI CERT Стандарт кодирования C. Проверено 19 ноября 2019 г.
- «Проект комитета ISO/IEC 9899:TC2» (PDF) . ИСО . 6 мая 2005 г., стр. 108–112 . Проверено 22 декабря 2008 г.
Внешние ссылки
[ редактировать ]- Демистификация ключевого слова Restrict : объяснение и примеры использования
- Уоллс, Дуглас. «Как использовать квалификатор ограничения в C» . Оракул™ . Проверено 21 ноября 2012 г.
- Ограниченные указатели в C : исходное обоснование определения