Profilage des gestionnaires de fonctions définies par l’utilisateur de Snowpark Python

Vous pouvez découvrir combien de temps ou de mémoire a été consacré à l’exécution du code de votre gestionnaire en utilisant le profileur de code intégré. Le profilage génère des informations décrivant le temps ou la mémoire consacrés à l’exécution de chaque ligne du gestionnaire.

À l’aide du profileur, vous pouvez générer des rapports axés sur l’un des éléments suivants à la fois :

  • Quantité de temps par ligne, qui indique le nombre de fois qu’une ligne a été exécutée, la durée de l’exécution, etc.

  • Utilisation de la mémoire par ligne, qui indique la quantité de mémoire consommée par ligne.

Le profileur enregistre le rapport généré dans un tableau des événements interne. Vous pouvez récupérer les résultats en utilisant une fonction conçue pour accéder à la table.

Note

Le profilage introduit une surcharge de performance lors de l’exécution de Python et peut affecter les performances de la requête. Il est destiné au développement et aux tests et ne doit pas être activé sur des charges de travail de production continue.

Privilèges requis

Pour gérer et utiliser les données des résultats du profileur, qui sont stockées dans le tableau des événements SNOWFLAKE.LOCAL.PROFILER_EVENTS_RAW, vous devez utiliser les rôles suivants :

Rôle de l’application

Remarques

PROFILER_EVENTS_ADMIN

Nécessaire pour gérer les données du tableau des événements dans lequel sont stockées les données du profileur, y compris pour sélectionner, tronquer ou détruire des enregistrements.

PROFILER_USER

Nécessaire pour lire les résultats du profileur à partir du tableau des événements.

Pour plus d’informations sur l’attribution d’un rôle d’application, consultez GRANT APPLICATION ROLE. L’exemple suivant utilise le rôle ACCOUNTADMIN pour accorder le rôle d’application PROFILER_USER à un utilisateur.

USE ROLE ACCOUNTADMIN;
CREATE ROLE PROFILER_ROLE;
GRANT APPLICATION ROLE SNOWFLAKE.PROFILER_USER TO ROLE PROFILER_ROLE;
GRANT ROLE PROFILER_ROLE TO USER some_user;

Limitations

  • Il peut s’écouler 15 à 20 secondes après l’exécution de la requête pour que les résultats du profileur soient prêts.

  • La sortie du profileur n’est pas enregistrée si l’exécution de la UDF échoue.

  • Le profilage récursif n’est pas pris en charge. Seules les fonctions de haut niveau des modules spécifiés sont profilées. Les fonctions définies à l’intérieur de fonctions ne le sont pas.

  • Le profilage des modules tiers n’est pas pris en charge.

  • La prise en charge des UDFs de profilage créées côté client via l’API snowflake.snowpark n’est pas disponible.

  • Les fonctions Python exécutées en parallèle sur joblib ne sont pas profilées.

  • UDTFs ne sont pas pris en charge.

  • Le temps est mesuré en durée chronométrée, et non en temps de CPU.

Utilisation

Une fois le profileur configuré, vous pouvez l’utiliser simplement en exécutant la UDF pour générer la sortie du profileur. Une fois que la UDF a fini de s’exécuter, la sortie du profileur est écrite dans un tableau des événements interne. Vous pouvez récupérer la sortie du profileur à l’aide d’une fonction système.

Suivez les étapes suivantes pour configurer et utiliser le profileur dans votre code :

  1. Activez le profileur et définissez le rapport de profil.

  2. Exécutez la UDF.

  3. Affichez la sortie du profilage.

Activer le profileur en spécifiant son focus

Pour activer le profileur, définissez l’un des paramètres de session suivants :

-- To enable profiling that focuses on activity per line
ALTER SESSION SET ACTIVE_PYTHON_PROFILER = 'LINE';

-- To enable profiling that focuses on memory usage
ALTER SESSION SET ACTIVE_PYTHON_PROFILER = 'MEMORY';

Note

Le profilage introduit une surcharge de performance lors de l’exécution de Python. Vous devez profiler votre code lors du développement et des tests. N’activez pas le profilage sur des charges de travail de production continue.

Spécifier le code à profiler

Par défaut, le profileur profile les méthodes définies en ligne avec la déclaration de la UDF. En d’autres termes, le profileur profilera toutes les méthodes définies dans le gestionnaire.

Pour la UDF d’exemple suivante, le profileur profilera la méthode handler et la méthode helper.

CREATE OR REPLACE function my_udf()
  RETURNS VARIANT
  LANGUAGE PYTHON
  RUNTIME_VERSION = 3.11
  PACKAGES = ('other_package')
  HANDLER = 'handler'
  AS $$
from other_package import some_method

def helper():
...

def handler():
...
$$;

Spécifier le code externe à profiler

Vous pouvez spécifier que le profileur doit profiler le code du gestionnaire défini en dehors de la UDF, comme un code importé à partir d’une zone de préparation.

Pour spécifier le code externe pour le profilage, définissez la valeur du paramètre de session PYTHON_UDF_PROFILER_MODULES sur une liste des modules contenant le code, séparés par des virgules.

ALTER SESSION SET PYTHON_UDF_PROFILER_MODULES = 'test_python_import_main, test_python_import_module';

Le profileur inclura les modules spécifiés dans sa sortie de profilage lorsque vous exécuterez une UDF qu’ils importent.

Le code dans l’exemple suivant montre une UDF qui importe le code des modules spécifiés :

CREATE OR REPLACE function test_udf_1()
  RETURNS STRING
  LANGUAGE PYTHON
  RUNTIME_VERSION = 3.11
  HANDLER = 'test_python_import_main.my_udf'
  IMPORTS = ('@stage1/test_python_import_main.py', '@stage2/test_python_import_module.py');

Exécuter la fonction définie par l’utilisateur

Après avoir activé le profileur, exécutez votre fonction définie par l’utilisateur (UDF) pour commencer le profilage.

Par défaut, le profileur profile les méthodes définies dans votre module. Pour plus d’informations sur l’enregistrement d’autres modules à partir de fichiers importés à profiler, consultez Spécifier le code à profiler.

SELECT return_mean(my_col) FROM MY_TABLE;

Voir la sortie du profilage

Les résultats du profilage sont généralement disponibles dans le tableau des événements 15 à 20 secondes après la fin de l’exécution de la UDF. Vous pouvez accéder à la sortie en utilisant la fonction système de la table, GET_PYTHON_UDF_PROFILER_OUTPUT.

Le code dans l’exemple suivant montre une requête sur le tableau des événements pour les résultats du profileur. L’query_id spécifié comme argument est l’ID de requête de la requête d’UDF pour lequel le profilage a été activé.

SELECT * FROM TABLE(SNOWFLAKE.LOCAL.GET_PYTHON_UDF_PROFILER_OUTPUT(<query_id>));

Résultats du profil

Lorsque vous afficherez les résultats du profileur, vous verrez un rapport qui diffère selon que vous avez spécifié le profilage pour un rapport de ligne ou un rapport de mémoire.

La sortie du profileur de mémoire se présente comme suit :

Handler Name: return_mean
Python Runtime Version: 3.12
Modules Profiled: ['return_mean_module']
Extension Function ID: 1

File: _udf_code.py
Function: return_mean at line 2

Line #    Mem usage    Increment  Occurrences    Line Contents
==============================================================
     2    107.0 MiB    107.0 MiB           1    def return_mean():
     3    144.6 MiB     37.6 MiB           1        import numpy as np
     4
     5                                              # Generate a numpy array with 10 random integers between 1 and 100
     6                                              # np.random.randint(low, high, size)
     7    147.3 MiB      2.7 MiB           1        random_array = np.random.randint(1, 101, 10)
     8
     9                                              # Use a numpy function to calculate the mean
    10    147.3 MiB      0.0 MiB           1        mean_value = np.mean(random_array)
    11
    12    147.3 MiB      0.0 MiB           1        count = 0
    13    147.3 MiB      0.0 MiB         101        for i in range(100):
    14    147.3 MiB      0.0 MiB         100            count = count + 1
    15
    16    147.3 MiB      0.0 MiB           1        return mean_value

La sortie du profil de ligne ressemblera à ceci :

Handler Name: return_mean
Python Runtime Version: 3.12
Extension Function ID: 1
Modules Profiled: ['return_mean_module']
Timer Unit: 0.001 s

Total Time: 0.229063 s
File: _udf_code.py
Function: return_mean at line 2

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
     2                                           def return_mean():
     3         1        206.1    206.1     90.0      import numpy as np
     4
     5                                               # Generate a numpy array with 10 random integers between 1 and 100
     6                                               # np.random.randint(low, high, size)
     7         1         22.8     22.8     10.0      random_array = np.random.randint(1, 101, 10)
     8
     9                                               # Use a numpy function to calculate the mean
    10         1          0.1      0.1      0.0      mean_value = np.mean(random_array)
    11
    12         1          0.0      0.0      0.0      count = 0
    13       101          0.0      0.0      0.0      for i in range(100):
    14       100          0.0      0.0      0.0          count = count + 1
    15
    16         1          0.0      0.0      0.0      return mean_value