J'étais toujours incertain, que signifie le mot clé restrict en C ++?
Cela signifie-t-il que les deux ou plusieurs pointeurs donnés à la fonction ne se chevauchent pas? Qu'est-ce que cela signifie d'autre?
c++
restrict-qualifier
Tapis
la source
la source
restrict
est un mot-clé c99. Oui, Rpbert S. Barnes, je sais que la plupart des compilateurs prennent en charge__restrict__
. Vous noterez que tout ce qui a des doubles traits de soulignement est, par définition, spécifique à l'implémentation et donc PAS C ++ , mais une version spécifique au compilateur de celui-ci.#warning
directive commune , ou les macros de signature de fonction (__PRETTY_FUNCTION__
sur GCC,__FUNCSIG__
sur MSVC, etc.).restrict
n'est pas considéré comme un mot clé C ++ (voir en.cppreference.com/w/cpp/keyword ), et en fait, la seule mention derestrict
dans la norme C ++ 11 (voir open-std.org/jtc1/sc22/wg21 /docs/papers/2012/n3337.pdf , une copie du FDIS avec des modifications rédactionnelles mineures, §17.2 [library.c], PDF page 413) déclare que:restrict
doit être omis (exclu de, laissé de côté) les signatures et la sémantique des fonctions de la bibliothèque standard C lorsque ces fonctions sont incluses dans la bibliothèque standard C ++. Ou en d'autres termes, j'ai déclaré le fait que si la signature d'une fonction de bibliothèque standard C contientrestrict
en C, lerestrict
mot - clé doit être supprimé de la signature de l'équivalent C ++.Réponses:
Dans son article, Memory Optimization , Christer Ericson dit que bien que
restrict
ne fasse pas encore partie de la norme C ++, il est pris en charge par de nombreux compilateurs et il recommande son utilisation lorsqu'elle est disponible:Dans les compilateurs C ++ qui le prennent en charge, il devrait probablement se comporter de la même manière qu'en C.
Voir cet article SO pour plus de détails: Utilisation réaliste du mot-clé C99 'restrict'?
Prenez une demi-heure pour parcourir le papier d'Ericson, c'est intéressant et ça vaut le coup.
Éditer
J'ai également constaté que le compilateur AIX C / C ++ d'
__restrict__
IBM prend en charge le mot - clé .g ++ semble également prendre en charge cela car le programme suivant se compile proprement sur g ++:
#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; }
J'ai également trouvé un bel article sur l'utilisation de
restrict
:Démystifier le mot-clé Restreindre
Modifier2
Je suis tombé sur un article qui traite spécifiquement de l'utilisation de restrict dans les programmes C ++:
Load-hit-stores et le mot clé __restrict
En outre, Microsoft Visual C ++ prend également en charge le
__restrict
mot clé .la source
#ifndef __GNUC__
#define __restrict__ /* no-op */
ou similaire. Et définissez-le comme__restrict
si_MSC_VER
est défini.Comme d'autres l'ont dit, si cela ne signifie rien à partir de C ++ 14 , considérons donc l'
__restrict__
extension GCC qui fait la même chose que le C99restrict
.C99
restrict
dit que deux pointeurs ne peuvent pas pointer vers des régions de mémoire qui se chevauchent. L'utilisation la plus courante concerne les arguments de fonction.Cela restreint la façon dont la fonction peut être appelée, mais permet davantage d'optimisations de compilation.
Si l'appelant ne respecte pas le
restrict
contrat, comportement indéfini.Le projet de C99 N1256 6.7.3 / 7 "Qualificatifs de type" dit:
et 6.7.3.1 "Définition formelle de restreindre" donne les détails sanglants.
Une optimisation possible
L' exemple de Wikipedia est très éclairant.
Il montre clairement comment car il permet de sauvegarder une instruction d'assemblage .
Sans restriction:
void f(int *a, int *b, int *x) { *a += *x; *b += *x; }
Pseudo assemblage:
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 x may point to a (a aliased by x) thus ; the value of x will change when the value of a ; changes. load R1 ← *x load R2 ← *b add R2 += R1 set R2 → *b
Avec restrict:
void fr(int *restrict a, int *restrict b, int *restrict x);
Pseudo assemblage:
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" is no longer needed. load R2 ← *b add R2 += R1 set R2 → *b
Est-ce que GCC le fait vraiment?
g++
4.8 Linux x86-64:g++ -g -std=gnu++98 -O0 -c main.cpp objdump -S main.o
Avec
-O0
, ce sont les mêmes.Avec
-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)
Pour les non-initiés, la convention d'appel est:
rdi
= premier paramètrersi
= deuxième paramètrerdx
= troisième paramètreLa sortie de GCC était encore plus claire que l'article du wiki: 4 instructions contre 3 instructions.
Tableaux
Jusqu'à présent, nous avons des économies d'instructions uniques, mais si le pointeur représente des tableaux à boucler, un cas d'utilisation courant, alors un tas d'instructions pourrait être enregistré, comme mentionné par supercat et michael .
Considérez par exemple:
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; } }
En raison de
restrict
, un compilateur intelligent (ou humain) pourrait optimiser cela pour:memset(p1, 4, size); memset(p2, 9, size);
Ce qui est potentiellement beaucoup plus efficace car il peut être optimisé pour l'assemblage sur une implémentation libc décente (comme la glibc) Est-il préférable d'utiliser std :: memcpy () ou std :: copy () en termes de performances? , éventuellement avec des instructions SIMD .
Sans, restreignez, cette optimisation ne pourrait pas être faite, par exemple, considérez:
char p1[4]; char *p2 = &p1[1]; f(p1, p2, 3);
Ensuite, la
for
version fait:p1 == {4, 4, 4, 9}
tandis que la
memset
version fait:p1 == {4, 9, 9, 9}
Est-ce que GCC le fait vraiment?
GCC 5.2.1.Linux x86-64 Ubuntu 15.10:
gcc -g -std=c99 -O0 -c main.c objdump -dr main.o
Avec
-O0
, les deux sont identiques.Avec
-O3
:avec restreindre:
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
Deux
memset
appels comme prévu.sans restriction: pas d'appels stdlib, juste une boucle large de 16 itérations que je n'ai pas l'intention de reproduire ici :-)
Je n'ai pas eu la patience de les comparer, mais je pense que la version restrictive sera plus rapide.
Règle d'aliasing stricte
Le
restrict
mot-clé n'affecte que les pointeurs de types compatibles (par exemple deuxint*
) car les règles strictes d'aliasing indiquent que l'aliasing des types incompatibles est un comportement indéfini par défaut, et les compilateurs peuvent donc supposer que cela ne se produit pas et s'optimiser.Voir: Quelle est la règle stricte d'aliasing?
Cela fonctionne-t-il pour les références?
Selon la documentation du GCC, il le fait: https://gcc.gnu.org/onlinedocs/gcc-5.1.0/gcc/Restricted-Pointers.html avec la syntaxe:
int &__restrict__ rref
Il existe même une version pour
this
les fonctions membres:void T::fn () __restrict__
la source
-fno-strict-aliasing
, alorsrestrict
ne devrait faire aucune différence entre les pointeurs du même type ou de types différents, non? (Je fais référence à "Le mot clé restrict n'affecte que les pointeurs de types compatibles")restrict
signifie quelque chose en C ++. Si vous appelez une fonction de bibliothèque C avec desrestrict
paramètres d'un programme C ++, vous devez obéir aux implications de cela. Fondamentalement, sirestrict
est utilisé dans une API de bibliothèque C, cela signifie quelque chose pour quiconque l'appelle depuis n'importe quel langage, y compris le FFI dynamique de Lisp.Rien. Il a été ajouté à la norme C99.
la source
restrict
comme mot clé. Ma réponse est donc correcte. Ce que vous décrivez est un comportement spécifique à l'implémentation et quelque chose sur lequel vous ne devriez pas vraiment vous fier.Il s'agit de la proposition initiale d'ajout de ce mot clé. Comme l'a souligné dirkgently, il s'agit d'une fonctionnalité C99 ; cela n'a rien à voir avec C ++.
la source
__restrict__
mot - clé qui est identique pour autant que je sache.restrict
. Le comportement du programme C ++ devient indéfini s'il enfreint les restrictions impliquées parrestrict
.restrict
mot clé. Bien sûr, si vous passez des pointeurs aliasés à une fonction C qui les déclare restreints (ce que vous pouvez faire à partir de C ++ ou C), alors ils ne sont pas définis, mais c'est à vous.restrict
. Le comportement du programme C ++ devient indéfini s'il enfreint les restrictions impliquées par restrict. Mais cela n'a en fait rien à voir avec C ++, car c'est "sur vous".Étant donné que les fichiers d'en-tête de certaines bibliothèques C utilisent le mot-clé, le langage C ++ devra faire quelque chose ... au minimum, en ignorant le mot-clé, nous n'avons donc pas à #définir le mot-clé sur une macro vide pour supprimer le mot-clé .
la source
extern C
déclaration, soit en la supprimant silencieusement, comme c'est le cas avec le compilateur AIX C / C ++, qui gère à la place le__rerstrict__
mot - clé. Ce mot-clé est également pris en charge sous gcc afin que le code compile le même sous g ++.Il n'y a pas de tel mot-clé en C ++. La liste des mots-clés C ++ se trouve dans la section 2.11 / 1 de la norme du langage C ++.
restrict
est un mot clé de la version C99 du langage C et non du C ++.la source
__restrict__
mot - clé qui est identique pour autant que je sache.