[!NOTE] Tradução automática Este artigo foi traduzido automaticamente a partir da versão original em inglês.

Comparação de Motores Modernos de Processamento de Dados: Polars, DataFusion, Daft, Ray Data, Pandas e Spark

Durante anos, o Pandas foi responsável pelo processamento de tabelas em memória, enquanto o Apache Spark cuidava do processamento distribuído. Essa divisão funcionou bem quando os dados estavam estruturados.

Os modelos modernos pipelines processam também imagens, áudio e vídeo. Nestes cenários de trabalho, a decodificação de CPU pode comprometer significativamente a velocidade de inferência de GPU, enquanto a coleta de lixo da JVM e o Global Interpreter Lock em Python limitam a taxa de transferência de dados. Os novos motores recorrem a Rust e Apache Arrow para reduzir esses custos operacionais.

Para comparar as opções, realizei testes de desempenho com o Polars, DataFusion, Daft, Ray Data e Spark em dois datasets reais. As viagens de táxi em Nova Iorque representam dados tabulares; as imagens do Food-101 correspondem a um pipeline multimodal.

O repositório engine-comparison-demo contém o código. Execute-o no seu próprio hardware, pois o desempenho do motor depende da máquina, dataset, e do volume de trabalho.

TL;DR: Comece por definir a estrutura do trabalho, e não por procurar um benchmark vencedor. O Polars é uma excelente opção padrão para criar pipelines de DataFrame locais; o DataFusion é adequado para equipas embedding que necessitam de um motor de consultas; o Ray Data e o Daft foram concebidos para cenários de CPU/GPU pipelines mistos; enquanto o Spark continua a ser uma escolha sólida para SQL distribuído e processamento ETL. As melhorias de desempenho apresentadas neste guia referem-se a casos de uso específicos, e não a promessas de desempenho universais. Execute novamente o benchmarks correspondente nos seus dados e hardware antes de tomar uma decisão.


Tipos de carga de trabalho de processamento de dados

Os motores de IA especializam‑se, pelo que a primeira questão a responder é qual é o tipo de carga de trabalho que realmente possui. A maior divisão existe entre cargas de trabalho estruturadas e multimodais.

Dois Mundos de Dados: Estruturado vs. Multimodal

Dados estruturados ou tabulares envolvem operações de filtragem, agregação e junções. Geralmente estão limitados por CPU e costumam caber na memória. De acordo com o Polars, aproximadamente 90% das consultas são processadas com menos de 1 TB., Assim, muitos deles podem ser executados numa única máquina moderna, em vez de num cluster.

Multimodal AI lida com imagens, áudio ou vídeo. A inferência pode ser executada num GPU, enquanto a CPU de decodificação, as leituras remotas ou os limites de agrupamento restringem o pipeline. A relação entre estes fatores varia consoante a instância e os operadores, pelo que é necessário medir a utilização ao longo de todo o processo, em vez de definir o tamanho do GPU de forma isolada.

Os motores mais recentes focam em diferentes áreas deste campo. A execução nativa consegue reduzir a sobrecarga associada ao Python, as interfaces compatíveis com o Arrow permitem um intercâmbio de dados mais eficiente, e a execução em fluxo permite limitar a utilização de memória em datasets maiores que a RAM. Nenhum destes benefícios é garantido automaticamente para todos os operadores ou processos de conversão.


Parte 1: processamento em único nó

Pandas privilegia um modelo de programação ágil, em memória. Muitas operações comuns geram intermediários, e a execução paralela não é coordenada por meio de um otimizador de consultas. Esse compromisso mantém o código exploratório direto, mas pode tornar-se dispendioso em varreduras analíticas de maior porte. No Polars, PDS-H benchmark Com um fator de escala de 10, o Pandas demorou cerca de 365 segundos para processar o conjunto de dados, enquanto o motor de streaming do Polars levou apenas 3,89 segundos. Esse resultado aproximado de 94x reflete as particularidades de benchmark e da configuração utilizada; não se trata de um fator de conversão geral do Pandas para o Polars.

Execução ávida vs. Execução preguiçosa

Polars para tabelas locais pipelines

Polars Trata‑se de uma opção padrão prática quando um DataFrame local pipeline ultrapassa os limites da execução simples e em único processo. O seu modo preguiçoso API cria um plano de consulta antes da execução, permitindo otimizações como o “predicate pushdown” e o “projection pruning”. O motor pode então executar os operadores em paralelo e, sempre que suportado, em lotes em fluxo contínuo.

O fosso aumenta à medida que os dados crescem. Em fator de escala 100 (~100 GB), O motor de streaming do Polars concluiu a tarefa em 23,94 segundos, contra 152,27 segundos para o seu próprio motor em memória — ou seja, cerca de 6 vezes mais rápido, mesmo com dados que excedem a capacidade da RAM.

De engine_comparison_examples.ipynb:

import polars as pl

# scan_parquet reads only the schema — no data loaded yet
q = (
    pl.scan_parquet("yellow_tripdata_2024-01.parquet")
    .filter(
        (pl.col("trip_distance") > 5.0)
        & (pl.col("total_amount") > 30.0)
    )
    .group_by("payment_type")
    .agg(
        pl.col("total_amount").mean().alias("avg_fare"),
        pl.col("trip_distance").mean().alias("avg_distance"),
        pl.len().alias("trip_count"),
    )
)

# The entire plan is optimized and executed here, in parallel
result = q.collect()

A diferença fundamental reside no modelo de execução. Na consulta “lazy” acima, o Polars consegue transferir o filtro e a seleção de colunas para a fase de leitura do formato Parquet. Isso permite ignorar grupos de linhas específicos e evitar a leitura de colunas que não são utilizadas. Um modelo “eager” típico do Pandas pipeline não dispõe de um plano de consulta completo para otimização, embora o uso criterioso de filtros Parquet, colunas selecionadas e backends alternativos possa reduzir em parte essa desvantagem.

DataFusion para motores de consulta embarcados

Onde o Polars é uma biblioteca que se utiliza diretamente, DataFusion é o motor sobre o qual se constroem outros motores. Ele fornece a potência necessária InfluxDB 3.0, GreptimeDB, e da Apple’s Acelerador Comet Spark.

Em novembro de 2024 Execução do ClickBench publicada pelo projeto DataFusion, DataFusion liderou as configurações de Parquet de único nó testadas. A equipa da Embucket publicou posteriormente um Execução da escala TPC-H com fator 1000 num único nó de grandes dimensões. Estes resultados demonstram o que é possível alcançar em termos de escalabilidade sob condições específicas; eles não comprovam que todo trabalho de carga de 1 TB deva necessariamente evitar a utilização de um cluster.

De engine_comparison_examples.ipynb:

from datafusion import SessionContext

ctx = SessionContext()
ctx.register_parquet("taxi", "yellow_tripdata_2024-01.parquet")

# SQL executed directly against Parquet — no intermediate copies
df = ctx.sql("""
    SELECT payment_type,
           COUNT(*)           AS trip_count,
           AVG(trip_distance) AS avg_distance,
           AVG(total_amount)  AS avg_fare
    FROM taxi
    WHERE trip_distance > 5.0 AND total_amount > 30.0
    GROUP BY payment_type
    ORDER BY trip_count DESC
""")
result = df.to_pandas()

força da DataFusion reside no seu modularidade. A extensão APIs abrange catálogos personalizados, fornecedores de tabelas, regras de otimizador e planos de execução; é por isso que ela é utilizada sempre que alguém está a desenvolver uma plataforma de dados personalizada ou embedding um motor de consultas para o seu próprio produto.

Inadequado para dados multimodais

Insensato Incorpora imagens, áudio, vídeo e embeddings no fluxo de trabalho do DataFrame. Disponibiliza expressões nativas para operações como a decodificação de imagens e o download de ficheiros a partir de URLs, permitindo que os processos de pré-processamento habituais não exijam a utilização de laços em Python para cada linha individualmente. Essa integração é a principal razão para a sua preferência face a motores tabulares genéricos.

De engine_comparison_examples.ipynb:

import daft

df = daft.read_parquet("yellow_tripdata_2024-01.parquet")

result = (
    df.where(
        (daft.col("trip_distance") > 5.0)
        & (daft.col("total_amount") > 30.0)
    )
    .groupby("payment_type")
    .agg(
        daft.col("total_amount").mean().alias("avg_fare"),
        daft.col("trip_distance").mean().alias("avg_distance"),
        daft.col("trip_distance").count().alias("trip_count"),
    )
    .collect()
)

O API parecerá familiar se você tiver experiência com o Pandas, mas o motor por trás é a mesma pilha Rust + Arrow utilizada no Polars e no DataFusion. O Daft revela todo o seu potencial quando as “linhas” do seu DataFrame são imagens, PDFs ou tensores.

Node único benchmark

Executei todos os quatro motores, bem como o Rust nativo através do Polars-rs, em aproximadamente 41 milhões. Viagens de táxi amarelo em Nova Iorque para o ano inteiro de 2024. Os resultados completos encontram-se na repositório de demonstração:

Benchmark Resultados: Desempenho em Nó Único

Com este tamanho (~660 MB em formato Parquet), todos os quatro motores mais recentes concluem a tarefa rapidamente. O valor de 94x entre Polars e Pandas apresentado acima provém do PDS-H; o meu conjunto de dados de táxis com 41 milhões de linhas gerou uma diferença menor. Polars, DataFusion, Daft e Polars-rs apresentaram resultados dentro da mesma faixa ampla, sendo que DataFusion registou o tempo total mais baixo nesta execução. Trata‑se de um resultado útil para esta consulta, mas não constitui uma classificação estável: a adequação de API e medições repetidas em dados representativos devem ser utilizadas para decidir entre motores tão próximos entre si.


Parte 2: dados multimodais

O ETL tabular costuma reduzir os dados: filtra, agrega e grava menos do que se lê. O processamento multimodal AI pipelines faz o oposto. Um único caminho de documento pode se dividir em dezenas de fragmentos de texto e vetores embedding.

Num PySpark convencional pipeline, as imagens e o áudio costumam ser introduzidos como dados binários e transferidos para bibliotecas em Python a fim de serem decodificados ou transformados. A travessia da fronteira entre JVM e Python, bem como a serialização dos resultados, podem representar uma parte significativa do tempo total de execução. O Spark permite a utilização de caminhos baseados em Arrow, UDFs vetorizados e integrações com aceleradores, mas essas opções exigem um projeto e medições explícitos.

Execução em pipeline e utilização de GPU

O Spark agenda os trabalhos em fases separadas por limites de shuffle. Uma implementação simples pode sequenciar as operações de download, decodificação e inferência de forma a deixar ociosa ou seja a capacidade de CPU ou GPU. O Spark dispõe de suporte para agendamento de recursos via GPU e de plugins, pelo que o tempo ocioso não é inevitável; o importante é que a sobreposição de tarefas e a pressão reversa não constituem propriedades automáticas de um job comum baseado em PySpark DataFrame.

Modelo de Execução em Pipeline

Execução em pipeline é a alternativa. Em vez de executar as fases uma após a outra, o motor sobreposições de operações de E/S, CPU e inferência, GPU, de modo que apenas o componente mais lento fica em espera a qualquer momento.

Processamento de imagens benchmark

Realizei um benchmark de processamento de imagens em 500 fotografias reais retiradas do Introdução à Alimentação dataset. Resultados obtidos a partir do repositório de demonstração:

Benchmark Resultados: Desempenho Multimodal

Polars e DataFusion não foram utilizados porque este teste foca em operações diretas sobre imagens, em vez de expressões tabulares. Nesta execução com 500 imagens, o caminho de arquivo utilizado pelo Daft foi 3,6 vezes mais rápido do que a solução base com Pandas + Pillow, enquanto a implementação em Rust apresentou velocidade 4,2 vezes superior. Este exemplo é útil para demonstrar os custos associados à orquestração, mas é demasiado pequeno para permitir a previsão do desempenho em produção por si só.


Parte 3: processamento distribuído

Assim que uma única máquina deixa de ser suficiente, é necessário decidir como distribuir o trabalho. É aqui que as diferenças arquitetónicas entre os motores passam realmente a ter relevância.

Apache Spark

O Spark representa uma opção consolidada para processos ETL em tabelas de grande escala, junções que exigem intensivo uso de operações de shuffle, e para organizações que já utilizam o seu ecossistema. A sua capacidade de recuperação baseada na linha de descendência e o amplo suporte a plataformas tornam-se cruciais quando a fiabilidade e a familiaridade operacional são mais importantes do que a velocidade local. A integração de CPU/GPU pipelines exige uma configuração mais cuidadosa de recursos e um pipeline mais elaborado em comparação com a abordagem baseada em SQL, área onde o Ray Data e o Daft oferecem uma abstração mais direcionada.

De distributed_spark.ipynb:

from pyspark.sql import SparkSession
from pyspark.sql import functions as F

spark = SparkSession.builder \
    .appName("TaxiETL") \
    .config("spark.sql.adaptive.enabled", "true") \
    .getOrCreate()

orders = spark.read.parquet("s3a://lake/taxi/*.parquet")
zones = spark.read.csv("s3a://lake/taxi_zones.csv", header=True)

# Spark excels at this: joining massive tables with a shuffle
result = (
    orders
    .filter(F.col("trip_distance") > 5.0)
    .join(zones, orders.PULocationID == zones.LocationID, "inner")
    .groupBy("Borough")
    .agg(
        F.sum("total_amount").alias("total_revenue"),
        F.avg("total_amount").alias("avg_fare"),
    )
    .orderBy(F.desc("total_revenue"))
)

Dados Ray para computação heterogénea

Dados Ray foi projetado com base em cargas de trabalho AI. Em vez das barreiras de fase do Spark, utiliza um modelo de streaming Isso mantém o GPUs alimentado. A funcionalidade que lhe confere existência é o agendamento misto de recursos: é possível especificar que um agente deseja “1 GPU, 4 CPUs”, enquanto outro pretende apenas CPUs, sendo que o Ray resolve o resto automaticamente.

Amazon divulgou Mais de 120 milhões de dólares em poupanças anuais Após migrar cargas de trabalho internas de processamento de dados selecionadas do Spark para o Ray, o relatório indica uma eficiência de custos 91% superior no teste de conceito e 82% superior em ambiente de produção, por cada GiB de dados de entrada no S3. A escala é significativa, mas este é um estudo de caso de migração aplicado a cargas de trabalho específicas, e não uma economia esperada na adoção típica do Ray.

De distributed_ray.ipynb:

import ray

ray.init()

ds = ray.data.read_images("s3://my-bucket/food101/")

class ImageClassifier:
    def __init__(self):
        import torch
        from torchvision.models import resnet18, ResNet18_Weights
        self.model = resnet18(weights=ResNet18_Weights.DEFAULT).cuda()
        self.model.eval()
        self.preprocess = ResNet18_Weights.DEFAULT.transforms()

    def __call__(self, batch):
        import torch
        tensors = torch.stack([
            self.preprocess(img) for img in batch["image"]
        ]).cuda()
        with torch.no_grad():
            preds = self.model(tensors)
        return {
            "prediction": preds.argmax(dim=1).cpu().numpy(),
            "confidence": preds.max(dim=1).values.cpu().numpy(),
        }

# ActorPoolStrategy creates persistent GPU workers
predictions = ds.map_batches(
    ImageClassifier,
    compute=ray.data.ActorPoolStrategy(size=4),
    num_gpus=1,
    batch_size=64,
)
predictions.write_parquet("s3://output/predictions/")

Um detalhe importante a saber: à medida que se adicionam mais CPUs por GPU, o Ray Data apresenta crescimento, enquanto outros motores atingem um platô. Em O benchmarks da Anyscale, A passagem de uma taxa de 4:1 para 32:1 CPU-para-GPU permitiu um aumento de velocidade de 3 vezes na inferência de imagens, uma vez que os alimentadores CPU finalmente conseguiram acompanhar o ritmo dos GPU.

Daft Distribuído

Daft escala horizontalmente através da sua Motor de frota: Um trabalhador Swordfish por nó, com o Flotilla a operar em camada superior para realizar o agendamento em todo o cluster. O Swordfish gere a execução local em Rust e as operações de E/S pipelines através de fluxos em pequenos lotes, garantindo que cada nó permaneça ocupado sem ter de esperar pela fase seguinte.

In benchmarks publicado pela Daft, A Flotilla executou tarefas de 2 a 18 vezes mais rapidamente do que as implementações Spark testadas em quatro cargas de trabalho multimodais. A maior diferença foi observada num caso de deteção de objetos em vídeo. Considere este resultado como prova de que o design da execução é fundamental para estes pipelines, e compare-o posteriormente com os resultados da Anyscale e com um teste local.

Anyscale, a empresa responsável pelo Ray, publicou um concorrente benchmark nele, o Ray Data reduziu ou eliminou a diferença em instâncias com alto nível de CPU após a otimização. Juntos, estes dois estudos realizados por fornecedores constituem um motivo para testar operadores representativos e as relações entre CPU e GPU, e não para se chegar a uma classificação definitiva.

De distributed_daft.ipynb:

import daft
from daft import col

# Daft's Flotilla engine distributes work across the cluster
df = daft.read_parquet("s3://data-lake/pdf_metadata/*.parquet")

# Download PDFs — Daft parallelizes downloads in Rust
df = df.with_column("pdf_bytes", col("pdf_url").url.download())

# Define a GPU UDF for embedding generation
@daft.udf(return_dtype=daft.DataType.list(daft.DataType.float32()))
class TextEmbedder:
    def __init__(self):
        from sentence_transformers import SentenceTransformer
        self.model = SentenceTransformer("all-MiniLM-L6-v2", device="cuda")

    def __call__(self, text_col):
        texts = text_col.to_pylist()
        embeddings = self.model.encode(texts, batch_size=32)
        return [emb.tolist() for emb in embeddings]

# Daft schedules CPU downloads and GPU embeddings simultaneously
df = df.with_column("embedding", TextEmbedder(col("text")))
df.write_parquet("s3://output/embeddings/")

Comparação distribuída

FuncionalidadeSparkDados Ray
Modelo de ExecuçãoTarefa por núcleo, baseada em partiçõesTarefas de streaming e atores
Pontos fortesProcessamento massivo de SQL/ETL, tolerância a falhasComputação heterogénea, saturação de GPUEncadeamento multimodal, memória limitada
GPU de controloAgendamento de recursos e plugins do ecossistemaRecursos explícitos de tarefa e atorAgendamento integrado de CPU/GPU pipeline
Ajuste típicoExecutores, memória, partições, aceleradoresLotes, atores, armazenamento de objetosLotes, recursos, concorrência de E/S
Caso de avaliação positivoTarefas de grande escala com SQL/ETL e alto volume de operações de shuffleTreino ou inferência com recursos mistosIngestão e transformação multimodal

Parte 4: o papel do Rust e do Arrow

Abaixo da competição, a convergência é a história mais interessante. O Polars, o Daft e o DataFusion utilizam amplamente o Rust, enquanto o Ray inclui componentes nativos juntamente com as suas funcionalidades em Python APIs. Todos eles conseguem trocar dados através de partes do Apache Arrow ecossistema.

Convergência entre Rust e Arrow

O Interface Arrow PyCapsule (__arrow_c_stream__ O protocolo) oferece às bibliotecas compatíveis uma forma padrão para trocar fluxos Arrow. As transferências podem evitar a serialização linha a linha e reutilizar buffers quando os esquemas e as estruturas de memória são compatíveis. A materialização, o reagrupamento, a conversão de tipos ou a transferência entre dispositivos ainda podem exigir cópia de dados; portanto, verifique a eficiência da transferência por meio de análise de desempenho, em vez de assumir que ela é gratuita.

from datafusion import SessionContext
import polars as pl

# Compute in DataFusion (Rust-native execution)
ctx = SessionContext()
ctx.register_parquet("events", "events.parquet")
df = ctx.sql("""
    SELECT user_id, COUNT(*) as event_count
    FROM events WHERE event_type = 'purchase'
    GROUP BY user_id HAVING COUNT(*) > 5
""")

# Convert through Arrow; compatible buffers may be reused
arrow_table = df.to_arrow_table()
df_polars = pl.from_arrow(arrow_table)

# Continue analysis in Polars
result = df_polars.with_columns(
    pl.col("event_count").rank().alias("rank")
).sort("rank")

Porque é que estes motores mais recentes utilizam Rust para execução nativa?

  1. Não existe colecionador de lixo de rastreio em código Rust. A propriedade confere aos operadores nativos um controlo mais direto sobre a alocação e desalocação de memória. Isso pode reduzir a latência associada ao GC, mas não impede a pressão na memória ou falhas devido à falta de memória disponível.

  2. Representações nativas compactas. As estruturas em Rust não possuem cabeçalhos de objeto em Java. O benefício prático depende do layout do motor: os sistemas JVM colunares também evitam representar cada valor como um objeto individual.

  3. Verificações de concorrência mais rigorosas. O Rust seguro elimina muitas corridas de dados em tempo de compilação. O código do motor ainda pode conter blocos inseguros e erros de concorrência lógica, mas a linguagem reduz a superfície de possíveis falhas.

Juntamente com o layout de memória em colunas da Arrow, estas opções permitem reduzir a sobrecarga associada à alocação e à serialização de dados. No entanto, elas não eliminam completamente esse impacto em todos os limites entre estruturas; objetos em Python, transporte de rede, esquemas incompatíveis e movimentação de dispositivos continuam a ter influência significativa.


Parte 5: adoção de múltiplos motores

Não é necessário forçar toda a plataforma a ser processada por um único motor. A composição revela-se útil quando os custos associados à sua gestão são inferiores aos de criar um sistema capaz de lidar com todas as cargas de trabalho.

O Modelo de Coexistência: Multi-Motor Pipeline

Uma das possíveis soluções de retransmissão utiliza o Spark para operações de junção em lakehouses consolidadas, grava os resultados num formato de tabela ou ficheiro aberto e transfere-os para o Ray Data ou o Daft a fim de realizar inferências GPU ou transformações multimodais. O Polars ou o DuckDB podem ser usados para análises locais nos mesmos ficheiros. Uma equipa mais pequena pode precisar apenas de um destes motores; adicione outro apenas quando um gargalo identificado justifique essa expansão operacional.

O que torna possível esta composição são os formatos abertos: Parquet, Delta, Iceberg, Arrow. Cada motor presente na pilha lê e escreve estes formatos de forma nativa, pelo que a transferência entre as diferentes fases é feita apenas através de um caminho de ficheiro.

Seleção do motor por caso de avaliação

Caso de avaliaçãoLista de pré-seleçãoValidar
DataFrames analíticos locaisPolars / DuckDBAPI combinação de ajuste, memória e consulta
SQL/ETL distribuído existenteApache SparkComportamento de embaralhamento, operações e custo
Trabalho paralelo nativo em PythonCusto de overhead do agendador, modelo de falha
Treino ou inferência híbrido CPU/GPURay Data / DaftUtilização do acelerador, contrapressão
Ingestão multimodalDaft / Ray DataOperadores nativos, comportamento de repetição
Mecanismo de consulta embutidoDataFusionExtensão APIs, cobertura SQL

Escala diagonal

Durante muito tempo, a opção disponível era escalar verticalmente (usar uma máquina maior) ou escalar horizontalmente (adicionar mais máquinas). Polars Cloud está a realizar uma operação intermédia, que é designada por diagonal scaling: escala horizontalmente enquanto lê dos armazéns em nuvem para maximizar o I/O, e depois converge para um único nó grande assim que os filtros e as agregações reduzem os dados, ignorando completamente o processo de distribuição e embaralhamento.

A lição mais importante é comparar o custo por tarefa concluída com sucesso, e não o preço horário da instância. Um nó de maior dimensão pode ser mais económico quando a redução proporcionada pelo runtime supera o seu custo unitário mais elevado, mas o resultado depende do tráfego de E/S, da pressão na memória e da quantidade de trabalho que é eliminada antes de uma reorganização.


Reproduzir a comparação

O repositório de demonstração complementar Contém os scripts, notebooks e o ambiente utilizados para as comparações. O comando rápido abaixo utiliza dados de táxis de um mês; siga as instruções do repositório dataset para recriar a execução completa ao longo de todo o ano, como mostrado anteriormente.

# Install dependencies
uv sync

# Run the tabular benchmark (~2.9M NYC taxi trips)
uv run python -m engine_comparison.benchmarks.tabular

# Run the multimodal benchmark (500 real food photos)
uv run python -m engine_comparison.benchmarks.multimodal

# Run native Rust benchmarks (Polars-rs + image crate)
cd rust_benchmark && cargo run --release && cd ..

O repositório dispõe também de notebooks para comparações lado a lado API, bem como de configurações Docker Compose para execuções distribuídas locais de Spark, Ray e Daft.


Principais conclusões

  1. Demonstre que uma única máquina é insuficiente. Os resultados obtidos através de escalonamento indicam que algumas análises volumosas conseguem ser processadas num único nó. Antes de aceitar os custos adicionais associados ao uso de um cluster, é necessário avaliar as necessidades de memória, E/S, volume de dados descartados e mecanismos de recuperação.

  2. Os modelos de execução são mais importantes do que a sintaxe convencional. O planeamento preguiçoso, o processamento por empilhamento, o fluxo em tempo real e os operadores paralelos explicam grande parte da diferença entre um script DataFrame de tipo “eager” e um motor analítico.

  3. Meça o acelerador como um todo pipeline. A decodificação, as operações de E/S de rede, o agrupamento de operações, a serialização e a contrapressão determinam se um GPU permanece ocupado. O Ray Data e o Daft tornam esta interação uma abstração central; o Spark consegue suportá-la através de um design e de ferramentas adicionais.

  4. Faça uma lista preliminar com base na carga de trabalho e, em seguida, em benchmark. Utilize a compatibilidade com o ecossistema para restringir as opções e um caso de uso representativo do tipo end-to-end para tomar a decisão final. Os resultados obtidos a partir de fornecedores benchmark são meras hipóteses, e não garantias.

  5. Os formatos abertos permitem que a escolha seja revertida. Interfaces compatíveis com setas, Parquet e formatos de tabela abertos podem reduzir os custos de transferência de dados. Verifique se uma determinada conversão reutiliza buffers ou os copia.

Se precisar de um ponto de partida, experimente o Polars para trabalhar com tabelas locais pipeline, e o Daft ou o Ray Data para estruturas CPU/GPU pipeline mistas. Escolha o Spark sempre que a sua execução distribuída, o ecossistema associado ou a infraestrutura operacional existente resolverem um determinado requisito — e não apenas quando o dataset atingir um tamanho específico.


Referências

Benchmarks e dados de desempenho

Motores e frameworks

Arquitetura e ecossistema

Repositório de demonstração

Datasets