Я просматривал некоторую документацию и вопросы / ответы и видел упомянутое. Я прочитал краткое описание, в котором говорится, что программист обещает, что указатель не будет использоваться для указания другого места.
Может ли кто-нибудь предложить некоторые реалистичные случаи, когда на самом деле стоит использовать это?
c
gcc
c99
restrict-qualifier
user90052
источник
источник
memcpy
противmemmove
один канонический пример.restrict
квалифицирующие аргументы,memcpy
позволяющие в принципе агрессивно оптимизировать наивную реализацию, и 2) простой вызовmemcpy
позволяет компилятору предполагать, что переданные ему аргументы не являются псевдонимами, что может позволить некоторую оптимизацию вокругmemcpy
вызова.memcpy(anything, anything, 0);
как не-оп, и гарантировать , что еслиp
это указатель на по крайней мере ,n
записываемые байтmemcpy(p,p,n)
; не будет иметь побочных эффектов. Такие случаи могут возникнуть ...Ответы:
restrict
говорит, что указатель - единственная вещь, которая обращается к базовому объекту. Это исключает возможность наложения указателей, обеспечивая лучшую оптимизацию компилятором.Например, предположим, у меня есть машина со специализированными инструкциями, которая может умножать векторы чисел в памяти, и у меня есть следующий код:
Потребности компилятор правильно обращаться , если
dest
,src1
иsrc2
перекрытия, а это означает , что необходимо выполнить одно умножение в то время, от начала до конца. Имеяrestrict
, компилятор может оптимизировать этот код с помощью векторных инструкций.В Википедии есть запись
restrict
, с другим примером, здесь .источник
dest
перекрывается любой из исходных векторов. С чего бы возникли проблемы, еслиsrc1
иsrc2
перекрываются?Пример Википедии является очень осветительным.
Это ясно показывает, как это позволяет сохранить одну инструкцию по сборке .
Без ограничений:
Псевдо сборка:
С ограничением:
Псевдо сборка:
GCC действительно делает это?
GCC 4.8 Linux x86-64:
С участием
-O0
они одинаковы.С
-O3
:Для непосвященных соглашение о вызовах :
rdi
= первый параметрrsi
= второй параметрrdx
= третий параметрВывод GCC был даже более ясным, чем статья в вики: 4 инструкции против 3 инструкций.
Массивы
Пока у нас есть единственная экономия команд, но если указатель представляет массивы, которые должны быть зациклены, это обычный случай использования, тогда может быть сохранен набор команд, как упомянуто суперкатом .
Рассмотрим для примера:
Из-за
restrict
этого умный компилятор (или человек) может оптимизировать это так:который потенциально гораздо более эффективен, так как может быть оптимизирован для сборки при достойной реализации libc (например, glibc): лучше ли использовать std :: memcpy () или std :: copy () с точки зрения производительности?
GCC действительно делает это?
GCC 5.2.1. Linux x86-64 Ubuntu 15.10:
С участием
-O0
, оба одинаковы.С
-O3
:с ограничением:
Два
memset
звонка, как и ожидалось.без ограничений: никаких вызовов stdlib, просто развертывание цикла в 16 итераций, которое я не собираюсь воспроизводить здесь :-)
У меня не хватило терпения их тестировать, но я считаю, что ограниченная версия будет быстрее.
C99
Давайте посмотрим на стандарт для полноты.
restrict
говорит, что два указателя не могут указывать на перекрывающиеся области памяти. Наиболее распространенное использование для аргументов функции.Это ограничивает способ вызова функции, но позволяет оптимизировать время компиляции.
Если вызывающая сторона не выполняет
restrict
договор, неопределенное поведение.Проект C99 N1256 6.7.3 / 7 « Классификаторы типов» гласит:
и 6.7.3.1 «Формальное определение ограничения» дает кровные детали.
Строгое правило алиасинга
restrict
Ключевое слово влияет только указатели совместимых типов (например , дваint*
) , поскольку строгие правила наложения спектров говорят , что сглаживание несовместимых типов не определенно поведение по умолчанию, и поэтому компиляторы могут предположить , что это не произойдет и оптимизирует прочь.Смотрите: что такое строгое правило наложения имен?
Смотрите также
restrict
, но GCC имеет__restrict__
в качестве расширения: что означает ключевое слово restrict в C ++?__attribute__((malloc))
, который говорит, что возвращаемое значение функции ни к чему не привязано: GCC: __attribute __ ((malloc))источник
void zap(char *restrict p1, char *restrict p2) { for (int i=0; i<50; i++) { p1[i] = 4; p2[i] = 9; } }
, что ограничивающие квалификаторы позволят компилятору переписать код как «memset (p1,4,50); memset (p2,9,50);». Restrict значительно превосходит псевдонимы на основе типов; Обидно, компиляторы больше ориентируются на последнее.__restrict
. В противном случае двойные подчеркивания могут быть неверно истолкованы как указание на то, что вы кричите.