Recientemente me he topado con esta pregunta tras necesitar hacer esto para realizar un análisis de microestructuras en tiempo real y he echado un vistazo a las distintas implementaciones posibles. Aquí están algunos de los pros / contras de cada implementación (voy a utilizar la terminología C / C ++):
Array: Tienes un array de structs ordenados desde el principio del libro (índice 0) hasta el peor (índice N). Tienes que preasignar el array para que sea lo suficientemente grande.
La inserción es O(LOG N) : Realiza una búsqueda binaria para encontrar el punto de inserción, luego realiza un memcpy para desplazar la cola del array. Finalmente, sobrescribe el elemento en el punto de inserción.
La búsqueda es O(1) : Sus índices se corresponden directamente con los niveles de profundidad del mercado. Sólo tienes que saltar a donde quieras.
Las eliminaciones son O(LOG N) : Buscas el orden que quieres borrar, luego realizas un memcpy desplazando todo hacia atrás en 1 índice.
Las actualizaciones son, en el peor de los casos, una supresión + una inserción . En el mejor de los casos, se comprueba si la orden actualizada cambia de orden.
Pros:
- Muy fácil de cachear.
- Fácil de optimizar para el compilador
- No hay que reordenar mucho
- Búsquedas más rápidas
- Las operaciones de copia suelen ser vectoriales (normalmente si se utiliza std::copy en lugar de memcpy)
Contras:
- Oportunidades limitadas de vectorización (SSE/AVX2)
- muchas operaciones de copia
Lista de enlaces simples : El mismo concepto que un array (la cabeza de la lista enlazada es la parte superior del libro, la cola es la parte inferior del libro).
La inserción es O(N) : Tiene que buscar en la lista, actualizar el "puntero siguiente" de su nueva orden al "puntero siguiente" de la orden anterior, actualizar el "puntero siguiente" de la orden anterior para que apunte a su nueva comilla, y actualizar la cabeza/cola/longitud de la lista si procede.
La búsqueda es O(N) : La parte superior del libro es trivial O(1) ya que es la cabeza de la lista. De lo contrario, tendrá que buscar a través de la lista.
El borrado es O(N) : Tienes que buscar en la lista para encontrar el punto de destino. A continuación, intercambia dos punteros.
Las actualizaciones son un borrado + una inserción en el peor de los casos . En el mejor de los casos es O(1) suponiendo que se conozca el índice correcto (ya profundizaré en esto en esta respuesta)
Unas cuantas optimizaciones le dan un rendimiento aceptable:
- Pre-asigna un array de structs de nodos de lista enlazada con un struct de comilla como miembro que corresponde a la profundidad máxima de tu libro. Utilice estos punteros en su lista enlazada. Dado que la memoria será contigua, el coste de la indirección de usar punteros se reducirá, ya que no habrá tantos fallos de caché.
- Mantener los punteros de profundidad de mercado del 25%, 50% y 75%, además de la cabeza y la cola. Esto reduce significativamente la "N" en O(N) para las operaciones de inserción, eliminación y actualización.
Ventajas: - No se repite la copia una vez que se busca en el lugar correcto - Las actualizaciones/inserciones/consultas son muy rápidas si la mayoría de las operaciones están cerca de la cabeza de la lista
Contras:
- La búsqueda de niveles más profundos tiene un alto coste O(N)
- Mal rendimiento de la caché si no se tiene cuidado
- Asignaciones dinámicas de memoria si no se preasigna una arena
Al final, he comprobado que la versión de matriz tiene el mejor rendimiento medio si está de acuerdo con la prioridad de precio/cantidad . Si necesita la precisión de la prioridad precio/cantidad/tiempo, las matrices son demasiado grandes.
Si te parece bien la prioridad precio/cantidad: Podemos utilizar la propiedad de que las cantidades tienden a ser muy similares para los valores para limitar el número de operaciones de copia:
Cree una matriz de 4 veces el tamaño del rango de precios posible esperado para el día (obviamente tendrá que reasignar su matriz si ocurre algo salvaje). Coloque el nivel de precios correspondiente a la apertura prevista en el centro de la matriz. Cada vez que reciba una nueva orden, el índice deseado se acercará al número de céntimos de diferencia entre este precio y el de la orden. A continuación, puede mirar hacia adelante/atrás para ver si el nivel de precio/cantidad existe. Si no existe, tendrá que desplazar la matriz mediante una copia. Pero si existe, ya está todo listo. Este enfoque de "matriz centrada" significa que después de unos minutos de citas, tendrá la gran mayoría de sus niveles de precio/cantidad definidos. Este método sólo funciona si está de acuerdo con la prioridad de precio/cantidad. Si necesita una prioridad de precio/cantidad/tiempo, probablemente deba optar por la lista enlazada.