Benchmark : Snowpark vs Spark Databricks


En janvier 2022, Snowflake a annoncé la sortie publique de Snowpark, leur nouvelle API permettant de manipuler les données en Java, Scala et Python avec la même syntaxe que Spark. Cette fonctionnalité majeure vient s’inscrire dans la guerre incessante que mènent depuis des années Snowflake et Databricks.

Dans cet article je vais vous présenter les raisons pour lesquelles Snowflake a sorti cette API et les raisons pour lesquelles vous pourriez avoir envie de vous en servir.

Nous allons comparer, chiffres à l'appui, si oui ou non Snowpark peut détrôner Spark sur Databricks.

Snowflake et Databricks

Pour rappel, Snowflake est une plateforme de données en SaaS (Software as a Service) développée en 2012 fournissant une seule plateforme pour les besoins de data warehousing et data lakes. Dans une optique serverless, elle sépare le stockage et le calcul, permettant de ne payer que pour ce dont on a besoin.

Le gros point vendeur de Snowflake est qu’il s’agit d’une solution clés en main. Elle est très performante sans configuration de notre part, permettant donc par exemple à des analystes de se concentrer sur leurs besoins métier.

Cependant, le parti pris initial de Snowflake était qu’elle n’était que orientée SQL. Ce positionnement a changé au fur et à mesure de la sortie des nouvelles fonctionnalités ouvrant Snowflake au-delà du SQL. En commençant par les UDF en Java, Scala, Python et JS, et avec maintenant la sortie de Snowpark.

Databricks quant à lui est une plateforme de données (également en SaaS) créée par les créateurs de Spark en 2013. Basé sur Spark et Delta Lake, Databricks est une plateforme centralisée répondant aux besoins des Data Engineers, Analysts et Scientists. Il intègre nativement de nombreux outils Machine Learning tels que MLflow et offre des notebooks permettant de manipuler les données en Scala, Python, R et SQL.

Databricks nous propose de manager les clusters Spark pour nous mais contrairement à Snowflake il reste de notre responsabilité de gérer nos fichiers et d'optimiser le stockage des données.

Une guerre commerciale

Snowflake et Databricks se font la guerre depuis des années, intégrant chacun progressivement des fonctionnalités présentes chez la concurrence.

Notre histoire du jour commence en novembre 2021, lorsque Databricks a décidé de rétracter la “clause de DeWitt" de ses conditions générales d'utilisation. Cette clause, présente chez la majorité des vendeurs, interdit la publication de benchmarks nominatifs. Elle est la raison pour laquelle on ne voit jamais de benchmarks comparant les solutions de bases de données entre elles.

Le 2 novembre 2021, Databricks annonce avoir atteint un nouveau record mondial sur le benchmark TPC-DS 100To. Nous aurons l’occasion de voir de quoi il s’agit en détail par la suite. Dans leur article, ils se vantent d’être 2,7 fois plus rapides que Snowflake et 12 fois moins chers ! Ils affirment que ces résultats ont étés reproduits par un centre indépendant et que cela montre que les solutions de Data Warehouse telles que Snowflake deviennent hors de prix lors d’une montée en échelle.

Snowflake réplique le 12 novembre en accusant Databricks d’avoir purement et simplement triché. Leurs mots ne sont pas aussi clairs mais presque. Le titre de leur article est même “Industry Benchmarks and Competing with Integrity”. Ils affirment que les résultats de Databricks sont erronés et qu’après avoir reproduit le benchmark sur Snowflake, il était 2,2 fois plus rapide que ce que Databricks avait rapporté. Ils proposent même aux lecteurs de créer un compte d'essai Snowflake et lancer le benchmark eux même, puisque les requêtes et un jeu de données TPC-DS de SF10To et SF100To sont disponibles à n'importe qui ayant un compte.

Databricks contre-attaque le 15 novembre en accusant, à son tour, Snowflake de triche avec un article contenant en titre “Snowflake [...] Not So Fast!”.  Databricks a affirmé que même si, effectivement, le benchmark utilisant les requêtes et le jeu de données de Snowflake a un temps d'exécution proche de celui reporté par Snowflake … l'histoire est différente si on upload un autre jeu de données.

D'après Databricks, le benchmark utilisant un jeu de données officiel TPC-DS mettrait 1,9 fois plus longtemps à tourner que le temps reporté par Snowflake et se rapproche donc plus de celui que Databricks avait rapporté. De plus, le jeu de données proposé par Snowflake a été republié 2 jours après la publication du nouveau record de Databricks le 2 novembre.

L'accusation de Databricks porte sur une optimisation du jeu de données spécifiquement sur le type d'opérations des requêtes effectuées par TPC-DS et ont challengé Snowflake à soumettre un benchmark TPC-DS officiel qui doit utiliser un jeu de données officiel et fournir toutes les informations concernant son temps de chargement et les optimisations qui sont faites lors de ce chargement.

On notera que Databricks s'est quand même fait passer pour une victime qui n'a volontairement pas sur-optimisé son jeu de données "puisqu'ils ont pensé aux clients" disent-ils, qui n'ont pas toujours une connaissance approfondie du jeu de données et/ou des requêtes qui y seront appliquées … alors qu'il s'agit d'une contrainte d'un benchmark officiel et qu'ils étaient donc forcés d'appliquer pour que leur résultat soit officiel.

Spark sur Snowflake

Dans l’esprit de cette guerre que se mènent les 2 géants, Databricks empiétait sur les plates-bandes de Snowflake avec Delta Lake et Spark SQL. Eh bien avec la sortie de Snowpark, c’est au tour de Snowflake d’essayer de prendre des parts de marché à Spark et plus précisément à Databricks.

Snowflake possédait déjà un connecteur pour que Spark puisse utiliser Snowflake comme source de données.

alt_text
Interaction entre Spark et Snowflake avec le connecteur

Le connecteur inclut les query pushdown qui permettent d’accélérer les requêtes en rapprochant les opérations au plus près possible de la source de données. Cette optimisation n’est cependant pas disponible pour toutes les opérations, par exemple les UDF Spark ne sont pas supportées.

Un autre inconvénient est que cette architecture nécessite le transfert de gros volumes de données dans les 2 sens. Avec le connecteur Spark. Le plan logique est créé par Spark puis Catalyst, le moteur d'optimisation de Spark, détermine s'il effectue du query pushdown vers Snowflake ou pas lors de sa phase de comparaison des possibles plans physiques. La requête est ensuite exécutée et le résultat stocké dans une zone de staging avant d’être renvoyé à Spark.

Arrive Snowpark

Après une preview remontant à juin 2021, Snowflake a annoncé en janvier 2022 la sortie publique de Snowpark : leur API au nom très subtil qui nous offre la possibilité de manipuler les données en dataframe à la Spark.

La syntaxe est strictement la même et ressemble donc beaucoup à ce qu’on pouvait déjà faire avec le connecteur Spark. Il y a cependant une différence majeure : Snowpark utilise directement le moteur de Snowflake sur des Virtual Warehouses Snowflake. Plus besoin de fournir un cluster Spark externe et transférer les données d’un système à l’autre. On réduit ainsi les coûts et risques de sécurité occasionnés par ces transferts. Tout comme avec Spark, les dataframes utilisent la lazy evaluation pour encore réduire les transferts de données.

Avec l’arrivée de Snowpark, Snowflake étend l’analyse de données sur sa plateforme au-delà du simple SQL vers Java, Scala et depuis juin en Python !

Quel intérêt ?

Prenons un exemple :

empDF.join(deptDF).where(empDF("dept_id") === deptDF("dept_id"))
    .select("emp_id","name","salary").show(10)

Le code ci-dessus indique à Spark d’effectuer une jointure entre la table des employés et celle des services d’une société, puis d’afficher les 10 premiers noms des employés et leurs salaires… Eh bien avec Snowpark la syntaxe est exactement la même !

L'avantage majeur par rapport au connecteur Spark est l'utilisation des ressources de Snowflake. Plus besoin de manager du Spark et de fournir des clusters tiers.

On peut maintenant migrer une codebase développée pour du Spark vers Snowpark, tout faire tourner nativement sur Snowflake et ne plus avoir à manager de cluster Spark. Et cette gestion de Spark est justement un point majeur de l’offre que propose actuellement Databricks.

Attention cependant : au moment de la rédaction de cet article, l’intégralité de la syntaxe Spark n’est pas encore supportée. Par exemple, tous les alias ne sont pas encore supportés : pour accéder à une colonne empDF("dept_id") et empDF.col("dept_id") sont tous les 2 supportés par Spark mais Snowpark ne supporte que le second. On notera également un manque dans la documentation sur certaines fonctions n’étant pas similaire, par exemple pour une jointure sur plusieurs colonnes.

Un gros avantage par rapport à Databricks est la disponibilité immédiate des Virtual Warehouses de Snowflake, là où Databricks prend de l'ordre de 1 à 10 minutes pour démarrer un cluster.

On tire également pleinement profit de la politique de Snowflake consistant à leur laisser la gestion des partitions. Pour des équipes avec un niveau débutant en Spark comme c'est mon cas, le partitionnement optimal du dataset et la gestion des fichiers, qui requièrent une compréhension avancée du fonctionnement de Spark, peut être déléguée à Snowflake. Avec Snowpark, pas besoin de former une équipe sur Spark et plus de troubleshooting sur ces deux points.

De plus, le micro partitioning de Snowflake aide fortement sur le pruning et on peut le voir. C’est en tout cas très visible pour TPC-DS SF1000. Je vous spoil peu les résultats, mais en investiguant j’ai vu que les temps gagnés par Snowflake sur Databricks pour les requêtes où il était plus rapide se sont principalement faits sur le pruning où Snowflake peut lire jusqu’à 10 fois moins de données. Ce qui est probablement proche du maximum, puisque les partitions Snowflake sont dans notre cas 10 fois plus petites puisque que Databricks recommande de partitionner nos tables en fichiers de 256 Mo pour un dataset de 1 To, alors que Snowflake indique avoir des fichiers de 50 à 500 Mo avant compression qui finissent par faire au maximum 16 Mo compressés peu importe la taille du dataset.

TPC-DS

Qu’est-ce que TPC-DS

Snowflake est une boite noire. Puisqu’on ne connaît pas son fonctionnement interne, notre seul moyen de comparaison doit être une mesure empirique des performances entre Snowflake et d’autres systèmes sur un jeu de données et un ensemble de requêtes qui seront le plus représentatifs de ce qu’on pourrait retrouver dans des cas réel d’utilisation. Pour cela, j’ai utilisé le benchmark TPC-DS.

TPC-DS est un benchmark défini par le “Transaction Processing Performance Council” (TPC), une organisation à but non-lucratif ayant pour objectif de créer des benchmarks réalistes et représentatifs, reproduisant des situations qu'on pourrait rencontrer dans des cas réels .

Parmi les benchmarks qu'ils proposent on retrouve “DS” pour "Decision Support", un benchmark conçu pour l’OLAP.

Le benchmark simule un système marchand utilisant plusieurs canaux de distribution : en ligne, par catalogue et en magasin. En plus du jeu de données, il comprend un ensemble de 99 requêtes SQL de BI dont la complexité varie fortement, allant d'un simple filtre à des requêtes d'une centaine de lignes avec de nombreuses imbrications et jointures entre tables de faits.

Depuis sa sortie en 2012 après 10 ans de développement, TPC-DS s'est imposé comme le benchmark d'informatique décisionnelle et est couramment utilisé par les vendeurs de bases de données et autres entreprises du Big Data pour mesurer leurs performances. Mais comme mentionné précédemment, vous trouverez rarement des benchmarks comparant les solutions les unes par rapport aux autres à cause de la fameuse clause de DeWitt.

Les coûts de ces benchmarks à grande échelle sont exorbitants : le système utilisé par Databricks en novembre 2021 leur a coûté près de 5,2 millions de dollars.

Ces coûts combinés à la clause de DeWitt empêchent TPC de conduire eux même les benchmarks. Ils s’en remettent donc aux vendeurs pour mesurer eux même les performances de leurs systèmes. Et pour éviter tous problèmes, les spécifications de TPC-DS sont très contraignantes afin de garantir l’objectivité et la transparence.

Ces contraintes, combinées à la boîte noire qu’est Snowflake font qu’il est impossible pour qui que ce soit d’autre que Snowflake de publier un résultat complet et officiel de TPC-DS. Cependant, rien ne nous force à exécuter un benchmark complet. Une des parties de TPC-DS appelée le “Power Run” consiste simplement à mesurer le temps d’exécution total des 99 requêtes exécutées séquentiellement.

Spécifications de TPC-DS

Ce Power Run, ainsi que le benchmark complet, peuvent être faits sur différents volumes de données appelés “Scale factors” ou “SF”. L’unité du SF est le gigaoctet mais il est souvent mesuré en téraoctets une fois qu’il le dépasse. On a donc par exemple SF1 à SF1000 de 1Go à 1To, puis SF10To, SF100To pour les valeurs dépassant 1To. Le plus petit scale factor admissible pour un benchmark officiel est SF1000 et le plus grand à ce jour est SF100000 ou SF100To dans sa version 3.2 .

Attention cependant à un point très important : TPC eux même insiste en longueur que les résultats ne sont pas comparables d’un scale factor à un autre puisque différents problèmes apparaissent et disparaissent à différentes échelles. Un système peut être le plus performant sur un SF1To mais être le moins performant sur un SF100To, ou vice versa. Cette distinction s’applique à la fois sur les temps d’exécution et sur les coûts.

Pour des raisons budgétaires, le benchmark de cet article a été réalisé sur un SF1000, c'est-à-dire 1To, qui est un scale factor reconnu pour le benchmark officiel.

Je rejoins une grande lignée en n'exécutant pas un benchmark officiel complet.

 "... there have been very few submissions to the official TPC-DS benchmark, despite more than 4 million pages on the Internet about TPC-DS" - Databricks

Bien qu’il ne soit pas soumis officiellement, j’ai fait de mon mieux pour respecter les contraintes imposées par la spécification afin d’obtenir un résultat le plus objectif possible.

J’ai donc également respecté les contraintes relatives à l’optimisation des tables qui interdisent le partitionnement et le clustering sur les colonnes autres que les clés primaires et les colonnes référençant une date.

Côté Databricks, en raison des très fortes cardinalités de ces colonnes sur les tables volumineuses, je n’ai partitionné aucune table. Un partitionnement aurait créé un trop grand nombre de fichiers qui auraient grandement ralenti nos requêtes. Je les ai cependant triées avant de les répartir en fichiers d'approximativement 256 Mo avec OPTIMIZE ZORDER BY. Il s’agit de la taille de fichiers recommandée par Databricks pour cette taille de tables. Je me suis basé sur le code mis à disposition par Databricks sur spark-sql-perf qui a simplement été modifié pour exécuter TPC-DS 3.2 au lieu de 2.4 et utiliser le z-ordering au lieu du partitionnement en vue de la taille plus petite de mon scale factor.

Côté Snowflake, rien de plus simple. Snowflake étant une solution clés en main, les optimisations se font automatiquement lors du chargement des données. La seule optimisation supplémentaire que j’aurais pu effectuer est de spécifier du clustering sur les colonnes autorisées en plus de la détection de clustering qu’effectue automatiquement Snowflake.

J’ai choisi de ne pas le faire car un des intérêts de Snowflake est justement le manque de configuration et que Snowflake bénéficie déjà d’avantages normalement interdits par TPC-DS : lors du chargement, toutes les colonnes sont partitionnées en micro-partitions et une collecte d’information sur le clustering est effectuée.

Résultats

Un SF1000 est le plus petit scale factor autorisé par TPC-DS. Il est jugé suffisamment volumineux pour fournir un résultat représentatif entre les vendeurs. Attention cependant : les résultats TPC-DS ne sont comparables qu'à des scale factor similaires, les contraintes n'étant pas les mêmes à ces différentes échelles. C'est la raison même pour laquelle TPC-DS propose ces différents SF : afin de se baser sur celui étant le plus proche du volume attendu par notre système. Un classement basé sur un SF10To pourrait se voir complètement inversé sur un SF100To.

Les résultats de ce benchmark ne sont donc comparables que dans les limites qui lui ont été appliquées.

A savoir :

  • Pour un scale factor de 1To (= SF1000).
  • Pour Databricks : en utilisant le moteur Photon sur des tables Delta n'ayant pas été partitionnées mais ayant été zorder sur leur clé primaire et clés référençant une dimension temporelle.
  • Pour Snowflake : sans effectuer de clustering manuel.
  • Pour un nombre égal de cœurs CPU. L'écart de RAM étant inconnu.
  • En utilisant une Virtual Warehouse S de Snowflake (8 cœurs) et 2 workers r5d.xlarge pour Databricks.

De plus, comme expliqué dans le post de Snowflake, chaque solution a son propre sweetspot selon le volume de données utilisé. Dans le cas de leur article, Snowflake n'avait pas d'avantage à passer d'un 4XL à un 5XL, là où c'était le cas de Databricks. Dans notre cas, l'inverse se produit. Le gain en performance d'un XS à un S à un M est prononcé chez Snowflake là où il l'est très peu voire pas du tout pour Databricks. C’est pour cette raison que j’ai décidé de choisir une taille S pour ce comparatif.

alt_text
Snowpark est plus rapide d'environ 15%

L’exécution des 99 requêtes se fait en 3437 secondes sur Databricks et en 2927 secondes sur Snowpark. Autrement dit, Snowpark est 14,8% plus rapide que Databricks sur ce benchmark.

On notera néanmoins un petit rappel : comme mentionné précédemment, un résultat officiel de TPC-DS ne peut pas optimiser le jeu de données comme il le souhaite. Le clustering et partitionnement des colonnes sont limités aux clés primaires et aux clés étrangères de dimensions temporelles. Or, Snowflake applique des optimisations sur l'ensemble des colonnes lors du chargement des tables. L'étendue des ces optimisations est inconnue puisqu'il s'agit d'une boîte noire et je n'ai pas pu lui préciser les contraintes du benchmark. On a donc une asymétrie entre les optimisations que j'ai pu effectuer sur Databricks par rapport à celles que Snowflake a pu appliquer de son côté.

Coûts

Nous avons vu qu’à ressources égales Snowpark est plus rapide sur l’exécution du benchmark. Mais intéressons nous maintenant aux coûts.

Les coûts de stockages sont exclus puisqu’ils sont identiques entre les 2 fournisseurs :  23$ par mois par téraoctets. Pour ce qui est des coûts de calcul en revanche, les 2 plateformes ont un système de tarification différent :

  • Databricks nous facture la gestion des clusters avec leurs crédits appelé le DBU en plus du coût des ressources que nous devons lui fournir. On paie donc à la fois pour les ressources et pour les DBU.
  • Snowflake possède un système plus simple où des crédits Snowflake sont consommés pendant l’exécution des Virtual Warehouses. Ces crédits couvrent à la fois le coût des ressources et celui des services offerts par Snowflake.

Ce n’est cependant pas parce que les coûts de Snowflake sont centralisés qu’ils vont être moins cher, au contraire.

Snowflake est 2 fois plus cher que Databricks à cette échelle

A noter qu’il est possible de jouer sur ces coûts en fonction de la version de Snowflake ou Databricks. Pour ces calculs j’ai choisi la version “Entreprise” des deux plateformes.

On pourrait davantage diminuer les coûts sur Databricks avec l’utilisation d’instances spot à prix réduit. Attention cependant avec les instances spot sur Databricks :  on peut perdre des nodes en plein calcul. Une instance spot a par exemple été perdue lors d’une de mes exécution du benchmark au cours de la requête la plus gourmande, ce qui s’est vu immédiatement sur le temps de la requête et le temps total du benchmark.

Il est également utile de noter que l’écart de prix nous permettrait d’allouer plus de ressources à Databricks et obtenir de meilleures performances si on voulait mettre les deux plateformes sur un pied d'égalité au niveau des coûts au lieu de les mettre à égalité côté ressources de calcul.

Afin de donner un comparatif plus générique sur les prix, on peut calculer l’écart de coûts entre une Virtual Warehouse Snowflake et son équivalent sur Databricks avec Photon.

Une warehouse XS possède 8 threads, une mémoire vive non spécifiée et coûte 1 crédit Snowflake par heure, soit 3,90$. Chaque taille au-dessus du XS double les ressources et les coûts. Nos coûts par heure seront donc de 3,90xx vaut 1 pour un XS et double à chaque échelle suivante.

L’équivalent en ressources d’un XS Snowflake sur AWS est un r5d.xlarge qui coûte 0,346$/h sur eu-central-1. 1 DBU utilisant le moteur Photon de Databricks Entreprise coûte 0,20$/h. On obtient un coût pour un équivalent XS de :

  • 1 m5d.xlarge et 1,38 DBU par heure pour le driver. Soit 0,548$/h
  • 2 r5d.xlarge et 3,6 DBU par heure pour les workers. Soit 1,412$/h

Lors d’un scale up dans notre cas, puisque la taille du driver n’est pas corrélée au nombre de workers et que les requêtes restent les mêmes, on peut garder la taille de driver constante et uniquement doubler les ressources des workers. Nos coûts par heure seront donc 0,548+1,412xx vaut 1 pour un XS et double à chaque échelle suivante.

On obtient alors la courbe suivante.

alt_text
Les prix de Snowflake explosent avant ceux de Databricks

Pour référence, un 4XL sur Snowflake et donc Snowpark coûte 128 crédits par heure, soit 499,20$. L’équivalent sur Databricks coûterait au total 181,28$. Avec ces hypothèses, Snowpark sera toujours près de 2,5 fois plus cher que Databricks. Et on voit qu'à grande échelle la différence est donc substantielle. Attention cependant, cette extrapolation est valide pour les prix mais ne l’est pas pour les performances qui ne sont pas linéaires.

Conclusion

En termes de performances, on voit qu’à un Scale Factor de l’ordre du To, pour une équipe avec un niveau de débutant en Spark, Snowpark est plus rapide d’environ 15% sur un pipeline complet.

Cependant, si on se concentre sur les coûts, Databricks représente une fraction du prix de Snowflake puisqu’il est 2,5 à 3 fois moins coûteux. Cet écart va donc rapidement se faire sentir quand on monte en échelle. On paie ici le coût de la solution 100% managée et c'est une différence qui peut ou non valoir le coût en fonction des équipes et de l’échelle de votre projet.

L’avantage apporté par ces coûts supplémentaires, en plus du gain de performances à cette échelle, est la gestion automatisée du stockage des données par Snowflake. On peut aisément migrer une codebase existante de Spark vers Snowpark grâce à leur syntaxe similaire voir identique et ainsi libérer l’équipe Spark pour d’autres projets.