Вопрос

Если бы у меня был C99 restrict ключевое слово верно, квалификация указателя с его помощью - это обещание, что данные, на которые он ссылается, не будут изменены за спиной компилятора с помощью псевдонимов.

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

Аналогичным образом, было бы уместно рассмотреть restrict квалификатор в прототипе функции как требование, чтобы пользователь обеспечивал эксклюзивный доступ ("избегать псевдонимов" или, может быть, что-то более сильное) на время вызова?Следует ли его использовать как "документацию"?

Кроме того, есть ли что-то, что нужно понимать в том факте, что restrict определяет указатель, а не данные, на которые он указывает (как const делает) ?

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

Это было полезно?

Решение

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

Другие советы

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

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 при правильном использовании может привести к действительно значительному ускорению.

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

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

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

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

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

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 из памяти на каждой итерации цикла, на всякий случай тот, кто это позвонил, был совершенно безумен.Использование ограничения сообщает компилятору, что он может генерировать код, предполагая, что a и b всегда будут разными, поэтому запись в *a никогда не изменит *b (или наоборот).

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

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

Restrict также содержит предупреждение API для пользователей о том, что данная функция реализована с допущением несмещенных параметров.

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

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

Мы можем видеть 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.

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top