Profilage des gestionnaires de procédures stockées 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 profileur génère des informations décrivant le temps ou la mémoire consacrés à l’exécution de chaque ligne du gestionnaire de procédure.

À 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, dans lequel le rapport 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, dans lequel le rapport indique la quantité de mémoire consommée par ligne.

Le profileur enregistre le rapport généré dans la zone de préparation interne à l’utilisateur de Snowflake que vous spécifiez. Vous pouvez lire la sortie du profileur à l’aide de la fonction StoredProcedureProfiler.get_output.

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

Lorsqu’une procédure stockée est exécutée après l’appel de la fonction StoredProcedureProfiler.set_active_profiler, Snowflake vérifie les privilèges suivants pour l’utilisateur qui exécute la procédure :

Limitations

  • Seules les procédures stockées sont prises en charge. Les UDFs ne sont pas encore prises en charge.

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

  • Le profilage des procédures stockées créées côté client via l’API snowflake.snowpark n’est pas pris en charge.

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

  • Les procédures stockées définies par le système ne peuvent pas être profilées. Elles ne produisent aucune sortie.

  • L’API du profilage doit être utilisée dans le même thread que celui à partir duquel la procédure a été appelée.

Utilisation

Une fois l’ensemble des paramètres définis, vous pouvez utiliser le profileur en appelant simplement la procédure stockée pour générer la sortie du profileur. Une fois l’exécution de la procédure terminée, la sortie du profileur est écrite dans un fichier sur la zone de préparation que vous avez spécifiée. Vous pouvez récupérer la sortie du profileur à l’aide d’une fonction du système, comme décrit ci-dessous.

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

  1. Faites l’acquisition d’un objet profileur à partir de l’objet Session.

  2. Indiquez la zone de préparation de Snowflake dans laquelle la sortie du profil doit être écrite.

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

  4. Appelez la procédure stockée.

  5. Affichez la sortie du profilage.

Acquérir un objet de profilage

En Python, créez une variable de type StoredProcedureProfiler avec laquelle vous configurerez et exécuterez le profileur.

# Create your sesssion
session = Session.builder.configs(CONNECTION_PARAMETERS).create()

# Acquire profiler object
profiler = session.stored_procedure_profiler()
Copy

Spécifiez la zone de préparation de Snowflake dans laquelle la sortie du profil doit être écrite

Avant d’exécuter le profileur, vous devez spécifier une zone de préparation dans laquelle enregistrer la sortie. Pour spécifier la zone de préparation, appelez StoredProcedureProfiler.set_target_stage, en spécifiant le nom complet d’une zone de préparation interne de Snowflake dans laquelle le rapport doit être écrit.

Gardez à l’esprit les points suivants :

  • Le nom de la zone de préparation doit être un nom entièrement qualifié.

  • Si la zone de préparation que vous entrez dans cette fonctionnalité n’existe pas, Snowflake crée une zone temporaire portant ce nom.

  • Si vous souhaitez conserver la sortie du profileur en dehors de la session, créez une zone de préparation permanente avant d’exécuter set_target_stage et spécifiez le nom de cette zone permanente dans l’appel de la fonction.

  • Si vous ne définissez pas de zone de préparation cible avec set_target_stage, Snowflake paramètre la zone de préparation temporaire de la session en cours comme zone cible. Pour découvrir cette zone de préparation temporaire, appelez Session.get_session_stage.

Le code de l’exemple suivant crée une zone de préparation temporaire profiler_output pour recevoir la sortie du profileur.

profiler.set_target_stage("mydb.myschema.profiler_output")
Copy

Activer le profileur en spécifiant son focus

Utilisez la fonction StoredProcedureProfiler.set_active_profiler, en spécifiant une valeur indiquant le type de rapport de profil que vous souhaitez générer.

  • Pour que le profileur rende compte de l’activité d’utilisation de la ligne, définissez le paramètre sur la valeur LINE (insensible à la casse), comme indiqué ci-dessous :

    profiler.set_active_profiler("LINE")
    
    Copy
  • Pour que le profileur rende compte de l’activité d’utilisation de la mémoire, définissez le paramètre sur la valeur MEMORY (insensible à la casse), comme indiqué ci-dessous :

    profiler.set_active_profiler("MEMORY")
    
    Copy

Pour désactiver le profileur, utilisez la fonction StoredProcedureProfiler.disable.

Appeler la procédure stockée

Une fois le profilage activé, appellez votre procédure stockée.

session.call("my_stored_procedure")
Copy

Voir la sortie du profilage

À la fin de l’exécution, vous pouvez accéder à la sortie à l’aide de la fonction StoredProcedureProfiler.get_output.

profiler.get_output()
Copy

Inclure des modules supplémentaires pour le profilage

Lors du profilage, vous pouvez inclure des modules qui ne sont pas inclus par défaut.

Par défaut, les méthodes définies dans votre module sont profilées. Ces méthodes incluent ce qui suit :

  • La méthode du gestionnaire

  • Méthodes définies dans le module

  • Méthodes importées de paquets ou d’autres modules

Pour inclure des modules supplémentaires pour le profilage, utilisez la fonction StoredProcedureProfiler.register_modules, en spécifiant les modules que vous souhaitez inclure.

Le code de l’exemple suivant enregistre les modules module_A et module_B pour le profilage.

profiler.register_modules(["module_A", "module_B"])
Copy

Pour annuler l’enregistrement des modules enregistrés, utilisez register_modules sans arguments, comme dans l’exemple suivant.

profiler.register_modules()
Copy

Exemple

Les exemples suivants montrent comment utiliser le profileur pour générer et récupérer un rapport sur l’utilisation des lignes.

Le code de cet exemple crée une procédure profiler_test_proc.

CREATE OR REPLACE PROCEDURE profiler_test_proc()
RETURNS NUMBER
LANGUAGE PYTHON
RUNTIME_VERSION = 3.8
PACKAGES = ('snowflake-snowpark-python')
HANDLER = 'main'
AS
$$
from snowflake.snowpark.functions import col, udf

def main(session):
  df = session.sql("select 1")
  return df.collect()[0][0]
$$;
Copy

Le code de l’exemple suivant établit un paramètre de profilage, puis profile la procédure profiler_test_proc.

profiler = profiler_session.stored_procedure_profiler
profiler.register_modules(["profiler_test_proc"])
profiler.set_target_stage(
  f"{db_parameters['database']}.{db_parameters['schema']}.{tmp_stage_name}"
)

profiler.set_active_profiler("LINE")

profiler_session.call("profiler_test_proc")
res = profiler.get_output()
print(res)

profiler.disable()
profiler.register_modules([])
Copy

La sortie du profil de ligne générée ressemble à ceci :

Handler Name: main
Python Runtime Version: 3.8
Modules Profiled: ['main_module']
Timer Unit: 0.001 s

Total Time: 0.0619571 s
File: _udf_code.py
Function: main at line 4

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
     4                                           def main(session):
     5         1          0.4      0.4      0.6      df = session.sql("select 1")
     6         1         61.6     61.6     99.4      return df.collect()[0][0]