Transformações Eficientes no cuDF com Compilação JIT
Sources: https://developer.nvidia.com/blog/efficient-transforms-in-cudf-using-jit-compilation, developer.nvidia.com
TL;DR
- A compilação JIT no cuDF permite fusão de kernels compilando kernels fused em tempo de execução, reduzindo a materialização de intermediários e melhorando a localidade de dados.
- A partir do cuDF 25.08, transformações JIT adicionam suporte a operadores não disponíveis no AST, incluindo o operador ternário e funções de string como find e substring.
- Benchmarks mostram ganhos de cerca de 2x–4x em várias traduções de string, com menor contagem de kernels e melhor localidade de cache impulsionando a melhoria.
- Existe um custo inicial de tempo de compilação para JIT (~600 ms por kernel) se nenhum kernel em cache for encontrado; execuções subsequentes são bem mais rápidas (~3 ms para carregar o kernel em cache).
Contexto e antecedentes
O cuDF, parte da suíte RAPIDS, oferece um conjunto amplo de algoritmos ETL para processar dados em GPUs. Para usuários de pandas, o cuDF oferece algoritmos acelerados com zero mudança de código via cudf.pandas. Para desenvolvedores C++, o cuDF expõe um submódulo em C++ que usa visões sem propriedade como entrada e tipos com propriedade como saída, facilitando o raciocínio sobre ciclos de vida dos dados e ampliando a composibilidade das APIs. Uma limitação conhecida desse modelo de várias etapas com materialização de intermediários é o aumento do tráfego de memória na GPU. A fusão de kernels surge como solução poderosa: executar múltiplos cálculos dentro de um único kernel GPU pode melhorar o throughput e reduzir o tráfego de memória. Este post explica como a compilação JIT traz fusão de kernels para o modelo de programação C++ do cuDF, entregando maior throughput e utilização mais eficiente de memória e compute na GPU. Em expressões escalares, um árvore de operandos e operadores é avaliada para produzir uma única coluna de saída. O cuDF oferece três abordagens: precompiled, AST e JIT transform. A abordagem precompiled chama APIs públicas do libcudf para cada operador, recursivamente, computando a árvore. A vantagem é o apoio mais amplo a tipos de dados e operadores, mas a desvantagem é a materialização de intermediários na memória global da GPU entre operações. A abordagem AST usa a API compute_column para percorrer a árvore completa com um kernel especializado, com paralelismo por linha de GPU. O interpretador AST facilita fusão de kernels em cuDF, mas tem limitações de suporte a tipos de dados e operadores. A abordagem JIT transform em cuDF usa NVRTC para compilar um kernel personalizado que realiza transformações arbitrárias, criando kernels fundidos em tempo de execução. NVRTC é uma biblioteca de compilação em tempo de execução para CUDA C++ que cria kernels fundidos durante a execução. A principal vantagem do JIT é que ele utiliza kernels otimizados para a expressão a ser avaliada, permitindo uma alocação eficiente de recursos da GPU e melhor aproveitamento de caches, ao invés de reservar registradores para cenários de pior caso. A partir do cuDF 25.08, o JIT transform adiciona suporte a operadores que o processamento AST não suportava, incluindo o operador ternário para if-else e funções de string como find e substring. O principal inconveniente é o tempo de compilação do kernel (~600 ms) na primeira execução sem kernel em cache, ou a dependência de cache para reduzir esse overhead. O reuso de kernels no cache é detalhado neste post. O repositório rapidsai/cudf no GitHub fornece uma suíte de exemplos string_transforms para demonstrar a manipulação de strings com abordagens precompiled e JIT transform. Os casos de exemplo são UDFs de processamento de strings, como extract_email_jit e extract_email_precompiled, que exploram uma computação que valida o formato de um endereço de e-mail e extrai o provedor, retornando unknown para entradas malformadas. A Figura 2 demonstra o exemplo extract_email_precompiled com a lógica destacada para localizar o ’@’ e o ’.’; a Figura 3 demonstra o exemplo extract_email_jit, que usa um UDF em C++ bruto para simplificar a lógica com if-else e retornos antecipados. O objetivo é ilustrar como o JIT pode tornar o processamento de strings mais eficiente ao reduzir intermediários. O uso do JIT resulta em runtimes mais rápidos quando comparado ao pré-compilado. A principal fonte de melhoria é a menor contagem de kernels. Quando o mesmo trabalho pode ser feito com menos kernels, as localizações de cache são mais eficazes e os registradores da GPU podem manter intermediários, em vez de armazená-los na memória global para kernels subsequentes. A Figura 4 mostra a linha do tempo das execuções de kernels para os três exemplos de transformação de strings, onde barras azuis representam os kernels lançados de forma precompiled e barras verdes representam os kernels lançados pelo JIT transform. A coleta de dados ocorreu com 200M de linhas de entrada (12,5 GB) em hardware NVIDIA GH200 Grace Hopper com recursos assíncronos de memória.
O que há de novo
A rota JIT transform do cuDF permite gerar kernels fundidos em tempo de execução para a transformação ou UDF específica. Isso é feito pela compilação de um kernel personalizado com NVRTC, permitindo execução especializada e fundida sem a necessidade de materializar intermediários entre etapas. Principais capacidades novas incluem:
- Fusão baseada em JIT de expressões, incluindo operações escalares e de string que não eram suportadas pelo AST anteriormente.
- Suporte a operadores como o ternário e funções de string como find e substring na trajetória JIT (desde cuDF 25.08).
- Benefícios de performance demonstrados em tarefas de transformação de strings; o caminho JIT reduz a contagem de kernels, melhora a localidade de cache e mantém mais dados nos registradores da GPU.
- Medições com o NVIDIA GH200, usando 200M de linhas e 12,5 GB de dados, evidenciam ganhos do JIT com a fusão de kernels.
- Um mecanismo de cache de kernel é utilizado para amortizar o custo de compilação. A primeira execução de um kernel JIT pode levar cerca de 600 ms para compilar, enquanto carregá-lo a partir do cache leva cerca de 3 ms. Subsequentes execuções no mesmo processo não têm overhead adicional. Na prática, os exemplos string_transforms demonstram como o uso de JIT pode simplificar a lógica de processamento de strings, por exemplo, com o extract_email_jit: ele localiza os caracteres e separa o provedor em uma única etapa, reduzindo a necessidade de construção de várias colunas intermediárias que ocorrem na abordagem precompiled.
Por que isso importa (impacto para desenvolvedores/empresas)
Para desenvolvedores que constroem pipelines de dados em GPUs, transformar o modo como as transformações são executadas pode levar a maior throughput e menor uso de memória. Fusão de kernels reduz overhead de lançamento e tráfego de memória entre operações, o que é particularmente relevante para workloads de strings e transformações complexas. Do ponto de vista corporativo, a capacidade de acelerar UDFs sem reescrever código em C++ ou adotar novas abstrações facilita ciclos de desenvolvimento mais curtos. A ampliação do suporte de cuDF 25.08 para operadores ternários e funções de string amplia as situações em que a fusão JIT pode ser aplicada, promovendo maior escalabilidade para análises em GPUs modernas. Em termos práticos, a JIT também ajuda a processar tamanhos de dados maiores antes de atingir limites de memória da GPU devido à menor materialização de intermediários.
Detalhes técnicos ou Implementação
- Abordagens de avaliação em cuDF: precompiled, AST e JIT transform. Precompiled usa APIs libcudf por operador, com amplo suporte, mas pode exigir intermediários. AST percorre a árvore de expressão com um kernel dedicado, com fusão potencial, porém com limitações de suporte a operadores/tipos. JIT transform utiliza NVRTC para gerar um kernel fundido em tempo de execução para a expressão ou UDF.
- Vantagens do JIT: kernels fundidos específicos para a transformação, melhor alocação de recursos e localidade de cache, reduzindo o número total de kernels.
- Novos operadores no JIT desde cuDF 25.08: o operador ternário e funções de string como find e substring.
- Cache de kernels e aquecimento: a primeira execução exige o tempo de compilação (~600 ms por kernel) na ausência de kernel em cache; carregar do cache leva ~3 ms. Execuções subsequentes no mesmo processo não têm overhead de JIT.
- Medições envolvem 200M de linhas, 12,5 GB, e hardware GH200 com memória assíncrona, com comparação entre kernels precompiled (azuis) e JIT (verdes).
Notas de implementação na prática
- Para facilitar testes e implantação, a RAPIDS fornece containers Docker com releases e builds noturnos para cuDF, incluindo binários pré-compilados e o submódulo libcudf.
- Os usuários podem instalar binários via canal Conda rapidsai-nightly, e acessar os exemplos string_transforms e testes associados.
Principais conclusões
- Transformações JIT permitem kernels fundidos em tempo de execução, reduzindo tráfego de memória e aumentando o throughput para transformações de strings e escalares.
- O cuDF 25.08 expande o suporte JIT para incluir operadores como ternário e funções de string como find e substring.
- Observa-se, em cenários práticos, ganhos de 2x–4x para algumas cargas de trabalho de transformação de strings, impulsionados por menos kernels e melhor localidade de cache; dados maiores tendem a apresentar benefícios mais perceptíveis.
- O custo inicial de compilação do JIT (~600 ms por kernel) pode ser amortizado com caches apropriados; execuções subsequentes tendem a ser rápidas (~3 ms para carregar o kernel cache).
- O uso efetivo do JIT pode depender do pré-carregamento de kernels no cache para obter vantagens já nas primeiras leituras de dados.
Perguntas frequentes
-
O que é o JIT transform no cuDF?
É uma rota de tempo de execução que utiliza NVRTC para compilar um kernel fundido específico para a transformação ou UDF, permitindo fusão de kernels.
-
Como o JIT se compara a abordagens precompiled e AST?
Precompiled oferece suporte amplo a operadores, mas pode exigir intermediários; AST facilita fusões em alguns cenários, porém tem limitações; JIT oferece kernels fundidos com potencial de maior throughput, porém com custo inicial de compilação e dependência de cache.
-
Como funciona o cache de kernels e por que é importante?
Kerns compilados são armazenados no caminho definido por LIBCUDF_KERNEL_CACHE_PATH. Se encontrado no cache, o carregamento é rápido (~3 ms). Caso contrário, a compilação leva ~600 ms por kernel; kernels compilados ficam disponíveis para usos futuros.
-
O que influencia os ganhos de performance do JIT transform?
O número total de kernels, o tamanho dos dados e o tráfego de memória determinam os ganhos. Dados maiores costumam ter maiores benefícios, especialmente quando o cache de kernels já está preenchido.
Referências
More news
NVIDIA HGX B200 reduz a Intensidade de Emissões de Carbono Incorporado
O HGX B200 da NVIDIA reduz 24% da intensidade de carbono incorporado em relação ao HGX H100, ao mesmo tempo em que aumenta o desempenho de IA e a eficiência energética. Esta análise resume os dados de PCF e as novidades de hardware.
Prever Eventos Climáticos Extremos em Minutos sem Supercomputador com Huge Ensembles (HENS)
NVIDIA e o Lawrence Berkeley National Laboratory apresentam Huge Ensembles (HENS), uma ferramenta de IA de código aberto que prevê eventos climáticos raros e de alto impacto usando 27.000 anos de dados, com opções de código aberto ou prontos para uso.
Playbook dos Grandmasters do Kaggle: 7 Técnicas de Modelagem Testadas para Dados Tabulares
Análise detalhada de sete técnicas testadas por Grandmasters do Kaggle para resolver grandes conjuntos de dados tabulares com aceleração por GPU, desde baselines diversificados até ensemble avançado e pseudo-rotulagem.
Como reduzir gargalos do KV Cache com NVIDIA Dynamo
O Dynamo da NVIDIA transfere o KV Cache da memória da GPU para armazenamento de custo mais baixo, permitindo janelas de contexto maiores, maior concorrência e menor custo de inferência em grandes modelos.
Microsoft transforma site da Foxconn no data center Fairwater AI, considerado o mais poderoso do mundo
A Microsoft divulga planos para um data center Fairwater AI de 1,2 milhão de pés quadrados no Wisconsin, com centenas de milhares de GPUs Nvidia GB200. projeto de US$ 3,3 bilhões promete treinamento de IA em escala sem precedentes.
NVIDIA RAPIDS 25.08 Adiciona Novo Profiler para cuML, Melhorias no Motor GPU Polars e Suporte Ampliado de Algoritmos
RAPIDS 25.08 traz profiladores function-level e line-level para cuml.accel, executor streaming padrão no motor GPU Polars, suporte ampliado de tipos e strings, novo Spectral Embedding no cuML e acelerações com zero código para mais algoritmos.