Я всегда был неуверен, что означает ключевое слово ограничения в C++?Что означает ключевое слово ограничения в C++?
Означает ли это, что два или более указателя, присвоенные функции, не перекрываются? Что еще это значит?
Я всегда был неуверен, что означает ключевое слово ограничения в C++?Что означает ключевое слово ограничения в C++?
Означает ли это, что два или более указателя, присвоенные функции, не перекрываются? Что еще это значит?
Ничего. Он был добавлен к стандарту C99.
Это не совсем так. По-видимому, это поддерживается некоторыми компиляторами C++, и некоторые люди настоятельно рекомендуют использовать его, когда они доступны, см. Мой ответ ниже. –
@ Robert S Barnes: стандарт C++ не распознает 'ограничение' как ключевое слово. Поэтому мой ответ верен. То, что вы описываете, - это поведение, специфическое для реализации, и то, на что вы * не должны полагаться. – dirkgently
@ dirkgently: При всем уважении, почему бы и нет? Многие проекты привязаны к конкретным нестандартным языковым расширениям, поддерживаемым только конкретными или очень немногими компиляторами. Ядро Linux и gcc приходит на ум. Это не редкость придерживаться конкретного компилятора или даже конкретной ревизии конкретного компилятора для всего полезного срока службы проекта. Не каждая программа должна строго соответствовать. –
Поскольку заголовочные файлы из некоторых библиотек C используют ключевое слово, язык C++ должен будет что-то с этим сделать .. как минимум, игнорируя ключевое слово, поэтому нам не нужно #define ключевое слово в пустой макрос для подавления ключевого слова.
Я бы предположил, что это либо обрабатывается с помощью декларации 'extern C', либо путем молчания, как в случае с компилятором AIX C/C++, который вместо этого обрабатывает ключевое слово' __rerstrict__'. Это ключевое слово также поддерживается в gcc, поэтому код будет скомпилировать то же самое в g ++. –
В своей статье, Memory Optimization, Кристер Эриксон говорит, что в то время как restrict
не является частью C++ стандарта еще, что он поддерживается многими компиляторами, и он рекомендует его использование при наличии:
ограничить ключевое слово
! Новый стандарт 1999 ANSI/ISO C
! Совсем не в стандарте C++, но поддерживается многими компиляторами C++
! Намек только, так ничего не может сделать и по-прежнему в соответствии
A ограничивают квалифицированный указатель (или ссылка) ...
! ... в основном представляет собой компромисс, который для области области действия указателя будет доступен только через этот указатель (и указатели скопировали из него).
В компиляторах C++, которые поддерживают его, что, вероятно, следует вести себя так же, как и в С.
Смотрите эту SO опубликовать подробную информацию: Realistic usage of the C99 ‘restrict’ keyword?
занимает полчаса, чтобы пролистать бумаги Эриксона, это интересно и стоит того времени.
Редактировать
Я также обнаружил, что компании IBM AIX C/C++ compiler supports the __restrict__
keyword.
г ++ также, кажется, поддерживает это как следующая программа компилируется на г ++:
#include <stdio.h>
int foo(int * __restrict__ a, int * __restrict__ b) {
return *a + *b;
}
int main(void) {
int a = 1, b = 1, c;
c = foo(&a, &b);
printf("c == %d\n", c);
return 0;
}
Я также нашел хорошую статью об использовании restrict
:
Demystifying The Restrict Keyword
Edit2
Я наткнулся на статью, в которой конкретно обсуждается использование o е ограничение в программах C++:
Load-hit-stores and the __restrict keyword
Кроме того, Microsoft Visual C++ also supports the __restrict
keyword.
Ссылка на бумагу для оптимизации памяти мертва, вот ссылка на аудио из его презентации GDC. http://www.gdcvault.com/play/1022689/Memory – Grimeh
@EnnMichael: Очевидно, что если вы собираетесь использовать его в переносном проекте на C++, вы должны «#ifndef __GNUC__'' #define __restrict__/* no-op */'или аналогичный. И определите его для '__restrict', если определена' _MSC_VER'. –
В C++ такого ключевого слова нет. Список ключевых слов C++ можно найти в разделе 2.11/1 стандарта языка C++. restrict
- это ключевое слово в C99 версии языка C, а не на C++.
Многие компиляторы C++ поддерживают ключевое слово '__restrict__', которое, насколько я могу судить, идентично. –
@Robert: Но ** в C++ ** такого ключевого слова нет. То, что делают отдельные компиляторы, это их собственный бизнес, но он не является частью языка C++. – jalf
Как другие говорили, если средства ничего от 14 C++, так что давайте рассмотрим расширение __restrict__
GCC, который делает то же самое, как C99 restrict
.
C99
restrict
говорит, что два указателя не может указывать на перекрытие областей памяти. Наиболее распространенное использование для аргументов функции.
Это ограничивает способ вызова функции, но позволяет оптимизировать компиляцию.
Если вызывающий абонент не соблюдает договор restrict
, неопределенное поведение.
В C99 N1256 draft 6.7.3/7 «Тип классификаторов» говорит:
Намеченное использование ограничения классификатора (например, класс хранения регистров) является содействие оптимизации и удаление всех экземпляров классификатором из все единицы перевода для предварительной обработки, составляющие соответствующую программу, не меняют своего значения (т. е. наблюдаемого поведения).
и 6.7.3.1 «Формальное определение ограничения» дает детали gory.
Возможная оптимизация
Wikipedia exampleявляется очень освещения.
Он ясно показывает, как позволяет сохранить одну инструкцию по сборке.
Без ограничения:
void f(int *a, int *b, int *x) {
*a += *x;
*b += *x;
}
Псевдо сборки:
load R1 ← *x ; Load the value of x pointer
load R2 ← *a ; Load the value of a pointer
add R2 += R1 ; Perform Addition
set R2 → *a ; Update the value of a pointer
; Similarly for b, note that x is loaded twice,
; because a may be equal to x.
load R1 ← *x
load R2 ← *b
add R2 += R1
set R2 → *b
С ограничения:
void fr(int *__restrict__ a, int *__restrict__ b, int *__restrict__ x);
Псевдо сборки:
load R1 ← *x
load R2 ← *a
add R2 += R1
set R2 → *a
; Note that x is not reloaded,
; because the compiler knows it is unchanged
; load R1 ← *x
load R2 ← *b
add R2 += R1
set R2 → *b
Действительно ли GCC это делает?
g++
4,8 Linux x86-64:
g++ -g -std=gnu++98 -O0 -c main.cpp
objdump -S main.o
С -O0
, они одинаковы.
С -O3
:
void f(int *a, int *b, int *x) {
*a += *x;
0: 8b 02 mov (%rdx),%eax
2: 01 07 add %eax,(%rdi)
*b += *x;
4: 8b 02 mov (%rdx),%eax
6: 01 06 add %eax,(%rsi)
void fr(int *__restrict__ a, int *__restrict__ b, int *__restrict__ x) {
*a += *x;
10: 8b 02 mov (%rdx),%eax
12: 01 07 add %eax,(%rdi)
*b += *x;
14: 01 06 add %eax,(%rsi)
Для непосвященных calling convention является:
rdi
= первый параметрrsi
= Второй параметрrdx
= третий параметрВыход GCC был еще яснее, чем статья wiki: 4 инструкции и 3 инструкции.
Массивы
До сих пор мы имеем единичные сбережения инструкции, но если указатель представляет массивы быть накинут, общий случай использования, то куча инструкций может быть сохранены, как уже упоминалось supercat и michael.
Рассмотрим, например:
void f(char *restrict p1, char *restrict p2, size_t size) {
for (size_t i = 0; i < size; i++) {
p1[i] = 4;
p2[i] = 9;
}
}
Из-за restrict
, умный компилятор (или человека), могли бы оптимизировать, что:
memset(p1, 4, size);
memset(p2, 9, size);
, потенциально гораздо более эффективным, как это может быть сборка оптимизирован на достойную реализацию libc (например, glibc) Is it better to use std::memcpy() or std::copy() in terms to performance?, возможно с SIMD instructions.
Без, ограничивайте, эта оптимизация не может быть выполнена, например.рассмотреть следующие вопросы:
char p1[4];
char *p2 = &p1[1];
f(p1, p2, 3);
Тогда for
версия делает:
p1 == {4, 4, 4, 9}
в то время как версия memset
делает:
p1 == {4, 9, 9, 9}
ли GCC действительно сделать это?
GCC 5.2.1.Linux x86-64 Ubuntu 15.10:
gcc -g -std=c99 -O0 -c main.c
objdump -dr main.o
С -O0
оба одинаковы.
С -O3
:
с ограничения:
3f0: 48 85 d2 test %rdx,%rdx
3f3: 74 33 je 428 <fr+0x38>
3f5: 55 push %rbp
3f6: 53 push %rbx
3f7: 48 89 f5 mov %rsi,%rbp
3fa: be 04 00 00 00 mov $0x4,%esi
3ff: 48 89 d3 mov %rdx,%rbx
402: 48 83 ec 08 sub $0x8,%rsp
406: e8 00 00 00 00 callq 40b <fr+0x1b>
407: R_X86_64_PC32 memset-0x4
40b: 48 83 c4 08 add $0x8,%rsp
40f: 48 89 da mov %rbx,%rdx
412: 48 89 ef mov %rbp,%rdi
415: 5b pop %rbx
416: 5d pop %rbp
417: be 09 00 00 00 mov $0x9,%esi
41c: e9 00 00 00 00 jmpq 421 <fr+0x31>
41d: R_X86_64_PC32 memset-0x4
421: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
428: f3 c3 repz retq
Два memset
вызовы, как и ожидалось.
без ограничения: нет STDLIB вызовов, только 16 итераций широкий loop unrolling, которые я не собираюсь приводить здесь :-)
Я не имел терпения сравнить их, но я не верю что ограниченная версия будет быстрее.
Строгие правила наложения спектров
restrict
ключевое слово влияет только указатели совместимых типов (например, два int*
), потому что строгие правила наложения спектров говорит, что сглаживание несовместимых типов не определено поведение по умолчанию, и поэтому компиляторы могут предположить, что это делает не происходит и не оптимизируется.
См: What is the strict aliasing rule?
ли работа для ссылок?
Согласно ССАГПЗ документации он делает: https://gcc.gnu.org/onlinedocs/gcc-5.1.0/gcc/Restricted-Pointers.html с синтаксисом:
int &__restrict__ rref
Существует даже версия для this
функций членов:
void T::fn() __restrict__
приятный asnwer. Что делать, если строковое сглаживание отключено '-fno-strict-aliasing', тогда' ограничение' не должно иметь никакого значения между указателями того же типа или разных типов, нет? (я ссылаюсь на «Ключевое слово ограничения влияет только на указатели совместимых типов») – user463035818
@ tobi303 Я не знаю! Дайте мне знать, если вы узнаете наверняка ;-) –
@jww да, это лучший способ его формулировки. Обновлено. –
'restrict' является c99 ключевое слово. Да, Rpbert S. Barnes, я знаю, что большинство компиляторов поддерживают '__restrict__'. Вы заметите, что что-либо с двойными подчеркиваниями является, по определению, специфичным для реализации и, следовательно, _NOT C++ _, но его конкретной версией для компилятора. – KitsuneYMG
Что? Просто потому, что конкретная реализация не делает ее не C++; C++ допускает конкретные действия для реализации явно, и не запрещает его или не делает его не C++. – Alice
@Alice KitsuneYMG означает, что он не является частью ISO C++ и вместо этого считается расширением C++. Создателям компилятора разрешено создавать и распространять свои собственные расширения, которые сосуществуют с ISO C++ и действуют как часть обычного или непереносимого неофициального дополнения к C++.Примерами могут служить старые Managed C++ от MS и их более поздний C++/CLI. Другими примерами могут быть директивы препроцессора и макросы, поставляемые некоторыми компиляторами, такие как общая директива '# warning' или макросы подписи функций (' __PRETTY_FUNCTION__' на GCC, '__FUNCSIG__' на MSVC и т. Д.). –