2009-09-15 19 views
8

Me pregunto si las clases "todopoderosas" en C++ y cómo lo hacen realmente impactan en el rendimiento.C++: impacto en el rendimiento de las clases GRANDES (con un montón de código)

Si tengo, por ejemplo, una clase Punto, con solo uint x; uint y; como datos, y han definido prácticamente todo lo que las matemáticas pueden hacer hasta cierto punto como métodos. Algunos de esos métodos pueden ser enormes. (copia-) los constructores no hacen nada más que inicializar los dos miembros de datos.

class Point 
{ 
    int mx; int my; 
    Point(int x, int y):mx(x),my(y){}; 
    Point(const Point& other):mx(other.x),my(other.y){}; 
// .... HUGE number of methods.... 
}; 

Ahora. Cargué una imagen grande y creo un Punto para cada píxel, rellenelos en un vector y use ellos. (digamos, todos los métodos se llaman una vez) ¡Esto solo es un ejemplo estúpido!

¿Sería más lento que la misma clase sin los métodos pero con muchas funciones de utilidad? ¡No estoy hablando de funciones virtuales de ninguna manera!

mi motivación para esto: a menudo me encuentro escribiendo clases agradables y relativamente potentes, pero cuando tengo para inicializar/usar un montón de ellos, como en el ejemplo anterior, me pongo nervioso. Creo que no debería.

lo que creo que sé es:

  1. Métodos existe sólo una vez en la memoria. (optimizaciones a un lado)
  2. La asignación solo tiene lugar para los datos miembros, y son lo único copiados.

Así que no debería importar. ¿Me estoy perdiendo de algo?

+2

ver también: http: // stackoverflow.com/questions/648647/in-c-where-in-memory-are-class-functions-put –

Respuesta

10

Tiene razón, los métodos solo existen una vez en la memoria, son como las funciones normales con un parámetro oculto adicional.

Y, por supuesto, sólo los miembros de datos se toman en cuenta para la asignación, así, la herencia puede introducir algunos PAD adicionales para vptrs en el tamaño del objeto, pero no es un gran problema

1

Si está realmente preocupado, que pueda dile a tu compilador que alinee los constructores. Este paso de optimización debería dejarlo con código limpio y ejecución limpia.

1

Estos 2 bits de código son idénticos:

Point x; 
int l=x.getLength(); 

int l=GetLength(x); 

dado que la clase Point tiene un método getLength no virtual(). La primera invocación realmente llama al int getLength(Point &this), una firma idéntica a la que escribimos en nuestro segundo ejemplo. (*)

Esto, por supuesto, no se aplicaría si los métodos que está llamando son virtuales, ya que todo pasaría por un nivel adicional de indirección (algo parecido al estilo C int l=x->lpvtbl->getLength(x)), sin mencionar que en su lugar de 2 int por cada píxel que realmente tiene 3, el extra es ese puntero a la tabla virtual.

(*) Esto no es exactamente cierto, el puntero "this" se pasa a través de uno de los registros de CPU en lugar de a través de la pila, pero el mecanismo podría haber funcionado fácilmente de cualquier manera.

1

Primero: no optimice prematuramente. Segundo: el código de limpieza es más fácil de mantener que el código optimizado.

Los métodos para las clases tienen este puntero oculto, pero no debe preocuparse por ello. La mayoría de las veces el compilador intenta pasarlo a través de registro.

La herencia y la función virtual introducen indirecciones en las llamadas apropiadas (inheritance = constructor/destructor call, virual function - cada llamada de función de esta función).

corta:

  • objetos no crea/destruye a menudo puede tener métodos virtuales, herencia, etc., siempre que se beneficia del diseño.
  • Los objetos que crea/destruye con frecuencia deberían ser pequeños (pocos miembros de datos) y no deberían tener muchos métodos virtuales (lo mejor sería ninguno en absoluto, en cuanto al rendimiento).
  • intenta alinear métodos pequeños/constructor. Esto reducirá la sobrecarga.
  • Elija un diseño limpio y refactorícese si no alcanza el rendimiento deseado.

Hay una discusión diferente sobre las clases que tienen interfaces grandes o pequeñas (por ejemplo, en uno de Scott Meyers (Más) libros efectivos en C++; opta por una interfaz mínima). Pero esto no tiene nada que ver con el rendimiento.

+0

Creo que te refieres a hacerlo en lugar de no hacerlo en tu segundo punto. También en su segundo punto no veo ninguna ventaja de tener pocos métodos virtuales, las llamadas virtuales son más lentas así que llamar a muchos métodos virtuales tiene algunos (pequeños) problemas de rendimiento. – Elemental

+0

gracias, por supuesto, tienes razón. En caso de que tengas pocas virt. métodos, la mayoría de las llamadas a métodos son directas (sin indirección sobre el vblbl). Si logras diseñar tu clase sin ningún virt. función, no vtbl tiene que configurarse durante la construcción. –

6

Ya tiene algunos consejos técnicos bastante buenos. Quiero agregar algo no técnico: como STL nos mostró a todos, hacerlo todo en funciones de miembros podría no ser la mejor manera de hacerlo. En lugar de acumular argumentos, me refiero al artículo de la clase de Scott Meyers sobre el tema: How Non-Member Functions Improve Encapsulation.

Aunque técnicamente no debería haber ningún problema, aún podría querer revisar su diseño desde un POV de diseño.

+0

Usted señaló un artículo intrigante. Pero mi preocupación es que estas personas parecen codificar muy diferente a la mayoría de las personas normales. Me encantan las funciones de los miembros porque en cada IDE digno de mención se me proponen y me dicen qué puede hacer esa clase. Esto no es realmente posible con las funciones de no amigos que no son miembros. – AndreasT

+0

Si una función gratuita no le dice lo que hace, entonces necesita un nombre mejor. – sbi

+0

No es lo que quise decir. Un IDE proporciona clase. que reduce las búsquedas lentas en los manuales. Las funciones gratuitas no se muestran allí. Puedo buscarlos, pero eso es mucho más complicado que escribir '.' y esperando medio segundo Esta es una de las razones por las que las personas buscan bibliotecas de clase de "estilo incorrecto" sin una gran cantidad de plantillas mágicas e interfaces dispersas. Todos los que escalaron la montaña del dominio del impulso pueden estar orgullosos de su logro, pero solo pueden mirar con envidia a las bibliotecas que se pueden usar sin problemas solo a partir de la información en una información contextual sensible a las herramientas. – AndreasT

0

He creado la misma clase de punto que usted, excepto que es una clase de plantilla y todas las funciones están en línea. Espero ver que el aumento del rendimiento no disminuya por esto. Sin embargo, una imagen de tamaño 800x600 tendrá 480kpixels y su impresión de memoria será cercana a 4M sin ningún tipo de información de color. No solo la memoria, sino también la inicialización del objeto 480k llevará demasiado tiempo. Por lo tanto, creo que no es una buena idea en ese caso. Sin embargo, si utiliza esta clase para transformar la posición de una imagen, o la utiliza para primitivas gráficas (líneas, curvas, círculos, etc.)

0

Estoy de acuerdo con los comentarios anteriores wrt: rendimiento y diseño de clase, y me gustaría para agregar un comentario aún no declarado sobre el diseño.

Me parece que está utilizando en exceso su clase Point más allá de su ámbito de diseño real. Claro, es puede ser usado de esa manera, pero debe es?

En el trabajo anterior en juegos de computadora a menudo he enfrentado situaciones similares, y generalmente el mejor resultado final ha sido que al hacer un procesamiento especializado (por ejemplo, procesamiento de imágenes) con un código especializado para eso. Los buffers de salida han sido más eficientes.

Esto también le permite optimizar el rendimiento para el caso que importa, de una manera más limpia, sin hacer que el código base sea menos sostenible.

En teoría, estoy seguro de que hay una manera astuta de usar una combinación compleja de código de plantilla, diseño de clase concreta, etc., y obtener casi la misma eficiencia de tiempo de ejecución ... pero normalmente no estoy dispuesto para hacer el comercio de complejidad de implementación.

3

supongo que esto es más de una respuesta de lo que estamos buscando, pero aquí va ...

SO está lleno de preguntas donde la gente está preocupada por el rendimiento de X, Y o Z, y esa preocupación es una forma de adivinando.

Si le preocupa el rendimiento de algo, no se preocupe, averigüe.

Esto es lo que debe hacer:

  1. escribir el programa

  2. Performance tune it

  3. aprender de la experiencia

Lo que esto me ha enseñado, y yo' lo he visto una y otra vez, es esto:

  • La mejor práctica dice no optimizan prematuramente.

  • La mejor práctica dice hacer un montón de clases de uso de la estructura de datos, con múltiples capas de abstracción, y los mejores algoritmos de orden O, "ocultación de información", con una arquitectura orientada a eventos y el estilo de notificación.

  • El ajuste del rendimiento revela que el tiempo va, que es: galopante generalidad, haciendo una montaña de un grano de arena, llamar a las funciones & propiedades sin realización de el tiempo que tardan, y hacer esto a través de múltiples capas usando un tiempo exponencial.

  • A continuación, se hace la pregunta: ¿Cuál es la razón detrás de la mejor práctica para los algoritmos de orden O, evento- y notificación impulsado por la arquitectura, etc. La respuesta viene: Bueno, entre otras cosas, el rendimiento .

Así pues, en cierto modo, las mejores prácticas nos está diciendo: a optimizar prematuramente. Obtener el punto? Dice "no se preocupe por el rendimiento", y dice "preocuparse por el rendimiento", y es por lo que estamos intentando sin éxito no preocuparnos. Y mientras más nos preocupemos, contra nuestro mejor juicio, peor se vuelve.

Mi sugerencia constructiva es esta: siga los pasos 1, 2 y 3 anteriores. Eso le enseñará cómo usar las mejores prácticas en moderación, y eso le dará el mejor diseño completo.

0

Las funciones de miembro no se copian junto con el objeto. Solo los campos de datos contribuyen al tamaño del objeto.

Cuestiones relacionadas