🚀 Prueba Zilliz Cloud, el Milvus completamente gestionado, gratis—¡experimenta un rendimiento 10 veces más rápido! Prueba Ahora>>

milvus-logo
LFAI

Antecedentes

  • Engineering
March 03, 2020
milvus

n este artículo, discutiremos cómo Milvus programa las tareas de consulta. También hablaremos de problemas, soluciones y orientaciones futuras para implementar la programación de Milvus.

Antecedentes

Sabemos por Managing Data in Massive-Scale Vector Search Engine que la búsqueda de similitud vectorial se implementa por la distancia entre dos vectores en un espacio de alta dimensión. El objetivo de la búsqueda vectorial es encontrar los K vectores más cercanos al vector objetivo.

Hay muchas formas de medir la distancia vectorial, como la distancia euclidiana:

1-euclidean-distance.png 1-distancia-euclídea.png

donde x e y son dos vectores. n es la dimensión de los vectores.

Para encontrar los K vectores más cercanos en un conjunto de datos, hay que calcular la distancia euclídea entre el vector objetivo y todos los vectores del conjunto de datos que se van a buscar. A continuación, los vectores se ordenan por distancia para obtener los K vectores más cercanos. El trabajo de cálculo es directamente proporcional al tamaño del conjunto de datos. Cuanto mayor sea el conjunto de datos, más trabajo de cálculo requerirá la consulta. Las GPU, especializadas en el procesamiento de grafos, disponen de muchos núcleos para proporcionar la potencia de cálculo necesaria. Por lo tanto, durante la implementación de Milvus también se tiene en cuenta la compatibilidad con múltiples GPU.

Conceptos básicos

Bloque de datos(Archivo de tabla)

Para mejorar el soporte de la búsqueda de datos a escala masiva, optimizamos el almacenamiento de datos de Milvus. Milvus divide los datos de una tabla por tamaño en múltiples bloques de datos. Durante la búsqueda vectorial, Milvus busca vectores en cada bloque de datos y fusiona los resultados. Una operación de búsqueda vectorial consta de N operaciones de búsqueda vectorial independientes (N es el número de bloques de datos) y N-1 operaciones de fusión de resultados.

Cola de tareas(TaskTable)

Cada Recurso tiene una matriz de tareas, que registra las tareas que pertenecen al Recurso. Cada tarea tiene diferentes estados, incluyendo Inicio, Cargando, Cargado, Ejecutando y Ejecutado. El cargador y el ejecutor de un dispositivo informático comparten la misma cola de tareas.

Programación de consultas

2-query-scheduling.png 2-programación-de-consultas.png

  1. Cuando se inicia el servidor Milvus, Milvus lanza el GpuResource correspondiente a través de los parámetros gpu_resource_config en el archivo de configuración server_config.yaml. DiskResource y CpuResource todavía no pueden editarse en server_config.yaml. GpuResource es la combinación de search_resources y build_index_resources y se denomina {gpu0, gpu1} en el siguiente ejemplo:

3-sample-code.png 3-sample-code.png

3-example.png 3-ejemplo.png

  1. Milvus recibe una solicitud. Los metadatos de la tabla se almacenan en una base de datos externa, que es SQLite o MySQl para un solo host y MySQL para distribuida. Tras recibir una petición de búsqueda, Milvus valida si la tabla existe y la dimensión es coherente. A continuación, Milvus lee la lista TableFile de la tabla.

4-milvus-reads-tablefile-list.png 4-milvus-lee-la-lista-de-archivos-de-tabla.png

  1. Milvus crea una SearchTask. Como el cálculo de cada FicheroTabla se realiza independientemente, Milvus crea una TareaDeBúsqueda para cada FicheroTabla. Como unidad básica de programación de tareas, una SearchTask contiene los vectores objetivo, los parámetros de búsqueda y los nombres de archivo de TableFile.

5-table-file-list-task-creator.png 5-table-file-list-task-creator.png

  1. Milvus elige un dispositivo informático. El dispositivo en el que una SearchTask realiza el cómputo depende del tiempo de finalización estimado para cada dispositivo. El tiempo estimado de finalización especifica el intervalo estimado entre el momento actual y el momento estimado en que finaliza el cómputo.

Por ejemplo, cuando un bloque de datos de una SearchTask se carga en la memoria de la CPU, la siguiente SearchTask está esperando en la cola de tareas de cálculo de la CPU y la cola de tareas de cálculo de la GPU está inactiva. El tiempo de finalización estimado para la CPU es igual a la suma del coste de tiempo estimado de la SearchTask anterior y de la SearchTask actual. El tiempo estimado de finalización para una GPU es igual a la suma del tiempo de carga de los bloques de datos en la GPU y el coste estimado de tiempo de la SearchTask actual. El tiempo de ejecución estimado para una tarea de búsqueda en un recurso es igual al tiempo medio de ejecución de todas las tareas de búsqueda en el recurso. Milvus elige entonces un dispositivo con el menor tiempo de ejecución estimado y le asigna la tarea de búsqueda.

Aquí suponemos que el tiempo de ejecución estimado para la GPU1 es menor.

6-GPU1-shorter-estimated-completion-time.png 6-GPU1-tiempo-estimado-de-terminación-más-corto.png

  1. Milvus añade SearchTask a la cola de tareas de DiskResource.

  2. Milvus mueve SearchTask a la cola de tareas de CpuResource. El subproceso de carga en CpuResource carga cada tarea de la cola de tareas secuencialmente. CpuResource lee los bloques de datos correspondientes en la memoria de la CPU.

  3. Milvus mueve SearchTask a GpuResource. El subproceso de carga en GpuResource copia los datos de la memoria de la CPU a la memoria de la GPU. GpuResource lee los bloques de datos correspondientes en la memoria de la GPU.

  4. Milvus ejecuta SearchTask en GpuResource. Dado que el resultado de una SearchTask es relativamente pequeño, el resultado se devuelve directamente a la memoria de la CPU.

7-scheduler.png 7-scheduler.png

  1. Milvus fusiona el resultado de SearchTask con el resultado de búsqueda completo.

8-milvus-merges-searchtast-result.png 8-milvus-merges-searchtast-result.png

Una vez completadas todas las tareas de búsqueda, Milvus devuelve al cliente el resultado completo de la búsqueda.

Creación de índices

La creación de índices es básicamente lo mismo que el proceso de búsqueda sin el proceso de fusión. No hablaremos de esto en detalle.

Optimización del rendimiento

Caché

Como ya se ha mencionado, los bloques de datos deben cargarse en los dispositivos de almacenamiento correspondientes, como la memoria de la CPU o la memoria de la GPU, antes del cálculo. Para evitar la carga repetitiva de datos, Milvus introduce la caché LRU (Least Recently Used). Cuando la caché está llena, los nuevos bloques de datos desplazan a los antiguos. Puede personalizar el tamaño de la caché mediante el archivo de configuración basándose en el tamaño actual de la memoria. Se recomienda una caché grande para almacenar los datos de búsqueda para ahorrar tiempo de carga de datos y mejorar el rendimiento de la búsqueda.

Solapamiento de la carga de datos y el cálculo

La caché no puede satisfacer nuestras necesidades para mejorar el rendimiento de la búsqueda. Es necesario volver a cargar los datos cuando la memoria es insuficiente o el tamaño del conjunto de datos es demasiado grande. Tenemos que reducir el efecto de la carga de datos en el rendimiento de la búsqueda. La carga de datos, ya sea del disco a la memoria de la CPU o de la memoria de la CPU a la memoria de la GPU, pertenece a las operaciones de E/S y apenas necesita trabajo computacional por parte de los procesadores. Por tanto, consideramos la posibilidad de realizar la carga de datos y el cálculo en paralelo para un mejor uso de los recursos.

Dividimos el cálculo de un bloque de datos en 3 etapas (carga del disco en la memoria de la CPU, cálculo en la CPU, fusión de resultados) o 4 etapas (carga del disco en la memoria de la CPU, carga de la memoria de la CPU en la memoria de la GPU, cálculo en la GPU y recuperación de resultados, y fusión de resultados). Si tomamos como ejemplo el cálculo en 3 etapas, podemos lanzar 3 subprocesos responsables de las 3 etapas para que funcionen como canalización de instrucciones. Dado que los conjuntos de resultados son en su mayoría pequeños, la fusión de resultados no requiere mucho tiempo. En algunos casos, el solapamiento de la carga de datos y el cálculo puede reducir el tiempo de búsqueda a la mitad.

9-sequential-overlapping-load-milvus.png 9-secuencial-solapamiento-carga-milvus.png

Problemas y soluciones

Diferentes velocidades de transmisión

Anteriormente, Milvus utilizaba la estrategia Round Robin para la programación de tareas multi-GPU. Esta estrategia funcionó perfectamente en nuestro servidor de 4-GPU y el rendimiento de búsqueda fue 4 veces mejor. Sin embargo, para nuestros hosts de 2 GPU, el rendimiento no era 2 veces mejor. Hicimos algunos experimentos y descubrimos que la velocidad de copia de datos para una GPU era de 11 GB/s. Sin embargo, para otra GPU, era de 3 GB/s. Tras consultar la documentación de la placa base, confirmamos que ésta estaba conectada a una GPU a través de PCIe x16 y a otra GPU a través de PCIe x4. Es decir, estas GPU tienen velocidades de copia diferentes. Posteriormente, añadimos el tiempo de copia para medir el dispositivo óptimo para cada SearchTask.

Trabajo futuro

Entorno de hardware con mayor complejidad

En condiciones reales, el entorno de hardware puede ser más complicado. Para entornos de hardware con múltiples CPU, memoria con arquitectura NUMA, NVLink y NVSwitch, la comunicación entre CPU/GPU ofrece muchas oportunidades de optimización.

Optimización de consultas

Durante la experimentación, descubrimos algunas oportunidades de mejora del rendimiento. Por ejemplo, cuando el servidor recibe varias consultas para la misma tabla, las consultas pueden fusionarse en determinadas condiciones. Utilizando la localidad de los datos, podemos mejorar el rendimiento. Estas optimizaciones se implementarán en nuestro desarrollo futuro. Ahora ya sabemos cómo se programan y realizan las consultas para el escenario de un único host y múltiples GPU. Seguiremos introduciendo más mecanismos internos para Milvus en los próximos artículos.

Try Managed Milvus for Free

Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.

Get Started

Like the article? Spread the word

Sigue Leyendo