web-dev-qa-db-fra.com

Spark fonctions vs performances UDF?

Spark propose désormais des fonctions prédéfinies qui peuvent être utilisées dans les trames de données, et il semble qu'elles soient hautement optimisées. Ma question initiale allait être sur ce qui est plus rapide, mais j'ai fait quelques tests moi-même et j'ai trouvé que les fonctions spark étaient environ 10 fois plus rapides au moins dans un cas. Est-ce que quelqu'un sait pourquoi c'est donc, et quand un udf serait-il plus rapide (seulement pour les cas où une fonction identique spark existe))?

Voici mon code de test (exécuté sur la communauté de Databricks ed):

# UDF vs Spark function
from faker import Factory
from pyspark.sql.functions import lit, concat
fake = Factory.create()
fake.seed(4321)

# Each entry consists of last_name, first_name, ssn, job, and age (at least 1)
from pyspark.sql import Row
def fake_entry():
  name = fake.name().split()
  return (name[1], name[0], fake.ssn(), fake.job(), abs(2016 - fake.date_time().year) + 1)

# Create a helper function to call a function repeatedly
def repeat(times, func, *args, **kwargs):
    for _ in xrange(times):
        yield func(*args, **kwargs)
data = list(repeat(500000, fake_entry))
print len(data)
data[0]

dataDF = sqlContext.createDataFrame(data, ('last_name', 'first_name', 'ssn', 'occupation', 'age'))
dataDF.cache()

Fonction UDF:

concat_s = udf(lambda s: s+ 's')
udfData = dataDF.select(concat_s(dataDF.first_name).alias('name'))
udfData.count()

Fonction Spark:

spfData = dataDF.select(concat(dataDF.first_name, lit('s')).alias('name'))
spfData.count()

Exécuté à la fois plusieurs fois, le udf prenait généralement environ 1,1 à 1,4 s et la fonction Spark concat prenait toujours moins de 0,15 s.

29
alfredox

quand un udf serait-il plus rapide

Si vous posez des questions sur Python UDF, la réponse n'est probablement jamais *. Étant donné que les fonctions SQL sont relativement simples et ne sont pas conçues pour des tâches complexes, il est pratiquement impossible de compenser le coût de la sérialisation, de la désérialisation et des données répétées mouvement entre Python et JVM.

Quelqu'un sait-il pourquoi

Les principales raisons sont déjà énumérées ci-dessus et peuvent être réduites à un simple fait que Spark DataFrame est nativement une structure JVM et les méthodes d'accès standard sont implémentées par de simples appels à Java API. UDF d'autre part sont implémentés dans Python et nécessitent le déplacement de données dans les deux sens.

Bien que PySpark en général nécessite des mouvements de données entre JVM et Python, en cas d'API RDD de bas niveau, il ne nécessite généralement pas d'activité de serde coûteuse. Spark SQL ajoute un coût supplémentaire de sérialisation et de sérialisation ainsi que le coût de déplacement des données depuis et vers une représentation non sécurisée sur JVM. Le dernier est spécifique à tous les UDF (Python, Scala et Java) mais le premier est spécifique aux langues non natives.

Contrairement aux UDF, les fonctions Spark SQL fonctionnent directement sur la machine virtuelle Java et sont généralement bien intégrées à la fois avec Catalyst et Tungsten. Cela signifie qu'elles peuvent être optimisées dans le plan d'exécution et la plupart du temps peuvent bénéficier de codgen et d'autres optimisations Tungsten, qui peuvent en outre fonctionner sur les données dans leur représentation "native".

Donc, dans un sens, le problème ici est que Python UDF doit apporter des données au code tandis que les expressions SQL vont dans l'autre sens).


* Selon estimations approximatives la fenêtre PySpark UDF peut battre Scala.

42
zero323

Depuis le 30 octobre 2017, Spark vient d'introduire des udfs vectorisés pour pyspark.

https://databricks.com/blog/2017/10/30/introducing-vectorized-udfs-for-pyspark.html

La raison pour laquelle Python UDF est lent, est probablement que PySpark UDF n'est pas implémenté de la manière la plus optimisée:

Selon le paragraphe du lien.

Spark a ajouté une API Python dans la version 0.7, avec prise en charge des fonctions définies par l'utilisateur. Ces fonctions définies par l'utilisateur fonctionnent une ligne à la fois) , et souffrent ainsi d'une surcharge de sérialisation et d'invocation élevée.

Cependant, les udfs nouvellement vectorisés semblent améliorer considérablement les performances:

allant de 3x à plus de 100x.

enter image description here

9
Tom Tang