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

J’étais toujours incertain, que signifie le mot-clé ressortingct en C ++?

Est-ce que cela signifie que deux ou plusieurs pointeurs donnés à la fonction ne se chevauchent pas? Qu’est-ce que ça veut dire d’autre?

    Dans son article, Optimisation de la mémoire , Christer Ericson dit que bien que ressortingct ne fasse pas encore partie du standard C ++, il est supporté par de nombreux compilateurs et il recommande son utilisation lorsque disponible:

    restreindre le mot clé

    ! Nouvelle norme 1999 ANSI / ISO C

    ! Pas encore en standard C ++, mais supporté par de nombreux compilateurs C ++

    ! Un indice seulement, donc ne rien faire et être toujours conforme

    Un pointeur qualifié restreint (ou référence) …

    ! … est fondamentalement une promesse au compilateur que pour la scope du pointeur, la cible du pointeur ne sera accessible que via ce pointeur (et les pointeurs copiés à partir de celui-ci).

    Dans les compilateurs C ++ qui le supportent, il devrait probablement se comporter comme en C.

    Voir ce post SO pour plus de détails: Utilisation réaliste du mot clé C99 “ressortingct”?

    Prenez une demi-heure pour parcourir le papier d’Ericson, c’est intéressant et ça vaut le coup.

    modifier

    J’ai également constaté que le compilateur AIX C / C ++ d’ IBM prend en charge le mot clé __ressortingct__ .

    g ++ semble également supporter cela car le programme suivant comstack proprement sur g ++:

     #include  int foo(int * __ressortingct__ a, int * __ressortingct__ 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 ressortingct :

    Démystifier le mot-clé Ressortingct

    Edit2

    J’ai parcouru un article traitant spécifiquement de l’utilisation de ressortingct dans les programmes C ++:

    Load-hit-stores et le mot clé __ressortingct

    Microsoft Visual C ++ prend également en charge le mot clé __ressortingct .

    Comme d’autres l’ont dit, si rien ne __ressortingct__ C ++ 14 , considérons l’extension __ressortingct__ GCC qui fait la même chose que la ressortingct C99.

    C99

    ressortingct indique que deux pointeurs ne peuvent pas pointer vers des régions de mémoire superposées. L’utilisation la plus courante concerne les arguments de fonction.

    Cela restreint la manière dont la fonction peut être appelée, mais permet d’optimiser davantage la compilation.

    Si l’appelant ne suit pas le contrat ressortingct , comportement indéfini.

    Le C99 N1256 Draft 6.7.3 / 7 “Type qualifiers” dit:

    L’utilisation prévue du qualificatif ressortingct (comme la classe de stockage de registre) vise à promouvoir l’optimisation et la suppression de toutes les instances du qualificatif de toutes les unités de prétraitement composant un programme conforme ne change pas sa signification (comportement observable).

    et 6.7.3.1 “Définition formelle de ressortingction” donne les détails évidents.

    Une optimisation possible

    L’ exemple de Wikipedia est très éclairant.

    Il montre clairement comment il permet de sauvegarder une instruction d’assemblage .

    Sans ressortingction:

     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 a may be equal to x. load R1 ← *x load R2 ← *b add R2 += R1 set R2 → *b 

    Avec ressortingct:

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

    Pseudo-assemblage:

     load R1 ← *x load R2 ← *a add R2 += R1 set R2 → *a ; Note that x is not reloaded, ; because the comstackr 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 identiques.

    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 *__ressortingct__ a, int *__ressortingct__ b, int *__ressortingct__ 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 la suivante:

    • 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.

    Tableaux

    Jusqu’à présent, nous avons enregistré des économies sur une seule instruction, mais si le pointeur représente des tableaux à superposer, un cas d’utilisation courant, alors un ensemble d’instructions pourrait être enregistré, comme mentionné par supercat et michael .

    Prenons par exemple:

     void f(char *ressortingct p1, char *ressortingct p2, size_t size) { for (size_t i = 0; i < size; i++) { p1[i] = 4; p2[i] = 9; } } 

    En raison de ressortingct , 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é en assemblage sur une implémentation décente de libc (comme glibc). Est-il préférable d'utiliser std :: memcpy () ou std :: copy () en termes de performances? , éventuellement avec les instructions SIMD .

    Sans, restreindre, cette optimisation ne pourrait pas être faite, par exemple en considérant:

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

    Alors for version fait:

     p1 == {4, 4, 4, 9} 

    alors que la version memset 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 ressortingct:

       3f0: 48 85 d2 test %rdx,%rdx 3f3: 74 33 je 428  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  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  41d: R_X86_64_PC32 memset-0x4 421: 0f 1f 80 00 00 00 00 nopl 0x0(%rax) 428: f3 c3 repz retq 

      Deux appels memset comme prévu.

    • sans ressortingction: pas d'appels stdlib, juste une boucle de 16 itérations en boucle 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 ressortingctive sera plus rapide.

    Règle ssortingcte d'aliasing

    Le mot-clé ressortingct n'affecte que les pointeurs des types compatibles (par exemple, deux int* ) car les règles ssortingctes d'alias stipulent que l'aliasing des types incompatibles est un comportement indéfini par défaut. Les compilateurs peuvent donc supposer que cela ne se produit pas et optimiser.

    Voir: Quelle est la règle d'aliasing ssortingcte?

    Est-ce que ça marche pour des références?

    Selon les documents de GCC, il le fait: https://gcc.gnu.org/onlinedocs/gcc-5.1.0/gcc/Ressortingcted-Pointers.html avec la syntaxe:

     int &__ressortingct__ rref 

    Il existe même une version pour les fonctions membres:

     void T::fn () __ressortingct__ 

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

    Ceci est la proposition d’origine d’append ce mot clé. Comme dirkgently l’a souligné, il s’agit d’une fonctionnalité C99 ; cela n’a rien à voir avec C ++.

    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 du standard de langage C ++. ressortingct est un mot clé en version C99 du langage C et non en C ++.

    É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 pour y remédier. Au minimum, en ignorant le mot-clé, nous ne devrons pas #définir le mot-clé dans une macro vide. .