Traduction automatique
Cet article a été traduit automatiquement depuis la version originale en anglais.
Comparatif des moteurs modernes de traitement de données : Polars, DataFusion, Daft, Ray Data, Pandas et Spark
Pendant des années, Pandas a géré le travail tabulaire en mémoire et Apache Spark le travail distribué. Cette séparation fonctionnait bien tant que les données étaient structurées.
Les charges de travail modernes ne sont plus toujours structurées. Les pipelines d’images, d’audio et de vidéo coexistent désormais avec les pipelines tabulaires, et dès que l’inférence GPU entre en jeu, le garbage collection de la JVM et le GIL de Python cessent d’être de simples irritants pour devenir le goulet d’étranglement. C’est en grande partie pour cela qu’une nouvelle génération de moteurs construits sur Rust et Apache Arrow s’est imposée.
J’ai migré la plupart de mes pipelines mono-nœud vers Polars au cours de l’année passée, et je voulais voir comment il se compare réellement au reste de cette nouvelle pile. J’ai donc benchmarké Polars, DataFusion, Daft, Ray Data et Spark sur des jeux de données réels — les trajets de taxi de NYC pour la partie tabulaire, et les images Food-101 pour la partie multimodale.
Tout le code se trouve dans le dépôt engine-comparison-demo. Clonez-le et exécutez les benchmarks sur votre propre matériel — vos chiffres seront différents des miens, et c’est précisément l’intérêt.
TL;DR : Sur les benchmarks favorables au marketing, les moteurs natifs Rust atteignent des accélérations de 94x par rapport à Pandas sur un seul nœud. Sur des charges réelles, vous verrez quelque chose de plus modeste mais constant — Polars et DataFusion sont tous deux d’excellents choix par défaut. Pour les pipelines multimodaux, les moteurs pipelinés comme Ray Data et Daft tournent jusqu’à 18x plus vite que Spark parce qu’ils maintiennent réellement les GPU occupés. Il n’y a pas de gagnant absolu. Le bon choix dépend de vos données, de votre échelle, et de la présence ou non de GPU dans la boucle.
Types de charges de travail en traitement de données
Les moteurs se spécialisent, donc la première question à trancher est le type de charge de travail que vous avez réellement. La plus grande séparation est entre structuré et multimodal.
Données structurées / tabulaires. Filtrage, agrégation, jointures. Charge CPU-bound, généralement en mémoire. Une grande partie de ce travail s’exécute sur des clusters distribués qui n’avaient pas besoin d’être distribués au départ — environ 90 % des requêtes traitent moins de 1 To de données, ce qui tient aujourd’hui facilement sur une seule machine.
IA multimodale. Images, audio, vidéo. GPU-bound côté inférence, mais c’est le prétraitement CPU qui vous pénalise. Les modèles de deep learning consomment les données plus vite que les CPU ne peuvent les décoder, donc sur une g6.xlarge standard (4 CPU par GPU), l’utilisation GPU tombe sous les 20 %. Tout le pipeline finit par être limité par la vitesse à laquelle vous pouvez alimenter le GPU.
Les nouveaux moteurs ciblent les deux. Rust leur apporte la vitesse, Arrow leur apporte le partage de données zero-copy entre processus, et l’exécution en streaming leur permet de gérer des jeux de données plus grands que la RAM.
Partie 1 : Traitement sur un seul nœud
Le goulet d’étranglement de Pandas n’est pas un bug. C’est sa conception. Pandas charge les fichiers en RAM de manière eager, copie les intermédiaires à presque chaque opération, et reste mono-thread à cause du GIL. Dans les benchmarks PDS-H au scale factor 10, Pandas met environ 365 secondes pour terminer une suite de requêtes standard. Le moteur de streaming de Polars exécute le même travail en 3,89 secondes — soit environ 94x plus rapide.
Polars : traitement de données tabulaires
Polars a remplacé Pandas comme choix par défaut pour l’ETL local sérieux dans beaucoup d’équipes avec lesquelles j’ai échangé. Il est écrit en Rust et utilise une exécution lazy : au lieu d’exécuter chaque ligne au fur et à mesure, il construit un plan de requête, l’optimise (predicate pushdown, projection pruning, column pruning), puis l’exécute sur tous les cœurs CPU en batches de streaming.
L’écart se creuse à mesure que les données grossissent. Au scale factor 100 (~100 GB), le moteur de streaming de Polars a terminé en 23,94 secondes contre 152,27 secondes pour son propre moteur en mémoire — soit environ 6x plus rapide, sur des données plus grandes que la RAM.
Extrait 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()
La différence avec Pandas n’est pas l’API. C’est l’architecture. Polars construit d’abord le plan et optimise l’ensemble du pipeline avant de toucher aux données. Le filter est poussé jusqu’au lecteur Parquet, donc des groupes de lignes entiers sont ignorés. Seules les colonnes référencées dans la requête sont lues depuis le disque. Pandas ne peut rien faire de tout cela — chaque ligne s’exécute dès qu’elle est parsée, et les copies intermédiaires apparaissent partout.
Apache DataFusion : moteur de requête extensible
Là où Polars est une bibliothèque qu’on utilise directement, DataFusion est le moteur sur lequel on construit d’autres moteurs. Il propulse InfluxDB 3.0, GreptimeDB et Comet, l’accélérateur Spark d’Apple.
En novembre 2024, DataFusion est devenu le moteur mono-nœud le plus rapide pour interroger des fichiers Parquet dans ClickBench, devant DuckDB, chDB et ClickHouse sur le même matériel. L’équipe Embucket a ensuite exécuté TPC-H au scale factor 1000 (~1 To) sur un seul nœud par-dessus DataFusion. C’est un chiffre utile à garder en tête chaque fois que quelqu’un dit qu’il lui faut un cluster.
Extrait 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()
La force de DataFusion est sa modularité. Les API d’extension couvrent les catalogs personnalisés, les table providers, les règles d’optimisation et les plans d’exécution, ce qui explique pourquoi il apparaît dès qu’on construit une plateforme de données sur mesure ou qu’on embarque un moteur de requête dans son propre produit.
Daft : traitement de données multimodales
Daft est celui qui prend vraiment les données multimodales au sérieux. Images, audio, vidéo, embeddings — ce sont de vrais types dans le DataFrame, pas des octets opaques. Pandas traite une image comme un tableau d’octets, Spark la sérialise via la JVM, mais Daft dispose d’expressions Rust natives pour décoder les images, récupérer des URLs et produire des tenseurs sans boucle Python au milieu.
Extrait 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()
)
L’API semblera familière si vous connaissez Pandas, mais le moteur repose sur la même pile Rust + Arrow que Polars et DataFusion. Là où Daft devient intéressant, c’est quand les « lignes » de votre DataFrame sont des images, des PDF ou des tenseurs.
Benchmark : performances sur un seul nœud
J’ai exécuté les quatre moteurs, plus du Rust natif via Polars-rs, sur environ 41 M de trajets de taxis jaunes de NYC pour toute l’année 2024. Résultats complets dans le dépôt de démo :
À cette taille (~660 MB Parquet), tous les moteurs modernes sont rapides. Le chiffre phare de 94x entre Polars et Pandas vient de PDS-H, un benchmark analytique synthétique — sur ma charge à 41 M de lignes, j’ai obtenu une amélioration réelle et constante par rapport à Pandas, mais nulle part près de 94x. Le résultat le plus intéressant est que Polars, DataFusion, Daft et le Rust pur via Polars-rs se situent tous dans le même ordre de grandeur sur cette charge. DataFusion devance légèrement les autres en temps total. Polars est le plus agréable à écrire et reste un excellent outil polyvalent.
Partie 2 : Gestion des données multimodales
L’ETL tabulaire réduit généralement les données : on filtre, on agrège, on écrit moins que ce qu’on lit. Les pipelines IA multimodaux font l’inverse. Un seul chemin de document peut se déployer en dizaines de chunks de texte et de vecteurs d’embedding.
Spark traite les images et l’audio comme des blobs binaires. Les manipuler en Python implique un aller-retour JVM-vers-Python via Py4J, un traitement avec Pillow ou OpenCV, puis une sérialisation de retour. Cet aller-retour domine le temps total d’exécution sur beaucoup de charges réelles.
Exécution pipelinée et utilisation GPU
Spark parallélise les partitions sur les cœurs, mais son exécution par stages (bulk synchronous) est le piège. À l’intérieur d’une tâche unique, chaque étape (téléchargement, décodage, classification) s’exécute séquentiellement sur un seul cœur, sans recouvrement asynchrone. Et toutes les tâches d’un stage doivent se terminer avant que le stage suivant ne commence. Les GPU restent inactifs pendant que les CPU prétraitent ; les CPU restent inactifs pendant que les GPU exécutent l’inférence. Chaque stage matérialise sa sortie avant que le suivant ne démarre, ce qui ajoute une pression mémoire au reste.
L’exécution pipelinée est l’alternative. Au lieu d’enchaîner les stages les uns après les autres, le moteur fait se chevaucher l’I/O, le travail CPU et l’inférence GPU, de sorte qu’à un instant donné seul le composant le plus lent attend.
Benchmark : traitement d’images
J’ai benchmarké le traitement d’images sur 500 photographies réelles du jeu de données Food-101. Résultats issus du dépôt de démo :
Polars et DataFusion n’apparaissent pas ici parce qu’ils n’ont pas d’opérations d’image natives — le travail sur image retomberait sur du Python séquentiel et la comparaison ne serait pas équitable. Les chiffres importants : les opérations d’image natives Rust de Daft sont 3,6x plus rapides que Pandas + Pillow, et le Rust pur est 4,2x plus rapide que Pandas sur l’ensemble du pipeline.
Partie 3 : Traitement distribué
Une fois qu’une seule machine ne suffit plus, il faut choisir comment répartir le travail. C’est là que les différences d’architecture entre moteurs commencent réellement à compter.
Apache Spark
Spark reste ce que je choisirais pour de l’ETL tabulaire à l’échelle du pétaoctet avec des shuffles complexes. La tolérance aux pannes via la lineage RDD fonctionne, l’écosystème est énorme (Databricks, EMR), et les jointures multi-To sont un terrain bien balisé. C’est sur les charges IA qu’il s’effondre. Les tâches sont planifiées sur des exécuteurs JVM qui ne connaissent pas les GPU, donc les GPU restent inactifs en attendant le prétraitement CPU.
Extrait 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"))
)
Ray Data : utilisation GPU
Ray Data a été conçu autour des charges IA. Au lieu des barrières de stage de Spark, il utilise un modèle de streaming qui maintient les GPU alimentés. La fonctionnalité qui justifie son existence est la planification de ressources mixtes : vous pouvez déclarer qu’un actor veut « 1 GPU, 4 CPUs » et qu’un autre ne veut que des CPU, et Ray se charge du reste.
Amazon a indiqué économiser plus de 120 millions de dollars par an en déplaçant certaines charges de Spark vers Ray. Les chiffres de leur PoC faisaient état de 91 % de meilleure efficacité coût et de 13x plus de données par heure ; en production, le chiffre s’est stabilisé à 82 % de meilleure efficacité coût par GiB d’entrée S3.
Extrait 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/")
Un détail utile à connaître : à mesure que vous ajoutez plus de CPU par GPU, Ray Data monte en charge tandis que les autres moteurs plafonnent. Dans les benchmarks d’Anyscale, passer d’un ratio CPU/GPU de 4:1 à 32:1 a donné une accélération de 3x sur l’inférence d’images, parce que les feeders CPU ont enfin suivi le rythme du GPU.
Daft (distribué)
Daft passe à l’échelle via son moteur Flotilla : un worker Swordfish par nœud, avec Flotilla au-dessus pour l’ordonnancement à l’échelle du cluster. Swordfish gère l’exécution Rust locale et pipeline l’I/O avec le calcul via du streaming en petits batches, de sorte que chaque nœud reste occupé sans attendre le stage suivant.
Sur quatre charges multimodales, Daft avec Flotilla a tourné 2 à 18x plus vite que Spark. Sur la plus lourde (détection d’objets vidéo avec YOLO11n), Spark a mis plus de 3,5 heures et Daft a terminé en moins de 12 minutes — 18x, principalement parce que Spark passe ce temps en sérialisation et en barrières de stage.
Un point à noter : Anyscale, la société derrière Ray, a publié ses propres benchmarks montrant que Ray Data réduit l’écart voire inverse le résultat sur des instances à fort CPU avec tuning manuel. En ce moment, les deux se dépassent d’un trimestre à l’autre.
Extrait 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/")
Comparatif direct en distribué
| Fonctionnalité | Spark | Ray Data | Daft (Flotilla) |
|---|---|---|---|
| Modèle d’exécution | Une tâche par cœur, basé sur les partitions | Tâches et actors en streaming | Un Swordfish par nœud, batches en streaming |
| Points forts | SQL/ETL massif, tolérance aux pannes | Calcul hétérogène, saturation GPU | Pipeline multimodal, mémoire bornée |
| Utilisation GPU | Mauvaise (basé sur des stages, pas de recouvrement CPU/GPU) | Excellente (assignation explicite des ressources) | Excellente (pipeline async I/O-calcul) |
| Coût de tuning | Élevé (mémoire des exécuteurs, cœurs, partitions) | Moyen (tailles de batch, object store) | Faible (le moteur gère les ressources) |
| Idéal pour | ETL tabulaire à l’échelle du pétaoctet | Entraînement/inférence avec CPU+GPU mixtes | Chargement de données multimodales pour PyTorch |
Partie 4 : Le rôle de Rust et Arrow
Sous la compétition, la convergence est l’histoire la plus intéressante. Polars, Daft, DataFusion et le cœur de Ray partagent tous la même base : Apache Arrow pour le format en mémoire et Rust pour la concurrence.
Ce n’est pas seulement de l’élégance architecturale. L’interface Arrow PyCapsule (protocole __arrow_c_stream__) permet de transmettre des données entre moteurs sans sérialisation : calculer dans DataFusion, transmettre le résultat à Polars ou PyArrow à coût nul ; faire le prétraitement multimodal dans Daft et injecter directement les batches Arrow dans un PyTorch DataLoader.
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
""")
# Zero-copy conversion to Arrow, then to Polars
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")
Alors pourquoi Rust plutôt que la JVM ?
-
Pas de pauses dues au garbage collection. Ownership et borrowing remplacent le GC, ce qui signifie pas de pauses stop-the-world ni de pics de latence imprévisibles. Les erreurs OOM qui apparaissent à grande échelle sur les systèmes JVM disparaissent en grande partie.
-
Pas de taxe d’en-tête d’objet. La JVM ajoute 12 à 16 octets d’en-tête à chaque objet. Sur des milliards de petits objets, cela constitue à lui seul un budget mémoire. Rust ne paie pas ce coût.
-
Sécurité des threads à la compilation. Le système de types de Rust rejette les data races avant même la génération du binaire, donc l’exécution multithread ne s’accompagne pas de la classe habituelle de bugs de concurrence subtils.
Combiné à la disposition mémoire en colonnes d’Arrow, le goulet d’étranglement de sérialisation qui consomme jusqu’à 30 % des cycles CPU dans les anciennes piles JVM disparaît.
Partie 5 : Adopter plusieurs moteurs
La réponse honnête, c’est qu’on ne choisit pas un seul moteur. On les compose.
En production, cela ressemble généralement à un relais. Les données brutes dans un data lake sont nettoyées et jointes par Spark (ou Dask si votre équipe est 100 % Python) vers des tables Parquet ou Delta. Ces tables alimentent ensuite Ray Data ou Daft pour le dernier kilomètre — inférence GPU, génération d’embeddings, transformations multimodales — pour lequel Spark n’a jamais été conçu. Pour l’analyse ad hoc et le développement local, Polars et DuckDB donnent un retour immédiat sans démarrer de cluster.
Ce qui rend cette composition possible, ce sont les formats ouverts : Parquet, Delta, Iceberg, Arrow. Tous les moteurs de la pile les lisent et les écrivent nativement, donc le passage d’une étape à l’autre n’est qu’un chemin de fichier.
Résumé des cas d’usage par moteur
| Tâche | Outil recommandé | Compromis |
|---|---|---|
| Analyse ad hoc sur un laptop | Polars / DuckDB | Vitesse > Échelle |
| ETL SQL massif (pétaoctet) | Apache Spark | Fiabilité > Vitesse |
| Passage à l’échelle natif Python | Dask | Simplicité > Optimisation |
| Entraînement de LLM / vision par ordinateur | Ray Data | Utilisation GPU > Fonctionnalités ETL |
| Chargement de données multimodales | Daft | Expérience développeur > Écosystème |
| Construction de moteurs de requête personnalisés | DataFusion | Extensibilité > Fonctionnalités prêtes à l’emploi |
Mise à l’échelle diagonale
Pendant longtemps, le choix se limitait à scale up (une machine plus grosse) ou scale out (plus de machines). Polars Cloud fait quelque chose entre les deux, qu’ils appellent la mise à l’échelle diagonale : monter horizontalement tout en lisant depuis le stockage cloud pour maximiser l’I/O, puis se replier sur un seul gros nœud une fois que filtres et agrégations ont réduit les données, en évitant complètement le shuffle distribué.
La partie contre-intuitive : une instance plus grosse et plus chère peut se révéler moins chère par job parce qu’elle pousse l’utilisation GPU suffisamment haut pour que le temps total d’exécution baisse plus vite que le tarif horaire n’augmente.
Essayez par vous-même
Le dépôt de démo compagnon contient tout le nécessaire pour reproduire ces benchmarks :
# 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 ..
Le dépôt contient aussi des notebooks pour des comparaisons d’API côte à côte, ainsi que des configurations Docker Compose pour des exécutions distribuées locales de Spark, Ray et Daft.
Points clés à retenir
-
Ne distribuez pas si vous n’y êtes pas obligé. Polars et DataFusion gèrent jusqu’à ~1 To sur une seule machine, avec des accélérations constantes par rapport à Pandas (et 94x sur des benchmarks analytiques lourds comme PDS-H). Ne prenez un cluster que lorsque vous avez réellement atteint les limites d’un seul nœud, pas avant.
-
Les limites de Pandas sont architecturales. Exécution eager, mono-threading, copies intermédiaires — ce sont des choix de conception délibérés qui deviennent des goulets d’étranglement lorsque les données grossissent. Ce qui change la donne, c’est l’exécution lazy avec predicate pushdown et column pruning.
-
L’exécution pipelinée bat les barrières de stage pour les charges GPU. Les stages de Spark laissent les GPU inactifs pendant que les CPU prétraitent. Ray Data et Daft superposent I/O, CPU et GPU de sorte qu’aucune ressource ne reste à attendre le stage suivant.
-
Utilisez chaque moteur pour ce qu’il fait bien. Spark pour l’ETL au pétaoctet, Polars et DuckDB pour l’analyse ad hoc, Ray Data pour les pipelines d’entraînement GPU, Daft pour le chargement de données multimodales, DataFusion pour les moteurs de requête personnalisés.
-
Rust + Arrow est la fondation commune. Ce n’est pas un hasard si Polars, DataFusion, Daft et Ray convergent ici — cela élimine les pauses GC, le surcoût de sérialisation inter-processus et toute une classe de bugs de concurrence.
Si vous ne savez pas par où commencer : Polars sur une seule machine, puis Daft ou Ray Data quand les GPU entrent dans l’équation. Spark seulement quand vous avez réellement un pétaoctet.
Références
Benchmarks et données de performance
- Benchmarks PDS-H de Polars - Polars streaming vs Pandas à SF-10 (94x) et SF-100 (6,4x plus rapide que l’exécution en mémoire)
- Résultats ClickBench de DataFusion (nov. 2024) - Moteur mono-nœud le plus rapide pour les requêtes Parquet
- Embucket : TPC-H SF-1000 sur DataFusion - TPC-H complet à ~1 To sur un seul nœud
- Migration Amazon de Spark vers Ray - 120 M$/an d’économies, 82 % d’efficacité coût en production
- Benchmarks Flotilla de Daft - 2 à 18x plus rapide que Spark sur des charges multimodales
- Benchmarks Ray vs Daft d’Anyscale - Ray Data ~30 % plus rapide en moyenne sur le multimodal
Moteurs et frameworks
- Polars - Bibliothèque DataFrame Rust avec exécution lazy
- Apache DataFusion - Moteur de requête Rust embarquable (GitHub)
- Daft - DataFrame distribué natif multimodal
- Ray - Framework de calcul distribué pour l’IA (Data Internals)
- Apache Spark - Moteur SQL et ETL distribué
- DuckDB - Moteur SQL analytique in-process
- Dask - Calcul parallèle natif Python
Architecture et écosystème
- Polars Cloud : mise à l’échelle diagonale - Mise à l’échelle verticale/horizontale dynamique
- Architecture Flotilla de Daft - Moteur distribué Swordfish + Flotilla
- Apache DataFusion Comet - Accélérateur Spark développé à l’origine chez Apple
- Apache Arrow - Format mémoire en colonnes (Flight RPC, Interface PyCapsule)
- InfluxDB 3.0 + DataFusion - Base de données time-series construite sur DataFusion
- GreptimeDB - Base de données d’observabilité utilisant DataFusion
Dépôt de démo
- engine-comparison-demo - Code compagnon de cet article : benchmarks, notebooks et Docker Compose pour Spark/Ray/Daft
Jeux de données
- NYC Taxi Trip Records - Données NYC TLC yellow taxi (Parquet, ~2,9 M de lignes/mois)
- Jeu de données Food-101 - 101 K images de nourriture d’ETH Zurich (Bossard et al., ECCV 2014)