2009-08-25 10 views
9

Digamos que tiene una estructura como la siguiente ...los punteros de C vs acceso miembro directo de estructuras

typedef struct { 
    int WheelCount; 
    double MaxSpeed; 
} Vehicle; 

... y tengo una variable global de este tipo (Soy muy consciente de las trampas de globales, esto es para un sistema integrado, que no diseñé, y para el cual son un mal desafortunado pero necesario.) ¿Es más rápido acceder a los miembros de la estructura directamente oa través de un puntero? es decir

double LocalSpeed = MyGlobal.MaxSpeed; 

o

double LocalSpeed = pMyGlobal->MaxSpeed; 

Una de mis tareas es simplificar y fijar un sistema incorporado recientemente heredada.

+1

¿Qué le proporciona un puntero que no tiene acceso directo? Y un puntero es más lento. –

+3

-1 para preguntar acerca de la micro-optimización sin compararla primero. – bk1e

Respuesta

19

En general, yo diría que ir con la primera opción:

double LocalSpeed = MyGlobal.MaxSpeed; 

Esto tiene uno menos eliminar la referencia (que no está encontrando el puntero, entonces derreferenciándolo llegar a su ubicación). También es más simple y fácil de leer y mantener, ya que no es necesario crear la variable de puntero además de la estructura.

Habiendo dicho eso, no creo que ninguna diferencia en el rendimiento sea visible, incluso en un sistema integrado. Ambos serán tiempos de acceso muy, muy rápidos.

+0

¿Por qué los votos a favor? Me gustaría saber por qué las personas no están de acuerdo. –

+1

Estoy con Reed aquí. La referencia directa es más segura, no requiere una verificación NULL y guarda una operación de eliminación de referencias. El contenido de la memoria de la variable se debe acceder de todos modos, entonces ¿por qué hacer una búsqueda adicional? Un puntero no le gana nada si ya tiene acceso directo a la variable. –

+0

En cuanto a la desreferenciación del puntero, eso es lo que había pensado, hace mucho que no tengo que lidiar con C o C++. Gracias chicos por tu consejo. –

1

Supongo que, si esto marca la diferencia, eso dependería de la arquitectura.

1

En C, no debería haber ninguna diferencia, o un golpe de rendimiento insignificante.

estudiantes se les enseña C:

pMyGlobal->MaxSpeed == (*pMyGlobal).MaxSpeed 

usted debería ser capaz de comparar el desmontaje de los dos para convencerse de que son esencialmente los mismos, incluso si usted no es un programador Asamblea de código.

Si está buscando una optimización del rendimiento, buscaría en otro lado. No podrá ahorrar suficientes ciclos de CPU con este tipo de micro-optimización.

Por razones estilísticas, prefiero la notación de estructura y puntos, especialmente cuando se trata de singleton-globales. Me parece mucho más limpio para leer.

+1

La pregunta no es sobre -> vs *, se trata de usar un puntero en primer lugar. –

+1

La pregunta es: "¿Es más rápido acceder a los miembros de la estructura directamente oa través de un puntero?" Creo que abordé la pregunta. – abelenky

8

El primero debe ser más rápido ya que no requiere desreferenciación del puntero. Por otra parte, eso es cierto para los sistemas basados ​​en x86, no estoy seguro de los demás.

en x86 el primero se traduciría en algo como esto

mov eax, [address of MyGlobal.MaxSpeed] 

y el segundo sería algo como esto

mov ebx, [address of pMyGlobal] 
mov eax, [ebx+sizeof(int)] 
1

En general, accediendo a la estructura directamente sería más rápido, como no requerirá una desreferencia de puntero adicional. La desreferencia del puntero significa que debe tomar el puntero (la cosa en la variable), cargar lo que sea que apunte y luego operarlo.

3

En su plataforma incrustada, es probable que la arquitectura esté optimizada de tal manera que sea esencialmente un lavado, y aunque no lo fuera, solo notaría un impacto en el rendimiento si se ejecutara en un circuito muy cerrado .

Probablemente existan áreas de rendimiento mucho más obvias en su sistema.

3
struct dataStruct 
{ 
    double first; 
    double second; 
} data; 

int main() 
{ 
    dataStruct* pData = &data; 

    data.first = 9.0; 
    pData->second = 10.0; 
} 

Ésta es la salida de montaje usando el modo de disparo VS2008:

data.first = 9.0; 
008D1000 fld   qword ptr [[email protected] (8D20F0h)] 

    pData->second = 10.0; 
008D1006 xor   eax,eax 
008D1008 fstp  qword ptr [data (8D3378h)] 
008D100E fld   qword ptr [[email protected] (8D20E8h)] 
008D1014 fstp  qword ptr [data+8 (8D3380h)] 
+1

Ahora apunte a su plataforma integrada. – Alan

+0

¿Cuáles fueron los ajustes de optimización? –

+0

Esto está mal. Su primer miembro siempre ganará por definición de una estructura (el puntero de una estructura también es un puntero de un primer miembro). Usted debe comparar segundo vs segundo. –

2

desmontar, desarmar, desmontar ...

En función de las líneas de código que no nos muestran que es posible que si su puntero es algo estático, un buen compilador lo sabrá y precalculará la dirección para ambos. Si no tiene optimizaciones, entonces toda esta discusión es muda. También depende del procesador que esté utilizando, ambos se pueden realizar con una sola instrucción dependiendo del procesador. Así que sigo los pasos básicos de optimización:

1) Desmontar y examinar 2) tiempo de la ejecución

Como se mencionó anteriormente, aunque la conclusión es que puede ser un caso de dos instrucciones en lugar de uno que cuesta un solo reloj ciclo que probablemente nunca verás La calidad de las elecciones de compilador y optimizador supondrá diferencias de rendimiento mucho más dramáticas que tratar de ajustar una línea de código con la esperanza de mejorar el rendimiento. El cambio de compiladores puede proporcionarle un 10-20% en cualquier dirección, a veces más. Al igual que cambiar los indicadores de optimización, encender todo no es el código más rápido, a veces -O1 tiene un mejor rendimiento que -O3.

Entender qué producen esas dos líneas de código y cómo maximizar el rendimiento del lenguaje de alto nivel proviene de compilar para diferentes procesadores y desmontar utilizando varios compiladores. Y más importante aún, el código alrededor de las líneas en cuestión juega un papel importante en la forma en que el compilador optimiza ese segmento.

El uso de otro ejemplo de esta pregunta:

typedef struct 
{ 
    unsigned int first; 
    unsigned int second; 
} dataStruct; 

dataStruct data; 

int main() 
{ 
    dataStruct *pData = &data; 

    data.first = 9; 
    pData->second = 10; 

    return(0); 
} 

con gcc (no es para tanto un compilador) se obtiene:

mov r2, #10 
mov r1, #9 
stmia r3, {r1, r2} 

Así que ambas líneas de código C se unen en una tienda, el problema aquí es el ejemplo utilizado como prueba. Dos funciones separadas habrían sido un poco mejores, pero necesita un código mucho mayor y el puntero necesita apuntar a otra memoria para que el optimizador no se dé cuenta de que es una dirección global estática, para probar esto debe pasar la dirección en entonces el compilador (bueno gcc) no puede entender que es una dirección estática.

O sin optimizaciones, mismo código, mismo compilador, sin diferencia entre puntero y directo.

mov r3, #9 
str r3, [r2, #0] 

mov r3, #10 
str r3, [r2, #4] 

Esto es lo que esperaría ver según el compilador y el procesador, puede que no haya diferencia. Para este procesador, incluso si el código de prueba ocultara la dirección estática del puntero de la función, se reduciría a dos instrucciones. Si el valor almacenado en el elemento de estructura ya estaba cargado en un registro, entonces sería una instrucción en ambos sentidos, puntero o directo.

Así que la respuesta a su pregunta no es absoluta ...depende. desmontar y probar.

+0

usando dobles en lugar de ints solo significa más registros, la tienda en sí misma sigue siendo una sola instrucción para el puntero o acceso directo, por lo que todavía no hay diferencia, para este compilador para este procesador para esta sesión de compilación. –

+0

Esto está mal. Su primer miembro siempre ganará por definición de una estructura (el puntero de una estructura también es un puntero de un primer miembro). Usted debe comparar segundo vs segundo. –

0

El acceso directo a los miembros es más rápido (para los punteros, normalmente obtendría una operación de referencia de puntero). Aunque estoy teniendo dificultades para imaginarlo en una situación en la que sería un problema, rendimiento o no.

Cuestiones relacionadas