¿Cuáles son las implicaciones de rendimiento de marcar métodos / propiedades como virtuales?


La pregunta es como se indica en el título: ¿Cuáles son las implicaciones de rendimiento de marcar métodos / propiedades como virtuales?

Nota - Estoy asumiendo que los métodos virtuales no estarán sobrecargados en el caso común; normalmente estaré trabajando con la clase base aquí.

Author: Erik Forbes, 2009-02-10

7 answers

Las funciones virtuales solo tienen una sobrecarga de rendimiento muy pequeña en comparación con las llamadas directas. En un nivel bajo, básicamente estás mirando una búsqueda de matriz para obtener un puntero de función, y luego una llamada a través de un puntero de función. Las CPU modernas incluso pueden predecir las llamadas indirectas a funciones razonablemente bien en sus predictores de rama, por lo que generalmente no dañarán demasiado las tuberías de CPU modernas. A nivel de ensamblado, una llamada a función virtual se traduce en algo como lo siguiente, donde I es un valor inmediato arbitrario.

MOV EAX, [EBP + I] ; Move pointer to class instance into register
MOV EBX, [EAX] ;  Move vtbl pointer into register.
CALL [EBX + I]  ;   Call function

Vs. lo siguiente para una llamada directa a la función:

CALL I  ;  Call function directly

La sobrecarga real viene en que las funciones virtuales no se pueden insertar, en su mayor parte. (Pueden estar en lenguajes JIT si la VM se da cuenta de que siempre van a la misma dirección de todos modos.) Además de la aceleración que se obtiene de inlining en sí, inlining permite varias otras optimizaciones, como el plegado constante, porque el llamante puede saber cómo funciona internamente el llamante. Para funciones que son lo suficientemente grandes como para no estar en línea de todos modos, el éxito de rendimiento probablemente será insignificante. Para funciones muy pequeñas que podrían estar en línea, es cuando debe tener cuidado con las funciones virtuales.

Editar: Otra cosa a tener en cuenta es que todos los programas requieren control de flujo, y esto nunca es gratis. ¿Qué reemplazaría su función virtual? Una instrucción switch? Una serie de sentencias if? Estas siguen siendo ramas que pueden ser impredecibles. Además, dada una rama N-way, una serie de sentencias if encontrará la ruta adecuada en O(N), mientras que una función virtual la encontrará en O (1). La instrucción switch puede ser O (N) u O (1) dependiendo de si está optimizada para una tabla de salto.

 127
Author: dsimcha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-02-16 15:14:56

Rico Mariani describe los problemas relacionados con el rendimiento en su blog Performance Tidbits, donde declaró:

Métodos Virtuales: Está utilizando métodos virtuales cuando llamadas directas ¿lo haría? Muchas veces la gente va con métodos virtuales para permitir el futuro extensibilidad. La extensibilidad es un lo bueno es que tiene un precio - asegúrese de su extensibilidad completa historia se resuelve y que su uso de funciones virtuales en realidad va conseguir a donde necesitas estar. Por ejemplo, a veces la gente piensa a través de los problemas del sitio de llamada, pero luego no considere cómo el " extendido" se van a crear objetos. Más tarde se dan cuenta de que (la mayor parte) las funciones virtuales no ayudaron en absoluto y necesitaban una forma completamente diferente modelo para obtener los objetos" extendidos" en el sistema.

Sellado: El sellado puede ser una forma de limitando el polimorfismo de su clase a solo aquellos sitios donde se necesita polimorfismo. Si quieres controlar completamente el tipo y luego sellar puede ser una gran cosa para el rendimiento ya que permite llamadas directas y en línea.

Básicamente el argumento contra los métodos virtuales es que no permite que el código sea un candidato de in-lining, a diferencia de las llamadas directas.

En el artículo de MSDN Mejorando el Rendimiento y la Escalabilidad de las aplicaciones. NET , esto se explica más a fondo:

Considere las Compensaciones de Virtual Miembros

Utilice miembros virtuales para proporcionar extensibilidad. Si no necesita extender su clase diseño, evitar miembros virtuales porque son más caros para llamar debido a un virtual búsqueda de tabla y que derrotan ciertas optimizaciones de rendimiento en tiempo de ejecución. Por ejemplo, los miembros virtuales no pueden ser insertados por el compilador. Además, cuando permite el subtipo, en realidad presenta un contrato muy complejo para los consumidores e inevitablemente termina con problemas de versionado cuando intenta mejorar tu clase en el futuro.

Una crítica de lo anterior, sin embargo, proviene del campo TDD/BDD (que quiere métodos predeterminados a virtuales) argumentando que el impacto en el rendimiento es insignificante de todos modos, especialmente cuando tenemos acceso a máquinas mucho más rápidas.

 14
Author: Jon Limjap,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-02-10 02:05:51

Normalmente un método virtual simplemente pasa por una tabla de punteros de función para llegar al método real. Esto significa una desreferencia extra y un viaje más de ida y vuelta a la memoria.

Si bien el costo no es absolutamente CERO, es extremadamente mínimo. Si ayuda a su programa a tener funciones virtuales, por supuesto, hágalo.

Es mucho mejor tener un programa bien diseñado con un pequeño, pequeño, pequeño éxito de rendimiento en lugar de un programa torpe solo por el bien de evitar la v-table.

 11
Author: abelenky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-02-10 02:01:00

Es difícil de decir con seguridad, porque el compilador JIT. NET puede ser capaz de optimizar la sobrecarga de distancia en algunos(muchos?) caso.

Pero si no lo optimiza, básicamente estamos hablando de una indirecta de puntero adicional.

Es decir, cuando llamas a un método no virtual, tienes que

  1. Guardar registros, generar la función prólogo/epílogo para configurar argumentos, copiar el valor devuelto y tal.
  2. saltar a un fijo, y estáticamente conocido, dirección

1 es lo mismo en ambos casos. En cuanto a 2, con un método virtual, debe leer desde un desplazamiento fijo en la tabla v del objeto y luego saltar a donde quiera que apunte. Eso hace que la predicción de ramas sea más difícil, y puede sacar algunos datos de la caché de la CPU. Así que la diferencia no es enorme, pero se puede sumar si haces que cada llamada de función sea virtual.

También puede inhibir las optimizaciones. El compilador puede insertar fácilmente una llamada a una función no virtual, porque sabe exactamente qué función se llama. Con una función virtual, eso es un poco más complicado. El compilador JIT todavía puede ser capaz de hacerlo, una vez que se determina qué función se llama, pero es mucho más trabajo.

Con todo, todavía puede sumar, especialmente en áreas críticas de rendimiento. Pero no es algo de lo que deba preocuparse a menos que la función se llame al menos unos cientos de miles de veces por segundo.

 4
Author: jalf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-02-10 02:04:21

Ejecuté esta prueba en C++. Una llamada a función virtual tarda (en un PowerPC de 3ghz) entre 7-20 nanosegundos más que una llamada a función directa. Eso significa que realmente solo importa para las funciones que planea llamar un millón de veces por segundo, o para las funciones que son tan pequeñas que la sobrecarga puede ser mayor que la función en sí. (Por ejemplo, hacer que las funciones del accessor sean virtuales a partir del hábito ciego es probablemente imprudente.)

No he ejecutado mi prueba en C#, pero espero que el la diferencia será aún menor, ya que casi todas las operaciones en el CLR implican una indirecta de todos modos.

 4
Author: Crashworks,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-02-10 02:11:17

Desde tus etiquetas, estás hablando de c#. Solo puedo responder desde la perspectiva de Delphi. Creo que será similar. (Estoy esperando comentarios negativos aquí :))

Un método estático será enlazado en tiempo de compilación. Un método virtual requiere una búsqueda en tiempo de ejecución para decidir a qué método llamar, por lo que hay una pequeña sobrecarga. Solo es significativo si el método es pequeño y se llama a menudo.

 2
Author: Richard A,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-02-10 01:56:46

En el lado del escritorio no importa si el método está sobrecargado o no, incurren en un nivel extra de indirección a través de la tabla de puntero del método (Tabla de métodos virtuales), lo que significa aproximadamente 2 lecturas de memoria adicionales a través de indirección antes de que la llamada al método comparara métodos no virtuales en clases no selladas y métodos no finales.

[Como dato interesante, en compact framework versión 1.0 el sobrecalentamiento es mayor ya que no utiliza tablas de métodos virtuales sino simplemente reflexión para descubre el método correcto para ejecutar al llamar a un método virtual.]

También es mucho menos probable que los métodos virtuales sean candidatos para la inserción u otras optimizaciones como tail call que los métodos no virtuales.

Aproximadamente esta es la jerarquía de rendimiento de las llamadas a métodos:

Métodos no virtuales

Pero ninguna de estas implicaciones de rendimiento de varios los mecanismos de envío no importan a menos que lo demuestre midiendo ;) (E incluso entonces las implicaciones de la arquitectura, la legibilidad, etc. podrían tener un gran peso sobre cuál elegir)

 0
Author: Pop Catalin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-02-10 02:00:14