Que signifie le mot-clé restrict en C++?

j'étais toujours incertain, que signifie le mot-clé restrict en C++?

signifie que les deux ou pointeur de donnée à la fonction ne se chevauchent pas? Quoi d'autre?

153
demandé sur Mat 2009-04-22 12:59:06

6 réponses

dans son article, optimisation de la mémoire , Christer Ericson dit que bien que restrict ne fasse pas encore partie de la norme C++, qu'il est supporté par de nombreux compilateurs et il recommande son utilisation quand disponible:

restreindre mot-clé

! Nouveau à la norme ANSI/ISO C 1999

! Pas encore en standard C++, mais pris en charge par de nombreux compilateurs C++

! Un conseil seulement, donc peut ne rien faire et encore conforme

restreindre l'qualifiés pointeur (ou de référence)...

! ...est fondamentalement un promesse au compilateur que pour la portée du pointeur, la cible du pointeur ne être accessible à travers ce pointeur (et les pointeurs copiés à partir d'elle).

dans les compilateurs C++ qui le supportent, il devrait probablement se comporter de la même façon que en C.

de Voir cette SI post pour plus de détails: Réaliste utilisation du C99 "limiter" mot?

prenez une demi-heure pour parcourir le journal D'Ericson, c'est intéressant et ça en vaut la peine.

Modifier

j'ai aussi trouvé que le compilateur AIX C/C++ d'IBM supporte le __restrict__ mot-clé .

g++ semble aussi supporter ceci comme le programme suivant 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 aussi trouvé un bel article sur l'utilisation de restrict :

Démystifier Le Mot-Clé Restrict

Edit2

j'ai parcouru un article qui traite spécifiquement de l'utilisation de restrict dans les programmes C++:

Load-hit-magasins et de l' __restreindre mot-clé

aussi, Microsoft Visual C++ supporte aussi le __restrict mot-clé .

123
répondu Robert S. Barnes 2017-05-23 11:47:11

comme d'autres ont dit, si signifie rien à partir de C++14 , alors considérons le __restrict__ extension GCC qui fait la même chose que le C99 restrict .

c99

restrict dit que deux pointeurs ne peuvent pas pointer vers des régions de mémoire se chevauchant. L'usage le plus courant est pour les arguments de fonction.

cela restreint la façon dont la fonction peut être appelée, mais permet plus d'optimisations de compilations.

si l'appelant ne respecte pas le contrat restrict , comportement non défini.

Le C99 N1256 projet 6.7.3/7 de Type "qualificatifs" dit:

l'utilisation prévue du qualificatif restrict (comme la register storage class) est de promouvoir l'optimisation, et de supprimer toutes les instances du qualificatif de toutes les unités de traduction de prétraitement composant un programme conforme ne change pas sa signification (c.-à-d., comportement observable).

et 6.7.3.1 "définition Formelle de restreindre" donne les détails sanglants.

une optimisation possible

le exemple Wikipédia est très illuminant.

il montre clairement comment comme il permet de sauver un ensemble instruction .

sans restriction:

void f(int *a, int *b, int *x) {
  *a += *x;
  *b += *x;
}

Pseudo assemblée:

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

avec restriction:

void fr(int *__restrict__ a, int *__restrict__ b, int *__restrict__ x);

Pseudo assemblée:

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

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 , ils 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ètre
  • rsi = second paramètre
  • rdx = troisième paramètre

la sortie GCC était encore plus claire que l'article wiki: 4 instructions vs 3 instructions.

matrices

jusqu'à présent nous avons des économies d'instruction simples, mais si pointer représentent des tableaux à boucler, un cas d'utilisation commune, alors un tas d'instructions pourrait être enregistré, comme mentionné par supercat et michael .

Considérons 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;
     }
 }

à cause de restrict , un compilateur intelligent (ou humain), pourrait optimiser cela à:

memset(p1, 4, size);
memset(p2, 9, size);

qui est potentiellement beaucoup plus efficace puisqu'il peut être optimisé pour l'assemblage sur une implémentation libc décente (comme glibc) est-il préférable d'utiliser std::memcpy() ou std::copy() en termes de performance? , éventuellement avec SIMD instructions .

sans, restreindre, cette optimisation ne pouvait pas être faite, par exemple considérer:

char p1[4];
char *p2 = &p1[1];
f(p1, p2, 3);

Puis 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 restriction:

    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    "1519120920"x4,%esi
    3ff:   48 89 d3                mov    %rdx,%rbx
    402:   48 83 ec 08             sub    "1519120920"x8,%rsp
    406:   e8 00 00 00 00          callq  40b <fr+0x1b>
                            407: R_X86_64_PC32      memset-0x4
    40b:   48 83 c4 08             add    "1519120920"x8,%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    "1519120920"x9,%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 comme prévu.

  • sans restriction: pas d'appels stdlib, juste une itération 16 large boucle déroulante que je n'ai pas l'intention de reproduire ici : -)

Je n'ai pas eu la patience de les comparer, mais je crois que la version de restriction sera plus rapide.

Strict règle d'identification

le mot-clé restrict n'affecte que les pointeurs de types compatibles (par exemple deux int* ) parce que les règles d'aliasing strictes indiquent que l'aliasing de types incompatibles est un comportement non défini par défaut, et donc les compilateurs peuvent supposer qu'il ne se produit pas et optimiser loin.

Voir: qu'est-Ce que la stricte aliasing règle?

fonctionne pour les références?

selon les documents du CCG il fait: https://gcc.gnu.org/onlinedocs/gcc-5.1.0/gcc/Restricted-Pointers.html avec syntaxe:

int &__restrict__ rref

il existe même une version pour this des fonctions des membres:

void T::fn () __restrict__
62

rien. Il a été ajouté à la norme C99.

16
répondu dirkgently 2009-04-22 09:01:59

ce est la proposition originale d'ajouter ce mot clé. Comme dirkgently l'a souligné cependant, il s'agit d'une fonctionnalité C99 ; cela n'a rien à voir avec C++.

10
répondu unwind 2015-09-22 11:02:29

il n'y a pas de mot clé dans C++. La liste des mots-clés C++ se trouve dans la section 2.11/1 de la norme de langage C++. restrict est un mot clé dans la version C99 du langage C et non dans C++.

5
répondu AnT 2015-05-28 08:20:37

puisque les fichiers d'en-tête de certaines bibliothèques C utilisent le mot-clé, le langage C++ devra faire quelque chose à ce sujet.. au minimum, en ignorant le mot-clé, de sorte que nous n'avons pas à #définir le mot-clé à une macro vide pour supprimer le mot-clé.

4
répondu Johan Boulé 2009-12-05 17:47:27