Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Le moteur d’exécution natif de Microsoft Fabric prend désormais en charge les fonctions définies par l’utilisateur (UDF) Python, les UDF Scala et les types de données complexes (tableaux, mappages et structs). Ces fonctionnalités vous permettent d’écrire des applications Spark expressives sans sacrifier les performances.
Prise en charge des UDF Python
Python est l’un des langages les plus populaires dans l’ingénierie des données et la science des données. Historiquement, les UDF Python ont entraîné une surcharge importante dans Spark en raison des coûts de sérialisation entre la JVM et les processus workers Python. Le moteur d’exécution natif réduit ces transitions coûteuses, ce qui accélère l’exécution sans modification du code.
Fonctionnement des fonctions Python définies par l’utilisateur dans le moteur d’exécution natif
Dans un modèle d’exécution Spark classique, l’exécution des UDF Python implique :
- Conversion de données à partir du format interne de Spark.
- Sérialisation et transfert vers les processus de travail Python.
- Python exécution UDF.
- Sérialisation des résultats dans la machine virtuelle JVM.
- Spark reprend l’exécution.
Ces transferts entre environnements d’exécution entraînent des coûts de sérialisation et de désérialisation, une utilisation inefficace du processeur et des pipelines d’exécution colonnaire défaillants. Le moteur d’exécution natif réduit cette surcharge en optimisant le chemin de transfert de données et en conservant le traitement vectorisé si possible.
Types d’UDF Python pris en charge
Le moteur d’exécution natif prend en charge :
-
Scalar UDFs : fonctions de Python ligne par ligne inscrites auprès de
udf(). -
Fonctions définies par l’utilisateur (UDF) vectorisées (Pandas) : fonctions décorées avec
@pandas_udf, qui traitent des lots de données à l’aide d’Apache Arrow pour un transfert efficace.
Les UDF vectorisées offrent les gains de performances les plus importants, car elles s’alignent naturellement sur le modèle de traitement colonnaire du moteur d’exécution natif.
Exemple : UDF vectorisée Python
import pandas as pd
from pyspark.sql.functions import pandas_udf
from pyspark.sql.types import DoubleType
@pandas_udf(DoubleType())
def calculate_discount(price: pd.Series, rate: pd.Series) -> pd.Series:
return price * (1 - rate)
df = spark.table("sales.transactions")
result = df.withColumn("discounted_price", calculate_discount(df.price, df.discount_rate))
result.show()
Aucune configuration supplémentaire n’est requise au-delà de l’activation du moteur d’exécution natif. Les UDF Python existantes bénéficient automatiquement.
Prise en charge des UDF Scala
Le moteur d’exécution natif accélère également les fonctions UDF Scala. Comme les UDF Scala s’exécutent nativement sur la JVM, le moteur peut déléguer les opérations prises en charge au pipeline d’exécution C++ vectorisé tout en maintenant l’efficacité de l’évaluation des UDF Scala dans le même environnement d’exécution.
Exemple : UDF Scala
import org.apache.spark.sql.functions.udf
val toUpperCase = udf((s: String) => s.toUpperCase)
val df = spark.table("catalog.customers")
val result = df.withColumn("name_upper", toUpperCase(df("name")))
result.show()
Les UDF Scala qui utilisent des types de données pris en charge sont accélérées sans modifier le code lorsque le moteur d’exécution natif est activé.
Prise en charge des types de données complexes
Les architectures lakehouse modernes dépendent de données semi-structurées et imbriquées. Le moteur d’exécution natif fournit désormais une prise en charge optimisée pour :
| Type de données | Description | Exemple de cas d’usage |
|---|---|---|
| Tableau | Collection ordonnée d’éléments | Étiquettes d’événements, catégories de produits |
| Carte | Paires clé-valeur | Propriétés de configuration, métadonnées |
| Struct | Champs nommés de types différents | Enregistrements clients imbriqués, objets d’adresse |
Opérations prises en charge pour les types complexes
Le moteur d’exécution natif accélère les opérations courantes sur les types de données complexes :
- Fonctions de tableau :
explode, ,array_containssize,flattentransform - Fonctions cartographiques :
map_keys,map_values,element_at - Accès au struct : accès aux champs avec la notation par point,
getField - Combinaisons imbriquées : tableaux de structures, associations avec des valeurs de type tableau
Exemple : Travailler avec des tableaux et des structures
from pyspark.sql.functions import explode, col, size
# Read data with nested schema
df = spark.table("events.telemetry")
# Operations on arrays - accelerated by native engine
result = (df
.filter(size(col("tags")) > 0)
.select(
col("event_id"),
col("metadata.source"), # Struct field access
explode(col("tags")).alias("tag")
)
)
result.show()
Exemple : Utilisation de cartes
from pyspark.sql.functions import map_keys, map_values, col
df = spark.table("config.settings")
# Map operations - accelerated by native engine
result = (df
.select(
col("setting_id"),
map_keys(col("properties")).alias("keys"),
map_values(col("properties")).alias("values")
)
)
result.show()
Résultats des performances
Les tests de performance internes montrent des améliorations significatives dans l’ensemble des charges de travail qui utilisent des UDF Python et des types de données complexes :
| Type de charge de travail | Amélioration des performances |
|---|---|
| Fonctions définies par l’utilisateur Python vectorisées | Jusqu’à 5,76 fois plus rapidement |
| UDF Python scalaires | Jusqu’à 1,08x plus rapide |
| TPC-DS de bout en bout (avec des types complexes) | Jusqu’à 2,35 fois plus rapidement |
Ces gains résultent de la réduction de la surcharge de sérialisation, de la vectorisation améliorée et de l’exécution en colonnes de bout en bout.
Avantages des schémas lakehouse avancés
L’accélération des types de données complexes est particulièrement importante pour :
- Optimisation Z-ORDER : les colonnes imbriquées participent à une organisation optimisée des données.
- Clustering liquide : les colonnes de type complexe tirent parti du clustering sans avoir à être aplaties.
- Analytique semi-structurée : les charges utiles JSON et les flux d’événements restent imbriqués pour l’interrogation naturelle.
- Architectures pilotées par les événements : les données de télémétrie et IoT conservent leur structure hiérarchique.
Au lieu d’aplatir les données ou de restructurer les pipelines pour améliorer les performances, travaillez naturellement avec des schémas de données complexes tout en maintenant une grande efficacité d’exécution.
Activer la fonctionnalité
La prise en charge des UDF Python, des UDF Scala et des types de données complexes est disponible lorsque le moteur d’exécution natif est activé. Aucune configuration supplémentaire n’est nécessaire.
Pour activer le moteur d’exécution natif, consultez le moteur d’exécution natif pour l’ingénierie des données Fabric.
Prerequisites
- Runtime 1.3 (Apache Spark 3.5) ou Runtime 2.0 (Apache Spark 4.0).
- Moteur d’exécution natif activé au niveau de l’environnement, du notebook ou de la définition de tâche Spark.
Limitations
- Toutes les bibliothèques Python ne sont pas prises en charge dans le chemin vectorisé. Les bibliothèques qui nécessitent la sérialisation d’objets Python arbitraires peuvent encore activer un mécanisme de repli.
- Les types complexes profondément imbriqués (par exemple, les tableaux de cartes de structs) peuvent revenir au moteur JVM pour certaines opérations.
- Le mode ANSI n’est pas pris en charge avec le moteur d’exécution natif.