registro - requisitos para patentar una marca
"Registrar" palabra clave en C? (17)
¿Qué hace la palabra clave de register
en lenguaje C? He leído que se utiliza para optimizar, pero no está claramente definido en ningún estándar. ¿Sigue siendo relevante y, si es así, cuándo lo usarías?
He leído que se utiliza para optimizar, pero no está claramente definido en ningún estándar.
De hecho está claramente definido por el estándar C. Citando el borrador N1570 sección 6.7.1 párrafo 6 (otras versiones tienen la misma redacción):
Una declaración de un identificador para un objeto con
register
especificador de clase de almacenamiento sugiere que el acceso al objeto sea lo más rápido posible. La medida en que dichas sugerencias son efectivas está definida por la implementación.
El operador unario &
no se puede aplicar a un objeto definido con register
, y el register
no se puede usar en una declaración externa.
Hay algunas otras reglas (bastante oscuras) que son específicas para register
objetos calificados:
-
La definición de un objeto de matriz conregister
tiene un comportamiento indefinido.
Corrección: es legal definir un objeto de matriz con elregister
, pero no puede hacer nada útil con dicho objeto (la indexación en una matriz requiere tomar la dirección de su elemento inicial). - El especificador
_Alignas
(nuevo en C11) no puede aplicarse a dicho objeto. - Si el nombre del parámetro pasado a la macro
va_start
estáregister
calificado, el comportamiento es indefinido.
Puede haber algunos otros; descargue un borrador del estándar y busque "registrarse" si está interesado.
Como su nombre lo indica, el significado original de register
era requerir que un objeto se almacenara en un registro de CPU. Pero con las mejoras en la optimización de compiladores, esto se ha vuelto menos útil. Las versiones modernas del estándar C no hacen referencia a los registros de la CPU, ya que ya no (tienen que asumir) que existe tal cosa (hay arquitecturas que no usan registros). La opinión general es que aplicar el register
a una declaración de objeto es más probable que empeore el código generado, ya que interfiere con la asignación del registro del compilador. Es posible que haya algunos casos en los que sea útil (por ejemplo, si realmente sabe con qué frecuencia se accederá a una variable y su conocimiento es mejor que lo que un compilador de optimización moderno puede descubrir).
El principal efecto tangible del register
es que evita cualquier intento de tomar la dirección de un objeto. Esto no es particularmente útil como una sugerencia de optimización, ya que se puede aplicar solo a las variables locales, y un compilador de optimización puede ver por sí mismo que la dirección de tal objeto no se toma.
¡Tiempo de cuentos!
C, como lenguaje, es una abstracción de una computadora. Te permite hacer cosas, en términos de lo que hace una computadora, que es manipular la memoria, hacer matemáticas, imprimir cosas, etc.
Pero C es solo una abstracción. Y en última instancia, lo que está extrayendo de ti es el lenguaje ensamblador. Ensamblaje es el lenguaje que lee una CPU, y si lo usas, haces cosas en términos de la CPU. ¿Qué hace una CPU? Básicamente, lee de la memoria, hace matemáticas y escribe en la memoria. La CPU no solo hace matemáticas en los números en la memoria. Primero, tiene que mover un número de la memoria a la memoria dentro de la CPU llamada registro . Una vez que haya terminado de hacer lo que tenga que hacer con este número, puede volver a la memoria normal del sistema. ¿Por qué usar la memoria del sistema? Los registros son limitados en número. Solo tiene unos cien bytes en los procesadores modernos, y los procesadores populares más antiguos estaban aún más limitados (el 6502 tenía 3 registros de 8 bits para su uso gratuito). Entonces, tu operación matemática promedio se ve como:
load first number from memory
load second number from memory
add the two
store answer into memory
Mucho de eso es ... no matemáticas. Esas operaciones de carga y almacenamiento pueden demorar hasta la mitad del tiempo de procesamiento. C, al ser una abstracción de las computadoras, liberó al programador la preocupación de usar y hacer malabares con los registros, y dado que el número y el tipo varían entre las computadoras, C coloca la responsabilidad de la asignación de registros únicamente en el compilador. Con una excepcion.
Cuando declara un register
variable, le está diciendo al compilador "Yo, tengo la intención de que esta variable se use mucho y / o sea de corta duración. Si yo fuera usted, trataría de mantenerlo en un registro". Cuando el estándar C dice que los compiladores no tienen que hacer nada en realidad, eso se debe a que el estándar C no sabe para qué computadora se está compilando, y podría ser como el 6502 anterior, donde se necesitan los 3 registros solo para operar , y no hay registro de repuesto para mantener su número. Sin embargo, cuando dice que no puede tomar la dirección, eso se debe a que los registros no tienen direcciones. Son las manos del procesador. Dado que el compilador no tiene que darle una dirección, y como no puede tener una dirección en absoluto, ahora hay varias optimizaciones abiertas para el compilador. Podría, digamos, mantener el número en un registro siempre. No tiene que preocuparse por dónde se almacena en la memoria de la computadora (más allá de la necesidad de recuperarla). Incluso podría convertirlo en otra variable, dárselo a otro procesador, darle una ubicación cambiante, etc.
tl; dr: variables de corta duración que hacen mucha matemática. No declares demasiados a la vez.
Durante los años setenta, al comienzo del lenguaje C, se introdujo la palabra clave register para permitir al programador dar pistas al compilador, diciéndole que la variable se usaría muy a menudo, y que debería ser prudente mantener su valor en uno de los registros internos del procesador.
Hoy en día, los optimizadores son mucho más eficientes que los programadores para determinar las variables que tienen más probabilidades de mantenerse en los registros, y el optimizador no siempre toma en cuenta la sugerencia del programador.
Mucha gente recomienda erróneamente no usar la palabra clave de registro.
¡A ver por qué!
La palabra clave de registro tiene un efecto secundario asociado: no puede hacer referencia (obtener la dirección de) una variable de tipo de registro.
Las personas que aconsejan a otros que no utilicen registros toman erróneamente este argumento como un argumento adicional.
Sin embargo, el simple hecho de saber que no puede tomar la dirección de una variable de registro, permite que el compilador (y su optimizador) sepan que el valor de esta variable no puede modificarse indirectamente a través de un puntero.
Cuando en un cierto punto de la secuencia de instrucciones, una variable de registro tiene su valor asignado en el registro de un procesador, y el registro no se ha utilizado ya que para obtener el valor de otra variable, el compilador sabe que no necesita volver a cargar El valor de la variable en ese registro. Esto permite evitar costosos accesos de memoria inútiles.
Haga sus propias pruebas y obtendrá mejoras significativas de rendimiento en sus bucles más internos.
El compilador de Visual C ++ de Microsoft ignora la palabra clave de register
cuando la optimización global de asignación de registros (el indicador del compilador / Oe) está habilitada.
Ver registro de palabras clave en MSDN.
El registro indica al compilador que optimice este código almacenando esa variable en particular en los registros y luego en la memoria. es una solicitud al compilador, el compilador puede o no considerar esta solicitud. Puede utilizar esta función en caso de que se acceda a algunas de sus variables con mucha frecuencia. Por ejemplo: un bucle.
Una cosa más es que si declara una variable como registro, no podrá obtener su dirección ya que no está almacenada en la memoria. Obtiene su asignación en el registro de la CPU.
El registro notificaría al compilador que el codificador creía que esta variable se escribiría / leería lo suficiente como para justificar su almacenamiento en uno de los pocos registros disponibles para el uso de la variable. La lectura / escritura de los registros suele ser más rápida y puede requerir un conjunto de códigos de operación más pequeño.
Hoy en día, esto no es muy útil, ya que la mayoría de los optimizadores de los compiladores son mejores que usted para determinar si se debe usar un registro para esa variable y por cuánto tiempo.
En los compiladores de C compatibles, intenta optimizar el código para que el valor de la variable se mantenga en un registro de procesador real.
En realidad, el registro le dice al compilador que la variable no hace alias con nada más en el programa (ni siquiera los de carácter).
Esto puede ser explotado por los compiladores modernos en una variedad de situaciones, y puede ayudar al compilador bastante en código complejo; en código simple, los compiladores pueden resolver esto por su cuenta.
De lo contrario, no sirve para nada y no se utiliza para la asignación de registros. No suele incurrir en una degradación del rendimiento para especificarlo, siempre que su compilador sea lo suficientemente moderno.
Es una sugerencia para el compilador que la variable será muy utilizada y que recomiende que se mantenga en un registro del procesador si es posible.
La mayoría de los compiladores modernos lo hacen automáticamente, y son mejores para escogerlos que los humanos.
Estás jugando con el sofisticado algoritmo de coloración de gráficos del compilador. Esto se utiliza para la asignación de registros. Bueno, en su mayoría. Actúa como una sugerencia para el compilador, es cierto. Pero no se ignora en su totalidad ya que no se le permite tomar la dirección de una variable de registro (recuerde que el compilador, ahora a su merced, tratará de actuar de manera diferente). Lo que de alguna manera te está diciendo que no lo uses.
La palabra clave fue utilizada hace mucho, mucho tiempo atrás. Cuando había tan pocos registros que podían contarlos todos usando su dedo índice.
Pero, como dije, en desuso no significa que no puedas usarlo.
He probado la palabra clave de registro bajo QNX 6.5.0 usando el siguiente código:
#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
#include <sys/neutrino.h>
#include <sys/syspage.h>
int main(int argc, char *argv[]) {
uint64_t cps, cycle1, cycle2, ncycles;
double sec;
register int a=0, b = 1, c = 3, i;
cycle1 = ClockCycles();
for(i = 0; i < 100000000; i++)
a = ((a + b + c) * c) / 2;
cycle2 = ClockCycles();
ncycles = cycle2 - cycle1;
printf("%lld cycles elapsed/n", ncycles);
cps = SYSPAGE_ENTRY(qtime) -> cycles_per_sec;
printf("This system has %lld cycles per second/n", cps);
sec = (double)ncycles/cps;
printf("The cycles in seconds is %f/n", sec);
return EXIT_SUCCESS;
}
Obtuve los siguientes resultados:
-> 807679611 ciclos transcurridos
-> Este sistema tiene 3300830000 ciclos por segundo
-> Los ciclos en segundos son ~ 0.244600
Y ahora sin registro int:
int a=0, b = 1, c = 3, i;
Tengo:
-> 1421694077 ciclos transcurridos
-> Este sistema tiene 3300830000 ciclos por segundo
-> Los ciclos en segundos son ~ 0.430700
La palabra clave de registro le dice al compilador que almacene la variable en particular en los registros de la CPU para que pueda ser accesible rápidamente. Desde el punto de vista del programador, la palabra clave de registro se usa para las variables que se usan mucho en un programa, de modo que el compilador puede acelerar el código. Aunque depende del compilador si mantener la variable en los registros de la CPU o en la memoria principal.
Le dice al compilador que intente usar un registro de CPU, en lugar de RAM, para almacenar la variable. Los registros están en la CPU y son mucho más rápidos de acceder que la memoria RAM. Pero es solo una sugerencia para el compilador, y puede que no siga adelante.
Me sorprende que nadie mencione que no puede tomar una dirección de variable de registro, incluso si el compilador decide mantener la variable en la memoria en lugar de en el registro.
Por lo tanto, al usar el register
no se gana nada (de todos modos el compilador decidirá por sí mismo dónde colocar la variable) y perderá el operador &
, no hay razón para usarlo.
No ha sido relevante durante al menos 15 años, ya que los optimizadores toman mejores decisiones sobre esto que usted. Incluso cuando era relevante, tenía mucho más sentido en una arquitectura de CPU con muchos registros, como SPARC o M68000 que en Intel con su escasez de registros, la mayoría de los cuales están reservados por el compilador para sus propios fines.
Sé que esta pregunta es sobre C, pero la misma pregunta para C ++ se cerró como un duplicado exacto de esta pregunta. Esta respuesta, por lo tanto, no puede aplicar para C.
El último borrador de la norma C ++ 11, N3485 , dice esto en 7.1.1 / 3:
Un especificador de
register
es una sugerencia para la implementación de que la variable así declarada será muy utilizada. [ nota: la sugerencia se puede ignorar y en la mayoría de las implementaciones se ignorará si se toma la dirección de la variable. Este uso está en desuso ...
En C ++ (pero no en C), el estándar no establece que no puede tomar la dirección de un register
declarado variable; sin embargo, debido a que una variable almacenada en un registro de CPU a lo largo de su vida útil no tiene una ubicación de memoria asociada, el intento de tomar su dirección sería inválido, y el compilador ignorará la palabra clave de register
para permitir tomar la dirección.
Solo una pequeña demostración (sin ningún propósito del mundo real) para comparación: al eliminar las palabras clave de register
antes de cada variable, esta pieza de código toma 3.41 segundos en mi i7 (GCC), con el mismo código completado en 0.7 segundos.
#include <stdio.h>
int main(int argc, char** argv) {
register int numIterations = 20000;
register int i=0;
unsigned long val=0;
for (i; i<numIterations+1; i++)
{
register int j=0;
for (j;j<i;j++)
{
val=j+i;
}
}
printf("%d", val);
return 0;
}