Quelles sont la sémantique de « restreindre » de C99 en ce qui concerne des pointeurs vers des pointeurs?

StackOverflow https://stackoverflow.com/questions/1449823

Question

Je suis en train de faire beaucoup d'arithmétique de matrice et je voudrais profiter de qualification de pointeur restrict de C99.

Je voudrais configurer mes matrices comme des pointeurs vers des pointeurs pour permettre subscripting facile, comme ceci:

int **A = malloc (ncols * sizeof(int *));
A[0] = malloc (nrows * ncols * sizof(int));
for (int i=1; i < ncols; i++) {
    A[i] = A[0] + i*nrows;
}

Maintenant, pour une fonction de multiplication matricielle

void mmultiply ( int nrows, int ncols, int **Out, int **A, int **B);

dois-je qualifier les deux pointeurs des arguments restreint? Il est une syntaxe valide, mais je suis un moment difficile de déterminer si int *restrict *restrict se comporte différemment de int **restrict.

Ensuite, avec les pointeurs correctement restreints, est l'accès aux éléments par le biais A[0][col*nrows + row] non défini? (C.-à-vous le compilateur supposer que I que accéder à la matrice par A[col][row] pour les valeurs de row telles que row < nrow)? Ou dois-je simplement rester cohérent?

Était-ce utile?

La solution

Pour la première question, « oui », cela signifie quelque chose de différent si vous utilisez les deux qualificatifs de restrict, en particulier, que les pointeurs aussi ne seront pas aliasées. Quant à savoir si cela fait une différence: en théorie, oui, dans la pratique, cela dépend de l'optimiseur

.

Pour la deuxième question, « oui », il supposera que tout accessible via un pointeur de ligne est accessible uniquement par le pointeur de la ligne.

Vous pouvez jeter const là aussi.

Enfin, si cela est gcc à -O2, ou -OS -O3, le compilateur fait déjà une analyse d'alias basée sur les types. Je suis sûr que d'autres compilateurs font aussi. Cela signifie que limiter les pointeurs contre les ints est déjà compris, ne laissant que les tableaux qui pourraient éventuellement stocker les uns aux autres.

En somme, l'optimiseur suppose que les pointeurs ne sont pas stockés dans comme ints, et il sait qu'il ne fait rien de pointeur écrit pendant la boucle.

Alors, vous obtiendrez probablement le même code avec seulement une limite.

Autres conseils

extérieure (seconde) restreindre indique au compilateur qu'aucune des tableaux de pointeurs (A, B, et à l'extérieur) alias. L'intérieur (premier) restreindre indique au compilateur qu'aucune des matrices de ints (pointée par les éléments des tableaux de pointeurs) alias.

Si vous accédez à la fois A [0] [* col nrows + ligne] et A [col] [suite] vous violez la intérieure restreindre, donc les choses pourraient se briser.

int **restrict affirme seulement que la mémoire adressée par Out, A et B ne se chevauchent pas (sauf que A et B peuvent se chevaucher, en supposant que votre fonction ne modifie pas l'un d'eux). Cela signifie que les tableaux de pointeurs. Il ne revendique rien sur le contenu de la mémoire pointée par Out, A et B. Note de bas de 117 n1124 dit:

  

p si l'identificateur est de type (int   ** restreindre), puis les expressions de pointeur p et p + 1 sont basées sur la   objet pointeur restreint désigné   par p, mais les expressions de pointeur * p   et p [1] ne sont pas.

Par analogie avec const, je pense que la qualification avec restrict sera deux fois affirmer ce que vous voulez, qui est qu'aucune des valeurs des points de tableau à la mémoire qui se chevauchent. Mais la lecture de la norme, je ne peux pas me prouver qu'il fait en réalité. Je pense que « Soit D une déclaration d'un identificateur commun qui fournit un moyen de désignation d'un objet P comme un pointeur restreindre qualifié de type T » ne signifie en effet que, pour int *restrict *restrict A, alors A [0] et un [1] sont objets désignés comme un pointeur restrict-qualifié pour int. Mais il est assez lourd jargon juridique.

Je ne sais pas si votre compilateur ne fait quoi que ce soit avec cette connaissance, vous l'esprit. De toute évidence, il pourrait, il est une question de savoir si elle est mise en œuvre.

Je ne sais pas vraiment ce que vous avez gagné sur un C-D 2 tableau classique, où vous venez allouez rows * cols * sizeof(int) et index avec A[cols*row + col]. Ensuite, vous avez seulement besoin clairement une utilisation de restreindre, et tout compilateur qui fait quoi que ce soit avec restrict sera en mesure de réordonner lit de A et B écrit sur Out. Sans restrict, bien sûr, il ne peut pas, donc en faisant ce que vous faites, vous vous jeter sur la miséricorde de votre compilateur. Si elle ne peut pas faire face à double limite, seule la seule restreindre le cas, votre double indirection vous a coûté l'optimisation.

A première estimation, la multiplication est susceptible d'être plus rapide qu'un pointeur supplémentaire indirection de toute façon. Vous souciez évidemment sur la performance ou vous ne comptez pas utiliser limiter du tout, donc je tester les performances assez bien (sur tous les compilateurs que vous aimez) avant de faire ce changement pour le bien de la syntaxe un peu plus agréable et ne pas avoir à se rappeler combien colonnes il y a dans votre tableau chaque fois que vous y accédez.

accède par l'intermédiaire des éléments A [0] [col * + nrows ligne] undefined?

Oui, si l'élément est modifié par l'un des accès, car cela rend A [0] un alias de mémoire également accessible par un [col]. Ce serait bien si seulement A et B étaient des indicateurs de limiter qualifiés, mais pas si A [0] et A [col] sont.

Je suppose que vous ne modifiez pas A cette fonction, donc en fait que l'alias est très bien. Si vous avez fait la même chose avec Out, cependant, le comportement serait indéfini.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top