El Portal de las Tecnologías para la Innovación

Acelerar los Escaneos de Parquet Apache en Apache Spark con GPU

A medida que los tamaños de los datos han crecido en empresas de todas las industrias, Parquet Apache se ha convertido en un formato prominente para almacenar datos. 

Apache Parquet es un formato de almacenamiento columnar diseñado para un procesamiento de datos eficiente a escala. Al organizar los datos por columnas en lugar de filas, Parquet permite consultas y análisis de alto rendimiento, ya que solo puede leer las columnas necesarias para una consulta en lugar de escanear filas completas de datos. El diseño de datos eficiente de Parquet, lo ha convertido en una opción popular en el ecosistema de análisis moderno, específicamente con las cargas de trabajo de Apache Spark.

El RAPIDS Accelerator para Apache Spark construido sobre cuDF admite Parquet como formato de datos para leer y escribir datos de manera acelerada en GPU. Para muchas cargas de trabajo de Spark a gran escala donde los tamaños de entrada de datos están en terabytes, tener escaneos de parquet eficientes es fundamental para lograr un buen rendimiento en tiempo de ejecución.

En esta publicación, discutimos cómo mitigar las limitaciones de ocupación causadas por usos de registro más altos y compartir resultados de referencia. 

Formato de datos de parquet Apache

El formato de archivo Parquet permite almacenar los datos en formato columnar utilizando trozos de columna ensamblados en grupos de filas. Los metadatos son distintos de los datos para permitir la división de columnas en varios archivos según sea necesario (Figura 1).

Un diagrama muestra un archivo con un número mágico (4 bytes) y luego un grupo de filas, columna, página, encabezado de página, niveles de repetición, niveles de definición y valores para múltiples grupos de filas. Una sección de ruptura muestra el pie de página, FileMetaData (ThriftCompactProtocol), versión, esquema, pares de clave/valor adicionales, metadatos de grupos de filas, metadatos de columna, longitud del pie de página y el número mágico nuevamente.
Figura 1. Formato de archivo de parquet (fuente: Formato de Archivo)

El formato Parquet admite una variedad de tipos de datos. Los metadatos especifican cómo deben interpretarse estos tipos, lo que permite que estos tipos representen tipos lógicos más complejos, como marcas de tiempo, cadenas, decimales, etc. 

También puede usar metadatos para especificar estructuras más complejas, como tipos anidados y listas. Los datos se pueden codificar en una variedad de formatos diferentes, como valores simples, diccionarios, codificación de longitud de ejecución, empaque de bits y más.

- BOOLEAN: 1 bit boolean- INT32: 32 bit signed ints- INT64: 64 bit signed ints- INT96: 96 bit signed ints- FLOAT: IEEE 32-bit floating point values- DOUBLE: IEEE 64-bit floating point values- BYTE_ARRAY: arbitrarily long byte arrays- FIXED_LEN_BYTE_ARRAY: fixed length byte arrays

Parquet sobre limitaciones de ocupación de GPU

Antes de la RAPIDS Accelerator para Apache Spark, la implementación anterior de los escaneos de parquet fue un núcleo cuDF monolítico que admitía todos los tipos de columnas de parquet en un solo conjunto de código de procesamiento. 

A medida que la adopción de Spark en GPU aumentó para los clientes con datos de Parquet, se invirtió más tiempo en comprender las características de rendimiento de los escaneos de Parquet dado el componente crítico del rendimiento que representaba. Hay varios recursos generales que tienen en cuenta la eficiencia con la que un kernel puede operar:

  • Microprocesadores de transmisión (SM): Unidad de procesamiento principal de la GPU, que es responsable de ejecutar tareas informáticas.
  • Memoria compartida: Memoria en chip de GPU que se asigna por bloque de subprocesos, de modo que todos los subprocesos en el mismo bloque puedan acceder a la misma memoria compartida.
  • Registros: Memoria GPU rápida en chip que almacena información utilizada por un solo hilo para las operaciones de cómputo que ejecutan los SM.

A medida que analizamos los escaneos de parquet, observamos que la ocupación general de la GPU era menor de lo deseado debido a los límites de registro. El uso del registro está determinado por cómo el compilador CUDA genera código basado en la lógica del kernel más la gestión de datos. 

Para el núcleo monolítico de Parquet, la complejidad para admitir todos los tipos de columnas creó un núcleo grande y complejo con alta memoria compartida y uso de registro. Si bien un solo núcleo monolítico puede haber consolidado el código juntos, su complejidad limitó los posibles tipos de optimizaciones y dio como resultado limitaciones de rendimiento a escala. 

Un diagrama muestra las fases de bucle de la decodificación de flujo de nivel en función del tipo (plano, anidado o listas); decodificación de índice de diccionario o decodificación booleana si corresponde; y copia de datos en función del tipo (ancho fijo o cadenas).
Figura 2. Parquet monolítico kernel en GPU

La Figura 2 representa el bucle de procesamiento de datos de Parquet en la GPU. Cada uno de los bloques es una cantidad grande y compleja de código del kernel, que puede tener sus propios requisitos de memoria compartida. Muchos de los bloques dependen del tipo, lo que conduce a un núcleo hinchado cargado en la memoria.

Específicamente, una de las limitaciones era cómo se decodificaban los bloques de parquet dentro de las urdimbres. Había una dependencia en serie para que las urdimbres esperaran a que las urdimbres ordenadas previamente se completaran antes de procesar sus bloques de datos. Esto permitió que diferentes partes del proceso de decodificación ocurrieran en diferentes deformaciones, pero creó una dependencia ineficiente para el trabajo que se realizará en la GPU. 

Pasar a un algoritmo de decodificación de todo el bloque fue crítico para el rendimiento, pero habría aumentado el recuento de registros y la ocupación limitada aún más debido a su intercambio de datos agregado y la complejidad de sincronización. 

Microkernels de parquet en cuDF

Para mitigar las limitaciones de ocupación causadas por usos de registro más altos, probamos una idea inicial de un núcleo más pequeño para preprocesar datos de tipo de lista en Parquet. Separamos un fragmento de código del núcleo monolítico en un núcleo autónomo y los resultados fueron impresionantes. Los puntos de referencia generales mostraron tiempos de ejecución más rápidos y los rastros de GPU demostraron una mejor ocupación.

Después de eso, probamos el mismo enfoque para diferentes tipos de columnas. Los microkernels para varios tipos utilizaron plantillas C++ para reutilizar las capacidades. Esto simplificó el código para el mantenimiento y la depuración por tipo.

Un diagrama enumera las fases de procesamiento anteriores sin las rutas de bucle o bifurcadas: decodificación de flujo de nivel para nulos o listas; procesamiento de flujo de nivel para tipos planos, tipos anidados y luego listas; decodificación de índice de diccionario; decodificación booleana; copia de datos para ancho fijo; y copia de datos para cadenas.
Figura 3. Enfoque de microkernel de parquet en GPU

El enfoque de microkernel de Parquet aprovecha la optimización del tiempo de compilación para pasar solo por las rutas de código necesarias para procesar un tipo determinado. En lugar de un núcleo monolítico que contiene todas las rutas de código posibles, puede generar muchos microkernels individuales que contienen solo el código necesario para esa ruta. 

Esto se puede hacer usando if constexpr en el momento de la compilación, de modo que el código se lea normalmente, pero no incluya ninguna ruta de código que nunca se tome para una combinación particular de atributos de datos (cadenas o ancho fijo, listas o listas no, etc.).

Aquí hay un ejemplo simple para procesar columnas de tipo de ancho fijo. Puede ver que la mayoría del procesamiento no es necesario y se omite en el nuevo enfoque de microkernel. Solo se necesita la copia de datos para este tipo.

El diagrama de bucle de la Figura 2 tiene todas las fases tachadas, excepto la copia de datos (ancho fijo).
Figura 4. Enfoque de microkernel de parquet para ancho fijo

Para resolver el cuello de botella entre las deformaciones, los nuevos microkernels permitieron procesar bloques enteros en cada paso para que las urdimbre pudieran procesar datos de forma independiente de manera más eficiente. Esto fue especialmente significativo para que las cadenas permitieran que un bloque completo de 128 subprocesos en la GPU copiara cadenas donde la implementación anterior solo usaba una urdimbre para copiar cadenas.

Ejecutamos puntos de referencia locales utilizando una GPU NVIDIA RTX A5000 de 24GB con datos comprimidos de Parquet Snappy de 512 MB precargados en búferes de dispositivos. Para probar lecturas fragmentadas, leemos fragmentos de 500 KB a la vez. Los datos de prueba incluyeron algunas variaciones:

  • Cardinalidad 0 y 1000
  • Longitud de ejecución 1 y 32
  • 1% nulos
  • Uso adaptativo del diccionario para ser utilizado si se repitieron los datos

La Figura 5 muestra los resultados en términos de mejora del rendimiento en todos los tipos de columnas de parquet con el nuevo enfoque de microkernel en la GPU.

Un gráfico de barras muestra las mejoras de rendimiento en todos los tipos: entero plano + float, bool plano, columnas anidadas, columnas de lista y cadenas con diccionario.
Figura 5. Mejoras de rendimiento con el enfoque de microkernel de Parquet en GPU

Una optimización para la lectura fragmentada de las columnas de la lista también mejoró el rendimiento en un 117% para las lecturas de 500 KB.

Comenzando con Apache Spark en GPU

El parquet es un formato de datos clave en amplia adopción para el procesamiento de datos grandes. La GPU puede acelerar el escaneo de datos de parquet en Apache Spark mediante el uso de microkernels optimizados en cuDF. 

Las empresas pueden aprovechar el RAPIDS Accelerator para Apache Spark para migrar sin problemas las cargas de trabajo de Apache Spark a las GPU NVIDIA. RAPIDS Accelerator para Apache Spark aprovecha las GPU para acelerar el procesamiento al combinar la potencia de la biblioteca cuDF de RAPIDS y la escala del marco informático distribuido de Spark. Ejecute las aplicaciones Apache Spark existentes en GPU sin cambios de código iniciando Spark con RAPIDS Accelerator para el archivo JAR del complemento Apache Spark. 

Obtenga información práctica con el procesamiento de escaneo de parquet y el acelerador RAPIDS para Apache Spark con el Spark RAPIDS Aceleración de parquet Cuaderno colab. NVIDIA Blog. M. A., P. M. y D- B. Traducido al español

Artículos relacionados

Scroll al inicio