variable una que programas programacion principiantes para orientada objetos metodos ejemplos ejemplo clases clase atributos c++ performance

una - Rendimiento C++ del acceso a las variables miembro frente a las variables locales



programacion orientada a objetos ejemplos c++ (11)

Además, hay una tercera opción: locales estáticos. No se vuelven a asignar cada vez que se llama a la función (de hecho, se conservan en llamadas) pero no contaminan la clase con variables de miembros excesivas.

¿Es más eficiente para una clase acceder a variables miembro o variables locales? Por ejemplo, supongamos que tiene un método (de devolución de llamada) cuya única responsabilidad es recibir datos, realizar cálculos en él y luego pasarlo a otras clases. En lo que respecta al rendimiento, ¿tendría más sentido tener una lista de variables miembro que el método rellene a medida que recibe datos? ¿O simplemente declara las variables locales cada vez que se llama al método de devolución de llamada?

Supongamos que este método se llamaría cientos de veces por segundo ...

En caso de que no me quede claro, he aquí algunos ejemplos rápidos:

// use local variables class thisClass { public: void callback( msg& msg ) { int varA; double varB; std::string varC; varA = msg.getInt(); varB = msg.getDouble(); varC = msg.getString(); // do a bunch of calculations } }; // use member variables class thisClass { public: void callback( msg& msg ) { m_varA = msg.getInt(); m_varB = msg.getDouble(); m_varC = msg.getString(); // do a bunch of calculations } private: int m_varA; double m_varB; std::string m_varC; };


El uso de las variables miembro debe ser marginalmente más rápido ya que solo deben asignarse una vez (cuando se construye el objeto) en lugar de cada vez que se invoque la devolución de llamada. Pero en comparación con el resto del trabajo que probablemente esté haciendo, espero que este sea un porcentaje muy pequeño. Benckmark ambos y ver cuál es más rápido.


En caso de duda, comparta y vea por usted mismo. Y asegúrese de que haga la diferencia primero: cientos de veces por segundo no es una carga enorme para un procesador moderno.

Dicho eso, no creo que haya ninguna diferencia. Ambos serán desplazamientos constantes desde un puntero, los locales serán del puntero de pila y los miembros serán del puntero "this".


En mi opinión, no debería afectar el rendimiento, porque:

  • En su primer ejemplo, se accede a las variables mediante una búsqueda en la pila, por ejemplo, [ESP] +4, que significa el final actual de la pila más cuatro bytes .
  • En el segundo ejemplo, se accede a las variables mediante una búsqueda relativa a esto (recuerde, varB es igual a this-> varB). Esta es una instrucción de máquina similar.

Por lo tanto, no hay mucha diferencia.

Sin embargo, debes evitar copiar la cadena;)


Este debería ser su problema con los compiladores. En su lugar, optimice para la mantenibilidad: si la información solo se usa localmente, guárdela en variables locales (automáticas). Odio leer clases repletas de variables miembro que en realidad no me dicen nada acerca de la clase en sí, sino solo algunos detalles sobre cómo un conjunto de métodos funcionan juntos :(

De hecho, me sorprendería si las variables locales no son más rápidas de todos modos, están obligados a estar en caché, ya que están cerca del resto de los datos de las funciones (marco de llamada) y un puntero de objetos podría estar en otro lugar, pero Solo estoy adivinando aquí.


La cantidad de datos con los que interactuará tendrá una mayor influencia en la velocidad de ejecución que la forma en que representa los datos en la implementación del algoritmo.

El procesador realmente no se preocupa si los datos están en la pila o en el montón (aparte de la posibilidad de que la parte superior de la pila esté en el caché del procesador como se mencionó en peterchen), pero para una velocidad máxima, los datos tendrán que encajar en la caché del procesador (caché L1 si tiene más de un nivel de caché, que prácticamente todos los procesadores modernos tienen). Cualquier carga de caché L2 - o $ DEITY prohíbe, memoria principal - ralentizará la ejecución. Entonces, si está procesando una cadena de varios cientos de KB de tamaño y posibilidades en cada invocación, la diferencia ni siquiera será medible.

Tenga en cuenta que en la mayoría de los casos, una aceleración del 10% en un programa es prácticamente indetectable para el usuario final (a menos que logre reducir el tiempo de ejecución de su lote nocturno de 25 h a menos de 24 h), por lo que no vale la pena preocuparse a menos que esté seguro y tenga la salida del generador de perfiles para realizar una copia de seguridad de que esta pieza de código en particular está dentro del 10% -20% del "área candente" que tiene una influencia importante sobre el tiempo de ejecución de su programa.

Otras consideraciones deberían ser más importantes, como el mantenimiento u otros factores externos. Por ejemplo, si el código anterior está en código altamente multiproceso, el uso de variables locales puede facilitar la implementación.


Preferiría las variables locales en principios generales, porque minimizan el mal estado mutable en su programa. En cuanto a rendimiento, su perfilador le dirá todo lo que necesita saber. Los locales deberían ser más rápidos para los ints y quizás otros builtins, porque pueden colocarse en registros.


Pregunta tonta.
Todo depende del compilador y de lo que hace para la optimización.

Incluso si funcionó, ¿qué has ganado? Forma de ofuscar su código?

El acceso variable generalmente se realiza mediante un puntero y un desplazamiento.

  • Puntero a objeto + desplazamiento
  • Puntero a Stack Frame + offset

Además, no olvide agregar el costo de mover las variables al almacenamiento local y luego copiar los resultados nuevamente. Todo lo que podría significar menos, ya que el compilador puede ser lo suficientemente inteligente como para optimizar la mayor parte de todos modos.


Resumen ejecutivo: en prácticamente todos los escenarios, no importa, pero hay una ligera ventaja para las variables locales.

Advertencia: estás micro-optimizando. Terminará pasando horas tratando de entender el código que se supone que gana un nanosegundo.

Advertencia: En su escenario, el rendimiento no debería ser la pregunta, sino el rol de las variables: ¿son temporales o el estado de esta clase?

Advertencia: Primera, segunda y última regla de optimización: ¡mida!

En primer lugar, observe el ensamblaje típico generado para x86 (su plataforma puede variar):

// stack variable: load into eax mov eax, [esp+10] // member variable: load into eax mov ecx, [adress of object] mov eax, [ecx+4]

Una vez que se carga la dirección del objeto, en un registro, las instrucciones son idénticas. La carga de la dirección del objeto generalmente se puede emparejar con una instrucción anterior y no afecta al tiempo de ejecución.

Pero esto significa que el registro ecx no está disponible para otras optimizaciones. Sin embargo , las CPU modernas hacen algunos trucos intensos para que no sea un problema.

Además, al acceder a muchos objetos, esto puede costarle más. Sin embargo , este es un promedio de menos de un ciclo, y a menudo hay más oportunidades para las instrucciones de emparejamiento.

Localidad de memoria: esta es una oportunidad para que la pila gane a lo grande. La parte superior de la pila está virtualmente siempre en la caché L1, por lo que la carga toma un ciclo. Es más probable que el objeto sea devuelto a la memoria caché L2 (regla general, 10 ciclos) o la memoria principal (100 ciclos).

Sin embargo , pagas esto solo por el primer acceso. si todo lo que tiene es un acceso único, los 10 o 100 ciclos son imperceptibles. si tiene miles de accesos, los datos del objeto también estarán en caché L1.

En resumen, la ganancia es tan pequeña que virtualmente nunca tiene sentido copiar variables de miembros en locales para lograr un mejor rendimiento.


Depende, pero espero que no haya absolutamente ninguna diferencia.

Lo importante es esto: el uso de variables miembro como temporales hará que su código no sea reentrante. Por ejemplo, fallará si dos hilos intentan llamar a callback () en el mismo objeto. El uso de locales estáticos (o variables de miembros estáticos) es aún peor, porque entonces su código fallará si dos hilos intentan llamar a callback () en cualquier objeto thisClass - o descendiente.


Algunos puntos que no han sido mencionados explícitamente por otros:

  • Está invocando potencialmente operadores de asignación en su código. por ejemplo, varC = msg.getString ();

  • Tiene algunos ciclos desperdiciados cada vez que se configura el cuadro de funciones. Estás creando variables, se llama al constructor predeterminado y luego se invoca al operador de asignación para obtener el valor RHS en los locales.

  • Declare que los locales son const-refs y, por supuesto, inicialícelos.

  • Las variables de miembros pueden estar en el montón (si su objeto se asignó allí) y, por lo tanto, sufren de no localidad.

  • Incluso unos pocos ciclos guardados son buenos, ¿por qué desperdiciar el tiempo de cálculo en absoluto, si puede evitarlo?