Estaba buscando documentación y preguntas / respuestas y la vi mencionada. Leí una breve descripción, afirmando que sería básicamente una promesa del programador que el puntero no se usará para apuntar a otro lado.
¿Alguien puede ofrecer algunos casos realistas en los que valga la pena usar esto?
c
gcc
c99
restrict-qualifier
user90052
fuente
fuente
memcpy
vsmemmove
es un ejemplo canónico.restrict
-calificar argumentos paramemcpy
permitir en principio una implementación ingenua para ser optimizada agresivamente, y 2) simplemente llamarmemcpy
permite al compilador suponer que los argumentos que se le dan no tienen alias, lo que podría permitir cierta optimización en torno a lamemcpy
llamada.memcpy(anything, anything, 0);
como un no-op, y asegurar que sip
es un puntero a al menosn
bytes de escrituramemcpy(p,p,n)
,; no tendrá efectos secundarios adversos. Tales casos pueden surgir ...Respuestas:
restrict
dice que el puntero es lo único que accede al objeto subyacente. Elimina el potencial de alias de puntero, permitiendo una mejor optimización por parte del compilador.Por ejemplo, supongamos que tengo una máquina con instrucciones especializadas que pueden multiplicar vectores de números en la memoria, y tengo el siguiente código:
El compilador debe manejar adecuadamente if
dest
,src1
ysrc2
superponerse, lo que significa que debe hacer una multiplicación a la vez, de principio a fin. Al tenerrestrict
, el compilador es libre de optimizar este código utilizando las instrucciones vectoriales.Wikipedia tiene una entrada
restrict
, con otro ejemplo, aquí .fuente
dest
se superponga cualquiera de los vectores de origen. ¿Por qué habría un problema sisrc1
ysrc2
se superponen?El ejemplo de Wikipedia es muy esclarecedor.
Muestra claramente cómo permite guardar una instrucción de ensamblaje .
Sin restricción:
Pseudo ensamblaje:
Con restringir:
Pseudo ensamblaje:
¿GCC realmente lo hace?
GCC 4.8 Linux x86-64:
Con
-O0
, son lo mismo.Con
-O3
:Para los no iniciados, la convención de convocatoria es:
rdi
= primer parámetrorsi
= segundo parámetrordx
= tercer parámetroLa salida de GCC fue incluso más clara que el artículo wiki: 4 instrucciones vs 3 instrucciones.
Matrices
Hasta ahora, tenemos ahorros en una sola instrucción, pero si el puntero representa las matrices que se van a recorrer, un caso de uso común, entonces se podrían guardar un montón de instrucciones, como menciona supercat .
Considere por ejemplo:
Debido a que
restrict
un compilador inteligente (o humano), podría optimizar eso para:que es potencialmente mucho más eficiente, ya que puede ser un ensamblaje optimizado en una implementación decente de libc (como glibc): ¿Es mejor usar std :: memcpy () o std :: copy () en términos de rendimiento?
¿GCC realmente lo hace?
GCC 5.2.1.Linux x86-64 Ubuntu 15.10:
Con
-O0
ambos son lo mismo.Con
-O3
:con restricción:
Dos
memset
llamadas como se esperaba.sin restricción: no hay llamadas stdlib, solo un desenrollamiento de bucle ancho de 16 iteraciones que no pretendo reproducir aquí :-)
No he tenido la paciencia para compararlos, pero creo que la versión restringida será más rápida.
C99
Veamos el estándar por completo.
restrict
dice que dos punteros no pueden apuntar a regiones de memoria superpuestas. El uso más común es para argumentos de función.Esto restringe cómo se puede llamar a la función, pero permite más optimizaciones en tiempo de compilación.
Si la persona que llama no sigue el
restrict
contrato, comportamiento indefinido.El C99 N1256 borrador 6.7.3 / 7 "Calificadores de tipo" dice:
y 6.7.3.1 "Definición formal de restricción" da los detalles sangrientos.
Regla de alias estricta
La
restrict
palabra clave solo afecta a punteros de tipos compatibles (por ejemplo, dosint*
) porque las estrictas reglas de alias dicen que el alias de tipos incompatibles es un comportamiento indefinido por defecto, por lo que los compiladores pueden asumir que no sucede y optimizar.Ver: ¿Cuál es la estricta regla de alias?
Ver también
restrict
, pero GCC tiene__restrict__
como extensión: ¿Qué significa la palabra clave restringir en C ++?__attribute__((malloc))
, que dice que el valor de retorno de una función no tiene un alias para nada: GCC: __attribute __ ((malloc))fuente
void zap(char *restrict p1, char *restrict p2) { for (int i=0; i<50; i++) { p1[i] = 4; p2[i] = 9; } }
, los calificadores de restricción permitirían al compilador reescribir el código como "memset (p1,4,50); memset (p2,9,50);". La restricción es muy superior al alias basado en tipo; Es una pena que los compiladores se centren más en lo último.__restrict
. De lo contrario, los subrayados dobles pueden malinterpretarse como una indicación de que está gritando.