que punteros puntero parametros funciones estructura ejemplo dev declaracion datos como cadenas aritmetica apuntadores c++ c optimization restrict-qualifier

c++ - punteros - ¿Restringe la ayuda en C si un puntero ya está marcado const?



punteros como parametros de funciones en c (5)

Su puntero es constante, diciéndole a cualquiera que llame a su función que no tocará los datos a los que se apunta a través de esa variable. Desafortunadamente, el compilador aún no sabrá si el resultado es un alias de los punteros const. Siempre puede usar un puntero no const como const-pointer. Por ejemplo, muchas funciones toman un puntero const char (es decir, cadena) como parámetro, pero puede, si lo desea, pasarle un puntero que no sea const, la función es simplemente hacer una promesa de que no usará ese particular puntero para cambiar cualquier cosa.

Básicamente, para acercarte a tu pregunta, deberías agregar restringir a ayb para ''prometer'' al compilador que quien usa esta función no pasará el resultado como un alias a a o b. Suponiendo, por supuesto, que puedas hacer tal promesa.

Solo me pregunto: cuando agrego restringir a un puntero, le digo al compilador que el puntero no es un alias para otro puntero. Supongamos que tengo una función como:

// Constructed example void foo (float* result, const float* a, const float* b, const size_t size) { for (size_t i = 0; i < size; ++i) { result [i] = a [0] * b [i]; } }

Si el compilador tiene que suponer que el result puede superponerse con a , debe volver a buscarlo cada vez. Pero, como a se marca const , el compilador también podría suponer que a está fijo y, por lo tanto, recuperarlo una vez está bien.

La pregunta es, en una situación como esta, ¿cuál es la forma recomendada de trabajar con Restringir? Seguramente no quiero que el compilador vuelva a buscar cada vez, pero no pude encontrar buena información sobre cómo se supone que debe funcionar el restrict aquí.


Como se indicó en la respuesta anterior, debe agregar "restringir". También quería comentar su escenario de que "el resultado podría superponerse con un". Esa no es la única razón por la que el compilador detectará que "a" podría cambiar. También podría ser cambiado por otro hilo que tenga un puntero a "a". Por lo tanto, incluso si su función no cambió ningún valor, el compilador asumirá que "a" podría cambiar.


En el estándar C-99 (ISO / IEC 9899: 1999 (E)) hay ejemplos de const * restrict de const * restrict , por ejemplo, en la sección 7.8.2.3:

Las funciones strtoimax y strtoumax

Sinopsis

#include <inttypes.h> intmax_t strtoimax(const char * restrict nptr, char ** restrict endptr, int base); --- snip ---

Por lo tanto, si se supone que el estándar no proporcionaría un ejemplo si const * fuera redundante para * restrict , entonces, de hecho, no es redundante.


Todos aquí parecen muy confundidos. No hay un solo ejemplo de un puntero de const en ninguna respuesta hasta el momento.

La declaración const float* a no es un puntero const, es const storage. El puntero sigue siendo mutable. float *const a es un puntero const para un float mutable.

Entonces la pregunta debería ser, ¿hay algún punto en float *const restrict a (o const float *const restrict a si lo prefiere).


Sí, necesitas restringir Pointer-to-const no significa que nada puede cambiar los datos, solo que no puede cambiarlo a través de ese puntero .

const es principalmente un mecanismo para pedirle al compilador que lo ayude a realizar un seguimiento de las cosas que desea modificar. const no es una promesa para el compilador de que una función realmente no modificará los datos .

A diferencia de restrict , usar puntero-a- const para datos mutables es básicamente una promesa para otros humanos, no para el compilador. La eliminación de const todo el lugar no conducirá a un comportamiento incorrecto del optimizador (AFAIK), a menos que intente modificar algo que el compilador coloca en la memoria de solo lectura (consulte a continuación las variables static const ). Si el compilador no puede ver la definición de una función al optimizar, tiene que suponer que descarta const y modifica los datos a través de ese puntero (es decir, que la función no respeta la const de sus arcos de puntero).

El compilador sabe que static const int foo = 15; no obstante, no puede cambiar y alineará de forma confiable el valor incluso si pasa su dirección a funciones desconocidas. (Esta es la razón por static const int foo = 15; cual static const int foo = 15; no es más lento que #define foo 15 para un compilador optimizado. Los buenos compiladores lo optimizarán como un constexpr siempre que sea posible).

Recuerde que restrict es una promesa para el compilador de que las cosas a las que accede a través de ese puntero no se superponen con ninguna otra cosa . Si eso no es cierto, su función no necesariamente hará lo que espera. por ejemplo, no llame a foo_restrict(buf, buf, buf) para operar en el lugar.

En mi experiencia (con gcc y clang), restrict es principalmente útil en los punteros que almacenas. No está de más poner restrict en los punteros de origen, también, pero normalmente obtienes todo el mejoramiento de asm posible al colocarlo solo en el (los) puntero (s) de destino, si todas las tiendas que tu función tiene son restrict por punteros.

Si tiene alguna función llamada en su ciclo, restrict en un puntero de fuente deja que clang (pero no gcc) evite una recarga. Vea estos casos de prueba en el explorador del compilador Godbolt , específicamente este:

void value_only(int); // a function the compiler can''t inline int arg_pointer_valonly(const int *__restrict__ src) { // the compiler needs to load `*src` to pass it as a function arg value_only(*src); // and then needs it again here to calculate the return value return 5 + *src; // clang: no reload because of __restrict__ }

gcc6.3 (dirigido a x86-64 SysV ABI) decide mantener src (el puntero) en un registro conservado de llamada a través de la llamada de función, y volver a cargar *src después de la llamada. O bien los algoritmos de gcc no detectaron esa posibilidad de optimización, o decidieron que no valía la pena, o los desarrolladores de gcc a propósito no lo implementaron porque creen que no es seguro. IDK que. Pero como clang lo hace, supongo que probablemente sea legal según el estándar C11.

clang4.0 optimiza esto para solo cargar *src una vez, y mantener el valor en un registro preservado de llamada a través de la llamada de función. Sin restrict , no hace esto, porque la función llamada podría (como un efecto secundario) modificar *src través de otro puntero.

La persona que llama de esta función podría haber pasado la dirección de una variable global, por ejemplo . Pero cualquier modificación de *src no sea a través del puntero src violaría la promesa de restrict al compilador. Como no pasamos src a valonly() , el compilador puede suponer que no modifica el valor.

El dialecto GNU de C permite usar __attribute__((pure)) o __attribute__((const)) para declarar que una función no tiene efectos secundarios , permitiendo esta optimización sin restrict , pero no hay un equivalente portátil en ISO C11 (AFAIK). Por supuesto, permitir que la función se alinee (al colocarla en un archivo de encabezado o usar LTO) también permite este tipo de optimización, y es mucho mejor para las funciones pequeñas, especialmente si se llama dentro de los bucles.

Los compiladores generalmente son bastante agresivos con las optimizaciones que permite el estándar, incluso si sorprenden a algunos programadores y rompen algunos códigos inseguros que funcionaban. (C es tan portátil que muchas cosas son un comportamiento indefinido en el estándar base, la mayoría de las implementaciones buenas definen el comportamiento de muchas cosas que el estándar deja como UB). C no es un lenguaje donde es seguro lanzar código al compilador hasta hace lo que quiere, sin verificar que lo está haciendo de la manera correcta (sin desbordamientos de enteros firmados, etc.)

Si observa la salida de asma x86-64 para compilar su función (de la pregunta), puede ver fácilmente la diferencia. Lo puse en el explorador del compilador Godbolt .

En este caso, poner restrict en a es suficiente para permitir que el clang izar la carga de a[0] , pero no gcc.

Con el resultado de float *restrict result , tanto clang como gcc elevarán la carga.

p.ej

# gcc6.3, for foo with no restrict, or with just const float *restrict a .L5: vmovss xmm0, DWORD PTR [rsi] vmulss xmm0, xmm0, DWORD PTR [rdx+rax*4] vmovss DWORD PTR [rdi+rax*4], xmm0 add rax, 1 cmp rcx, rax jne .L5

vs.

# gcc 6.3 with float *__restrict__ result # clang is similar with const float *__restrict__ a but not on result. vmovss xmm1, DWORD PTR [rsi] # outside the loop .L11: vmulss xmm0, xmm1, DWORD PTR [rdx+rax*4] vmovss DWORD PTR [rdi+rax*4], xmm0 add rax, 1 cmp rcx, rax jne .L11

En resumen, ponga __restrict__ en todos los punteros que garanticen que no se superpongan con otra cosa .

Por cierto, restrict es solo una palabra clave en C. Algunos compiladores C ++ admiten __restrict__ o __restrict como una extensión, por lo que debe #ifdef en compiladores desconocidos.

Ya que