StarRocks Technology Insider: La esencia de la programación vectorizada

 Autor: Kang Kaisen, StarRocks PMC, responsable de la investigación y el desarrollo de la dirección de consulta

Este artículo es una compilación de mi charla de MeetUp fuera de línea en StarRocks. Se divide principalmente en tres partes: la primera parte presenta brevemente los conocimientos básicos de vectorización, la segunda parte explica cómo vectorizar la base de datos y, finalmente, algunas ideas superficiales después de StarRocks. práctica de vectorización.

 

#01

¿Por qué la vectorización puede mejorar el rendimiento de la base de datos?

Las bases de datos analizadas en este artículo se basan todas en la arquitectura de la CPU, y la vectorización de la base de datos generalmente se refiere a la vectorización basada en la CPU. Por lo tanto, la esencia de la optimización del rendimiento de la base de datos es cómo optimizar el rendimiento de un programa basado en la CPU. Esto lleva a dos preguntas clave:

1. Cómo medir el rendimiento de la CPU

2. ¿Qué factores afectan el rendimiento de la CPU?

La respuesta a la primera pregunta se puede resumir con la siguiente fórmula: Tiempo de CPU = Número de instrucción * CPI * Tiempo de ciclo de reloj

  • Número de instrucción Indica el número de instrucciones. Cuando escribe un programa de CPU, se convertirá en instrucciones de CPU cuando finalmente se ejecute, y la cantidad de instrucciones generalmente depende de la complejidad del programa.

  • CPI es la abreviatura de (Ciclo por instrucción), que se refiere al ciclo requerido para ejecutar una instrucción.

  • Clock Cycle Time se refiere al tiempo requerido para un ciclo de CPU, que está fuertemente relacionado con las características de hardware de la CPU.

Lo que podemos cambiar a nivel de software son los dos primeros: Número de Instrucción y CPI. Entonces, la pregunta es, específicamente para un programa de CPU, ¿qué factores afectarán el número de instrucción y el CPI?

Sabemos que la ejecución de instrucciones de la CPU se divide en los siguientes 5 pasos:

1. Obtener instrucción

2. Decodificación de instrucciones

3. Ejecuta el comando

4. Acceso a la memoria

5. Escriba el resultado de nuevo en el registro

El Frontend de la CPU es responsable de las dos primeras partes, y el Backend es responsable de las últimas tres partes. En base a esto, Intel propuso el método de análisis de rendimiento de microarquitectura de CPU de "Método de análisis de microarquitectura de arriba hacia abajo", como se muestra en la siguiente figura:

 

Para conocer el contenido específico del método de análisis de microarquitectura de arriba hacia abajo, puede consultar los documentos pertinentes. Este artículo no lo ampliará. Para facilitar su comprensión, podemos simplificar la figura anterior a la siguiente figura (no completamente precisa) :

 

Es decir, los cuellos de botella de rendimiento que afectan a un programa de CPU incluyen principalmente cuatro puntos principales: Retiro, Mala especulación, Frontend Bound y Backend Bound.Las razones principales (no del todo precisas) causadas por los cuatro puntos de cuello de botella son: falta de optimización de instrucciones SIMD , errores de predicción de bifurcación, error de caché de instrucciones, error de caché de datos.

En correspondencia con la fórmula de cálculo del tiempo de CPU anterior, podemos sacar las siguientes conclusiones:

 

La vectorización de la base de datos mejorará los cuatro puntos anteriores, que se explicarán más adelante.Hasta ahora, este artículo explica en principio por qué la vectorización puede mejorar el rendimiento de la base de datos.

 

#02

Conceptos básicos de vectorización

Antes de entender cómo se vectorizan las bases de datos, comprendamos algunos conceptos básicos de vectorización.

Tenga en cuenta que en el segundo capítulo, el término vectorización puede entenderse como equivalente a SIMD, que solo se refiere a la vectorización CPU SIMD en un sentido estricto, que es diferente de la vectorización generalizada en el campo de la base de datos.

 

1. Introducción a SIMD

SIMD es la abreviatura de Single Instruction Multiple Data, lo que significa que una sola instrucción puede operar múltiples flujos de datos, a diferencia del SISD tradicional, que es una sola instrucción y un solo flujo de datos.

Como se muestra en la figura anterior, para el más simple A + B = C, si queremos calcular 4 conjuntos de sumas, el SISD tradicional necesita ejecutar 8 instrucciones de carga (4 veces para A y B), 4 instrucciones de suma y 4 Guardar instrucciones. . Pero cuando usamos SIMD de 128 bits de ancho, solo necesitamos 2 instrucciones de carga, 1 instrucción de adición y 1 instrucción de almacenamiento, por lo que, en teoría, podemos obtener una mejora de rendimiento 4 veces mayor. El ancho de bits del registro vectorizado actual se ha desarrollado a 512 bits y, en teoría, la operación de suma de Int se puede acelerar 16 veces.

 

2. ¿Cómo desencadenar la vectorización?

Como se mencionó anteriormente, las instrucciones SIMD pueden generar enormes ganancias de rendimiento y, naturalmente, los desarrolladores de bases de datos necesitan comprender y dominar cómo hacer la programación SIMD.

Como se muestra en la figura anterior, generalmente hay 6 métodos para la vectorización de disparadores SIMD Estos 6 métodos son de arriba hacia abajo, y los requisitos para los ingenieros son cada vez más altos, y cada vez más cosas deben escribirse manualmente. 

  • El primero es la vectorización automática por parte del compilador. El código no necesita procesamiento ni cambios especiales, y el compilador convierte automáticamente el código escalar predeterminado en código vectorizado. Sin embargo, el compilador solo puede manejar programas relativamente simples de forma predeterminada.Puede buscar los Casos compatibles específicos en Internet, y hay mucha información;

  • La segunda es que le damos al compilador algunas pistas para darle más información y contexto, y el compilador también puede generar instrucciones SIMD;

  • El tercero es usar API de programación paralela como OpenMP o Intel Cilk, y los desarrolladores pueden agregar algunos Pragmas para generar instrucciones SIMD;

  • El cuarto es usar algunas clases contenedoras para SIMD Intrinsics;

  • El quinto es usar la programación SIMD Intrinsics directamente;

  • El último es escribir código ensamblador directamente.

En el proyecto StarRocks, nuestro principio de vectorización es activar la vectorización automática del compilador tanto como sea posible, es decir, los tipos primero y segundo. Para las operaciones que no se pueden vectorizar automáticamente pero el rendimiento es crítico, usaremos la forma SIMD Intrinsics para vectorizar manualmente.

Con respecto a cómo desencadenar la automatización del compilador, cómo agregar Sugerencia al compilador para desencadenar la vectorización y cómo vectorizar manualmente a través de SIMD Intrinsics, puede consultar la información en la parte de vectorización de mi blog personal "Materiales de aprendizaje de bases de datos", que no se repetirá en este artículo.

"Materiales de aprendizaje de bases de datos": https://blog.bcmeng.com/post/database-learning.html

 

3. ¿Cómo verificar que el programa genera código vectorizado?

Cuando el código de un proyecto es complejo, cómo garantizar que el código desencadene la vectorización es un problema muy común. Podemos comprobar de dos formas:

La primera es agregar algunas opciones de compilación al compilador, el compilador generará si algún código desencadena la vectorización y el motivo de la no vectorización. Por ejemplo, el compilador GCC, podemos agregar las opciones de compilación -fopt-info-vec-all o -fopt-info-vec-optimized, -fopt-info-vec-missed, -fopt-info-vec-note. Resultados como se muestra a continuación:

En el segundo método, podemos ver directamente el código ensamblador final ejecutado, por ejemplo, usando sitios web como https://gcc.godbolt.org/ o herramientas como Perf y Vtune, si los registros en el código ensamblador son xmm, ymm, zmm o instrucciones Comenzar con v generalmente significa que el código activa la vectorización.

 

#03

Vectorización de base de datos

 

1. El núcleo de la vectorización de bases de datos

Han pasado más de 2 años desde que el motor de vectorización de StarRocks escribió la primera línea de código para convertirse en un ejecutor de consultas maduro, estable y líder en el mundo. Según nuestra experiencia, la vectorización de bases de datos no se trata solo de activar la vectorización SIMD de la CPU, sino un proyecto enorme y sistemático de optimización del rendimiento.

 

2. Desafíos de la vectorización de bases de datos

Los desafíos de la vectorización de bases de datos incluyen principalmente los siguientes:

1. Diseño completo en columnas: el disco, la memoria y la red son todos diseños en columnas, lo que significa una reconstrucción completa del motor de almacenamiento y el motor informático.

2. Todos los operadores, expresiones y funciones admiten la vectorización: esto significa años-hombre de trabajo

3. Utilice las instrucciones SIMD tanto como sea posible para los cálculos de operadores y expresiones: esto significa mucha optimización cuidadosa de Case By Case

4. Rediseñar la gestión de la memoria: porque los datos procesados ​​han cambiado de una fila a miles de filas

5. Rediseñar la estructura de datos: por ejemplo, es necesario cambiar las estructuras de datos de los operadores principales, como Unir, Agregar y Ordenar.

6. El rendimiento general se mejora más de 5 veces: el rendimiento de todos los operadores y expresiones debe mejorarse más de 5 veces, lo que significa una optimización del rendimiento integral y sistemática, y todos los operadores y expresiones importantes no deben tener deficiencias en el rendimiento.

 

3. Puntos clave de la vectorización de operadores y expresiones

La vectorización de bases de datos se refleja principalmente en la vectorización de operadores y expresiones en ingeniería, y el punto clave de la vectorización de operadores y expresiones es una oración: Cálculo por lotes por columna, como se muestra en la siguiente figura:

 

De acuerdo con el método de análisis de arriba hacia abajo de Intel, Batch optimiza los errores de predicción de rama y la pérdida de caché de instrucciones, por columna optimiza la pérdida de caché de datos y es más fácil activar la optimización de instrucciones SIMD.

Lote es en realidad más fácil de hacer. La dificultad es cómo procesar algunos operadores importantes, como Unir, Agregar, Ordenar, Mezclar, etc. por columna, y lo que es aún más difícil es cómo activar SIMD tanto como sea posible mientras se procesa por columna Optimización de instrucciones. El procesamiento columna por columna y la optimización de instrucciones SIMD de cada operador se explicarán en detalle en la siguiente serie de artículos sobre "Análisis de código fuente de consultas de StarRocks".

 

4. Cómo optimizar el rendimiento de la vectorización de bases de datos

Como se mencionó anteriormente, la vectorización de bases de datos es un proyecto enorme y sistemático de optimización del rendimiento. En los últimos dos años, hemos implementado cientos de puntos de optimización de varios tamaños. Resumí la experiencia de optimización del rendimiento de la vectorización de StarRocks durante más de dos años en 7 aspectos (tenga en cuenta que dado que la ejecución de la vectorización es una estrategia de ejecución de un solo subproceso, la siguiente experiencia de optimización del rendimiento no implica simultaneidad):

1. Bibliotecas de terceros de alto rendimiento: en algunas partes o detalles, ya existe una gran cantidad de bibliotecas de código abierto con un rendimiento excelente. En este momento, es posible que no necesitemos implementar algunos algoritmos o estructuras de datos desde cero. -Las bibliotecas de terceros de rendimiento pueden acelerar todo el proceso del progreso del proyecto. En StarRcoks, utilizamos excelentes bibliotecas de terceros como Parallel Hashmap, Fmt, SIMD Json e Hyper Scan.

2. Estructuras de datos y algoritmos: las estructuras de datos y los algoritmos eficientes pueden reducir directamente la cantidad de instrucciones de la CPU en órdenes de magnitud. En StarRocks 2.0, presentamos un diccionario global de baja cardinalidad, que puede convertir operaciones relacionadas con cadenas en operaciones relacionadas con la configuración a través del diccionario global. Como se muestra en la figura a continuación, StarRcoks cambia el Agrupar por anterior basado en dos cadenas en un Agrupar por basado en un número entero, de modo que Escaneo, Cálculo de hash, Igualdad, Memcpy y otras operaciones tendrán una mejora de rendimiento varias veces, y la consulta completa eventualmente tendrá un aumento de rendimiento de 3x.

 

3. Optimización adaptativa: en muchos casos, si tenemos más contexto o más información, podemos hacer optimizaciones más específicas, pero a veces estos contextos o información solo se pueden obtener cuando se ejecuta la consulta, por lo que debemos ajustar dinámicamente la estrategia de ejecución. de acuerdo con la información de contexto durante la ejecución de la consulta, lo que se denomina optimización adaptativa. La siguiente figura muestra un ejemplo de selección dinámica del filtro Join Runtime de acuerdo con la tasa de selección Hay 3 puntos clave: 

  a.Si un Filtro difícilmente puede filtrar datos, no elegimos;

  b. Si un Filtro casi puede filtrar los datos, solo mantenemos un Filtro; 

  c. Mantener como máximo 3 filtros útiles

 

4. Optimización SIMD: como se muestra en la figura a continuación, StarRcoks usa muchas instrucciones SIMD en operadores y expresiones para mejorar el rendimiento.

 

5. Optimización de bajo nivel de C++: incluso con la misma estructura de datos, el mismo algoritmo y diferentes implementaciones de C++, el rendimiento puede variar varias veces. Por ejemplo, Move se convierte en Copy, si Vector es Reserve, Inline y varias optimizaciones relacionadas con bucles, cálculos en tiempo de compilación, etc.

6. Optimización de la gestión de la memoria: cuando el tamaño del lote es mayor, la concurrencia es mayor, la aplicación y el lanzamiento de la memoria son más frecuentes y la gestión de la memoria tiene un mayor impacto en el rendimiento. Implementamos un grupo de columnas para reutilizar la memoria de las columnas, lo que optimizó significativamente el rendimiento general de las consultas. La siguiente figura es una representación de código de la optimización de memoria de la función de agregación de HLL. Al cambiar la asignación de memoria de HLL a asignación de bloques y realizar la reutilización, el rendimiento de la agregación de HLL se mejora directamente 5 veces.

 

7. Optimización de caché de CPU: los estudiantes que optimizan el rendimiento deben estar familiarizados con los datos de la figura a continuación y saber que el impacto de la falta de caché de CPU en el rendimiento es muy grande, especialmente después de que habilitamos la optimización SIMD, comienza el cuello de botella del programa de CPU Bound se convierte en Memory Bound. Al mismo tiempo, también debemos tener claro que con la optimización continua del programa, el cuello de botella de rendimiento del programa seguirá cambiando.

 

El siguiente código muestra un ejemplo del uso de Prefetch para optimizar Cache Miss. Necesitamos saber que Prefetch debe ser el último intento de optimizar el caché de la CPU, porque el tiempo y la distancia de Prefetch son difíciles de comprender y deben probarse por completo.

 

#04

Reflexiones superficiales sobre la ingeniería de vectorización de StarRocks

En primer lugar, los principios subyacentes de muchas cosas son similares: cuando entiendo profundamente la microarquitectura de la CPU, descubrí que la microarquitectura de la CPU y la arquitectura general de la base de datos también son muy similares. Por ejemplo, la CPU y StarRocks se dividen en Frontend y Backend, y Frontend de CPU Responsable de obtener, codificar y decodificar instrucciones, Backend es responsable de la ejecución de instrucciones e interacción de datos, StarRocks' Frontend es responsable de analizar y planificar SQL, y Backend es responsable de la ejecución de SQL y la interacción de almacenamiento. Cuanto más sepa sobre el sistema y la arquitectura, más profundos serán sus sentimientos.

En segundo lugar, la creación de una base de datos de alto rendimiento requiere no solo una arquitectura excelente y razonable, sino también detalles de ingeniería extremadamente excelentes. Esto parece obvio, pero no lo es, si reconoces esto, cuando quieras construir una base de datos con un rendimiento extremo, no diseñarás todo el sistema a partir de los detalles y niveles de algoritmo como ClickHouse, ni lenguajes como Java o Ir a ser elegido para implementar la capa de ejecución de consultas y la capa de almacenamiento.

 

En tercer lugar, la fusión de vectorización y compilación de consultas. Sabemos que la vectorización y la compilación de consultas son dos tipos de métodos de ejecución de consultas, que son ortogonales y no entran en conflicto. Sin embargo, la mayoría de las bases de datos de código abierto en la industria eligen el método de vectorización. De hecho, podemos usar completamente el método de compilación de consultas. , que combina información contextual para generar código vectorizado más robusto. Además, en los últimos años, la industria ha mejorado las muchas deficiencias de la propia compilación de consultas.

Cuarto, pruebe hardware nuevo como GPU y FPGA. Sabemos que si una cosa tarda un mes en lograr 80 puntos, entonces puede tomar 1 año lograr 90 puntos desde 80 puntos, y puede demorar 2 años lograr 99 puntos desde 90 puntos. Si bien el rendimiento basado en la arquitectura de la CPU aún no ha alcanzado el límite, es posible que se necesite mucha energía para lograr un gran avance. Podríamos considerar abrir nuevas pistas y campos de batalla en el nuevo hardware.

Quinto, el desafío es imposible. En los dos años desde que comenzamos nuestro negocio, nuestro equipo ha implementado el motor de vectorización, el optimizador CBO y el motor paralelo Pipeline desde cero... Mirando los avances y logros uno tras otro, mi mayor sentimiento es que siempre debemos creer en nosotros mismos. y desafiar lo imposible. Finalmente, recomiendo un libro  "Thinking Like a Rocket Scientist: Turning the Impossible into Possible" . Este libro interpreta a la perfección los valores del equipo de StarRocks: cómo superar las dificultades pensando en grande y atreviéndote a pensar Difícil, iteración rápida y una gran idea tras otra.

 

Este número de StarRocks Technology Insider ha terminado. Si está ansioso por aprender, debe haber aprendido algunas cosas nuevas o tener alguna nueva confusión. También puede escanear el código QR del grupo de usuarios a continuación para unirse a la comunidad de StarRocks para comunicarse. !

 

 

Acerca de StarRocks 

Desde su establecimiento hace más de dos años, StarRocks se ha centrado en crear la nueva generación de base de datos MPP de escena completa ultrarrápida más importante del mundo, ayudando a las empresas a establecer un nuevo paradigma de análisis de datos "extremadamente rápido y unificado" y ayudando a las empresas para digitalizar completamente sus operaciones.

En la actualidad, ha ayudado a más de 110 usuarios a gran escala como Tencent, Ctrip, SF Express, Airbnb, Didi, JD.com y ZhongAn Insurance a crear nuevas capacidades de análisis de datos y la cantidad de servidores StarRocks que se ejecutan de manera estable en el entorno de producción llega a miles. 

En septiembre de 2021, el código fuente de StarRocks está abierto y la cantidad de estrellas en GitHub ha superado las 3100. La comunidad global de StarRocks ha crecido rápidamente. Hasta el momento, ha habido más de 100 contribuyentes y más de 5000 usuarios de la comunidad, atrayendo a docenas de líderes de la industria nacionales y extranjeros para participar en la construcción conjunta.

{{o.nombre}}
{{m.nombre}}

Supongo que te gusta

Origin my.oschina.net/u/5658056/blog/5567276
Recomendado
Clasificación