¿Cuáles son algunos patrones de diseño de C ++ para aplicaciones en tiempo real como el comercio algorítmico?

Las aplicaciones en tiempo real requieren un rendimiento determinista, que excluye Java y otros lenguajes recolectados de basura. C y C ++ son los lenguajes elegidos para sistemas en tiempo real. Creo que cuando se refiere al comercio algorítmico, en realidad se refiere al comercio de alta frecuencia (HFT).

Los sistemas en tiempo real y de rendimiento combinan:

  1. Arquitectura básica sólida
  2. Consideración HW
  3. Perfiles y optimización basada en datos

Arquitectura básica
La arquitectura básica incluye la aplicación del conocimiento del rendimiento big-O con la comprensión de la escala de datos en diferentes puntos del programa. Cuantos núcleos ¿Cuál es la situación de coherencia de caché? ¿Está involucrado NUMA? ¿Qué tan importante es la afinidad del procesador?

Muéstrame tus diagramas de flujo y oculta tus tablas, y seguiré desconcertado. Muéstrame tus tablas, y generalmente no necesitaré tus diagramas de flujo; Serán obvios. ~ Fred Brooks, El Mes del Hombre Mítico

Tiene que obtener sus estructuras de datos y sus interfaces correctas. Sin las estructuras de datos correctas, su programa está condenado al infierno del rendimiento. Debe tomar el tiempo y el cuidado adecuados con sus interfaces, de modo que pueda variar la implementación sin afectar a los clientes. “[Código] a toda prisa, arrepiéntete a gusto”.

Los algoritmos importan

Por ejemplo, durante la revisión del código de una interfaz de mensajería del cliente, encontramos que hubo dos bucles for-over involuntariamente sobre los mensajes, uno anidado dentro del otro. La revisión del código se activó porque en condiciones adversas el tiempo de procesamiento se deterioró de 1us a 10ms. Los desarrolladores insistieron en que, dado que el procesamiento de un solo mensaje tomó 1us, los 10ms tuvieron que ser un problema en otra parte. “¿Cuál es el mayor número de mensajes que ve en la cola de mensajes?” “100, lo limitamos a 100 mensajes”. Tiene un procesamiento O (N ^ 2), lo que significa que con 100 mensajes es 100 * 100 = 10,000X ralentiza ese solo mensaje. ¿Qué es 1us * 10,000? ”“ 10ms … ”Cambiar el algoritmo de manera que cada mensaje se considerara solo una vez mejoró el rendimiento del peor de los casos a significativamente menos de 1ms.

Los casos de “esquina” importan

En otro caso, usamos weak_ptr en los métodos de devolución de llamada para E / S asíncrona. Mientras los sistemas estuvieran en estado estable, podríamos salir con la pereza y usar shared_ptr en todas partes. Sin embargo, este uso de shared_ptr evitó el apagado limpio con E / S asíncrona. El uso de weak_ptr, que observa el shared_ptr y solo puede promocionarse a shared_ptr utilizable mientras exista el recurso referenciado, fue fundamental para limpiar el apagado.

Algunos miembros del equipo argumentaron que nunca cerramos; solo apagado. Sin embargo, al solucionar los problemas que impedían el apagado limpio, también abordamos los errores difíciles de reproducir que ocurrieron cuando se retiró una solicitud de E / S asíncrona.

Patrones de diseño comunes

Productor-Consumidor con una cola segura para subprocesos como notificación de conexión y condición variable. Esto libera al Productor para hacer más trabajo tan pronto como indique la variable de condición. El consumidor es eficiente porque bloquea la variable de condición proporcionada por el sistema operativo (y eficiente).

El patrón de mensajería entrega paquetes de datos a un destinatario con un nombre abstracto. La responsabilidad de traducir el nombre abstracto en un mecanismo de entrega concreto y un punto final se maneja dentro de la biblioteca de mensajes.

Los grupos de subprocesos crean previamente una serie de subprocesos y estos subprocesos se asignan según sea necesario para trabajar una tarea específica y luego se devuelven al grupo para su reutilización.

La E / S asincrónica es una técnica de codificación que complementa el Patrón de mensajería y los Grupos de subprocesos. El programa realiza una solicitud de E / S y proporciona una función de devolución de llamada invocada por el sistema operativo cuando se completa la E / S. Esto es eficiente porque las CPU son órdenes de magnitud más rápidas que las E / S. La biblioteca Asio de Boost es de gran ayuda aquí para E / S asíncronas portátiles.

Sondeo : sí, su profesor de sistemas operativos le dijo que el sondeo es ineficiente. Lo es, pero proporciona una respuesta de latencia más baja que interrumpe a los manejadores. Particularmente cuando tiene docenas de núcleos disponibles, asignar un núcleo a cada puerto de E / S de 40 Gb tiene mucho sentido.

shared_ptr : es difícil justificar no usar shared_ptr (o similar) a menos que y hasta que el perfil muestre una sobrecarga inaceptable. Una vez que hice ese perfil, una vez rechacé una oferta de trabajo atractiva porque el programador principal me dijo: “Cualquiera que no recuerde cuándo liberar sus recursos no debería estar programando para mí”. Sí, y dejaré de usar cinturones de seguridad cuando Conduzco porque si no puedo evitar un accidente, no debería operar un automóvil.

Event Drive Architecture, donde cada programa registra manejadores de mensajes que activan ciertas piezas de código a través de una variable de condición. Idealmente, los controladores de mensajes se registran al inicio y hay una sola llamada de bloqueo en la aplicación pendiente de mensajes. El bloqueo en un solo punto hace que sea mucho más fácil rastrear puntos muertos.

Perfilado

Una de las mayores verdades desconocidas de la programación moderna es que los humanos predicen espectacularmente mal dónde estará el cuello de botella en el rendimiento. Haríamos bien en cumplir la ley de carpintería: “Mide dos veces, corta una vez”.

Trabajando en mi tesis de maestría, me encontré con problemas de rendimiento. Sabía que mi código dependía en gran medida de la integración, por lo que pasé una semana optimizando ese código con la biblioteca Intel Performance Primitives, ajustes manuales, etc. Al final de la semana, aceleré la integración en un impresionante 60%. Desafortunadamente, cuando ejecuté la aplicación general con anticipación esperanzadora, me decepcionó lamentablemente. En cuanto a la creación de perfiles, descubrí que la mejora del 60% en el tiempo de integración se tradujo en una mejora del 3% en el rendimiento general de la aplicación. La elaboración de perfiles mostró que más del 80% del tiempo del programa se gastó en las instrucciones impares _I386EXCHANGEINTERLOCK. Bueno, compré la PC más rápida que pude para el proyecto, que era un Athlon de doble procesador (no de doble núcleo). Esta instrucción se usa para sincronizar cachés. Al aplicar un comando de una sola línea , SetProcessorAffinity, obtuve una mejora de velocidad de 5X. Lección aprendida.

More Interesting

¿Qué SDK y lenguaje de programación debo usar para codificar algoritmos de aprendizaje automático para predicciones en tiempo real?

¿Existen algoritmos que puedan determinar la convergencia o la falta de ella para cualquier serie arbitraria que se pueda expresar en notación de suma estándar?

¿Cómo se diseña el algoritmo de búsqueda difusa en Sublime Text? ¿Cómo diseñarías algo similar?

¿Cómo podemos encontrar la segunda ruta más pequeña entre dos nodos en un gráfico ponderado / no ponderado de manera eficiente?

¿Qué pasaría si le preguntas a AI si sus algoritmos son autoconsistentes?

¿Cuál es el algoritmo utilizado por Google para la búsqueda por voz e imagen?

¿Qué debo aprender en línea si quiero obtener un trabajo bien remunerado en TI en India? ¿Debería ser algo así como algoritmos de estructura de datos o un lenguaje como Python o R o algo así como un desarrollador de aplicaciones de Android o algo más?

¿Por qué es difícil realizar una búsqueda binaria en una lista vinculada?

¿Cómo encontramos la altura de un árbol binario? ¿Cómo se relaciona con el nivel?

¿Cuál sería el impacto económico de un algoritmo de compresión tan eficiente como el representado en Silicon Valley?

Cómo resolver este problema de matriz (detalles en la descripción)

¿Cómo funciona la recursividad en el árbol de búsqueda binaria en orden? ¿Cómo se pueden explicar las llamadas recursivas, sin resumirlas como llamadas de pila?

¿Qué significa "algo"?

¿Qué te dirías a ti mismo cuando recién comenzaste a programar, aprender algoritmos?

Cómo recorrer un trabajo de búsqueda binaria e imprimirlo en orden