Actualmente estoy en la etapa de diseño de una arquitectura basada en componentes en C ++.
Mi diseño actual incluye el uso de funciones como:
std::vector
s destd::shared_ptr
s para sostener los componentesstd::dynamic_pointer_cast
std::unordered_map<std::string,[yada]>
Los componentes representarán los datos y la lógica de varios elementos que se necesitan en un software similar a un juego, como gráficos, física, inteligencia artificial, audio, etc.
He leído por todas partes que las fallas de caché son difíciles para el rendimiento, por lo que realicé algunas pruebas, lo que me llevó a creer que, de hecho, puede ralentizar una aplicación.
No he podido probar las características del lenguaje antes mencionadas, pero en muchos lugares se dice que tienden a costar mucho y, si es posible, deben evitarse.
Dado que estoy en la etapa de diseño de la arquitectura, y estos se incluirán en el núcleo del diseño, ¿debería tratar de encontrar formas de evitarlos ahora, ya que será muy difícil cambiarlo más adelante si hay rendimiento? ¿cuestiones?
¿O simplemente estoy atrapado haciendo una optimización prematura?
fuente
the answer to this question is almost always a resounding "YES !!"
. Todavía siento que es mejor hacer que funcione primero y optimizar después, pero YMMV, todos tienen su opinión, todos los cuales son válidos, y solo el OP realmente puede responder a su propia pregunta subjetiva.Respuestas:
Sin leer nada más que el título: Sí.
Después de leer el texto: sí. Aunque es cierto que los mapas y los punteros compartidos, etc., no funcionan bien en caché, seguramente descubrirá que para lo que desea usarlos, por lo que yo entiendo, no es el cuello de botella y no se mantendrá use la memoria caché de manera eficiente independientemente de la estructura de datos.
¡Escriba el software evitando los errores más estúpidos, luego pruebe, luego encuentre los cuellos de botella y luego optimice!
Fwiw: https://xkcd.com/1691/
fuente
No estoy familiarizado con C ++, pero en general depende.
No necesita optimizar prematuramente los algoritmos aislados donde puede optimizar fácilmente cuando se trata de eso.
Sin embargo, necesita obtener un diseño general de la aplicación para lograr los indicadores clave de rendimiento deseados.
Por ejemplo, si necesita diseñar una aplicación para atender millones de solicitudes por segundo, debe pensar en la escalabilidad de la aplicación al diseñarla, en lugar de hacer que la aplicación funcione.
fuente
Si tiene que preguntar, entonces sí. Optimización prematura significa optimización antes de estar seguro de que hay un problema de rendimiento significativo.
fuente
ECS? De hecho, sugeriré que puede no ser prematuro si es así, pensar mucho en el lado orientado a los datos del diseño y comparar diferentes representantes porque podría afectar los diseños de su interfaz , y esto último es muy costoso cambiar tarde el juego. Además, ECS solo exige mucho trabajo y pensamiento por adelantado y creo que vale la pena utilizar algo de ese tiempo para asegurarse de que no le dará problemas de rendimiento a nivel de diseño más adelante dado que va a ser el corazón de su todo el maldito motor. Esta parte me deslumbra:
Incluso con pequeñas optimizaciones de cadena, tiene un contenedor de tamaño variable (cadenas) dentro de otro contenedor de tamaño variable (unordered_maps). De hecho, las pequeñas optimizaciones de cuerda en realidad podría ser tan perjudicial como útil en este caso, si la tabla es muy escasa, ya que la pequeña optimización cadena implicaría que cada índice no utilizada de la tabla hash todavía utilizará más memoria para la optimización de las SS (
sizeof(string)
sería ser mucho más grande) hasta el punto en que la sobrecarga de memoria total de su tabla hash podría costar más de lo que esté almacenando en ella, especialmente si es un componente simple como un componente de posición, además de incurrir en más errores de caché con el gran paso para ir de una entrada en la tabla hash a la siguiente.Supongo que la cadena es algún tipo de clave, como una ID de componente. Si es así, esto ya hace las cosas dramáticamente más baratas:
... si desea los beneficios de poder tener nombres fáciles de usar que los scripters puedan usar, por ejemplo, las cadenas internas pueden brindarle lo mejor de ambos mundos aquí.
Dicho esto, si puede asignar la cadena a un rango razonablemente bajo de índices densamente utilizados, entonces podría ser capaz de hacer esto:
La razón por la que no considero esto prematuro es porque, nuevamente, podría afectar los diseños de su interfaz. El objetivo del DOD no debería ser tratar de encontrar las representaciones de datos más eficientes imaginables de una vez IMO (que generalmente se debe lograr de forma iterativa según sea necesario), sino pensar en ellas lo suficiente como para diseñar interfaces en la parte superior para trabajar con eso datos que le dejan suficiente espacio para respirar para perfilar y optimizar sin cambios de diseño en cascada.
Como un ejemplo ingenuo, un software de procesamiento de video que combina todo su código contra esto:
No va a llegar lejos sin una reescritura potencialmente épica, ya que la idea de abstraer en el nivel de un solo píxel ya es extremadamente ineficiente (la
vptr
misma a menudo costaría más memoria que el píxel completo) en comparación con el resumen en el nivel de la imagen (que a menudo representan millones de píxeles). Por lo tanto, piense lo suficiente en sus representaciones de datos de antemano para que no tenga que enfrentar un escenario de pesadilla, e idealmente no más, pero aquí creo que vale la pena pensar en estas cosas por adelantado, ya que no desea construir un intrincado motor alrededor de su ECS y descubra que el ECS en sí mismo es el cuello de botella en formas que requieren que cambie las cosas a nivel de diseño.En cuanto a las fallas de caché ECS, en mi opinión, los desarrolladores a menudo se esfuerzan demasiado para que su caché ECS sea amigable. Comienza a producir muy poco dinero para que el dólar intente acceder a todos sus componentes de una manera perfectamente contigua, y a menudo implicará copiar y mezclar datos por todo el lugar. Por lo general, es lo suficientemente bueno, por ejemplo, simplemente mezclar índices de componentes de clasificación antes de acceder a ellos para que pueda acceder a ellos de una manera en la que al menos no esté cargando una región de memoria en una línea de caché, solo para desalojarla y luego cargarla todo de nuevo en el mismo bucle solo para acceder a una parte diferente de la misma línea de caché. Y un ECS no tiene que proporcionar una eficiencia sorprendente en todos los ámbitos. No es que un sistema de entrada se beneficie de eso tanto como un sistema de física o renderizado, por lo que recomiendo apuntar a "bueno" eficiencia en todos los ámbitos y "excelente" solo en los lugares donde realmente lo necesita. Dicho esto, el uso de
unordered_map
ystring
aquí son lo suficientemente fáciles de evitar.fuente