Profiling für benutzerdefinierte Snowpark Python-Funktions-Handler

Mit dem integrierten Code-Profiler können Sie herausfinden, wie viel Zeit oder Speicher für die Ausführung Ihres Handler-Codes aufgewendet wurde. Der Profiler generiert Informationen, die beschreiben, wie viel Zeit oder Speicher für die Ausführung jeder Zeile Python-Code aufgewendet wurde.

Mit dem Profiler können Sie Berichte erstellen, die sich jeweils auf einen der folgenden Punkte konzentrieren:

  • Amount of time per line zeigt die Anzahl der Ausführungen einer Zeile, die Ausführungsdauer usw. an.

  • Amount of memory usage per line zeigt den pro Zeile verbrauchten Speicher an.

Der Profiler speichert den erstellten Bericht in einer internen:doc:Ereignistabelle</developer-guide/logging-tracing/event-table-columns>. Sie können die Ergebnisse mithilfe einer Funktion abrufen, die für den Zugriff auf die Tabelle vorgesehen ist.

Bemerkung

Profiling führt zu einem Performance-Overhead bei der Python-Ausführung und kann die Leistung der Abfrage beeinträchtigen. Sie ist für Entwicklung und Tests gedacht und sollte nicht für kontinuierliche Produktions-Workloads aktiviert werden.

Erforderliche Berechtigungen

Zum Verwalten und Verwenden der Profiler-Ergebnisdaten, die in der SNOWFLAKE.LOCAL.PROFILER_EVENTS_RAW-Ereignistabelle gespeichert werden, müssen Sie die folgenden Rollen verwenden:

Anwendungsrolle

Anmerkungen

PROFILER_EVENTS_ADMIN

Erforderlich für die Verwaltung von Daten in der Ereignistabelle, in der Profilerdaten gespeichert sind, einschließlich des Auswählens, Kürzens oder Löschens von Datensätzen.

PROFILER_USER

Erforderlich, um Profiler-Ergebnisse aus der Ereignistabelle zu lesen.

Weitere Informationen zum Zuweisen einer Anwendungsrolle finden Sie unter:doc:/sql-reference/sql/grant-application-role. Im folgenden Beispiel wird die Rolle ACCOUNTADMIN verwendet, um einem Benutzenden die Anwendungsrolle PROFILER_USER zuzuweisen.

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;

Einschränkungen

  • Nach der Ausführung der Abfrage kann es 15–20 Sekunden dauern, bis die Ergebnisse des Profilers bereit sind.

  • Die Profiler-Ausgabe wird nicht gespeichert, wenn die Ausführung der UDF fehlschlägt.

  • Rekursive Profilerstellung wird nicht unterstützt. Nur die Top-Level-Funktionen der angegebenen Module werden profiliert. Bei Funktionen, die innerhalb von Funktionen definiert sind, ist dies nicht der Fall.

  • Das Profiling von Modulen von Drittanbietern wird nicht unterstützt.

  • Unterstützung für die Profiling-UDFs, die clientseitig mit der ` snowflake.snowpark`-API erstellt wurden, ist nicht verfügbar.

  • Python-Funktionen, die parallel über joblib laufen, werden nicht profiliert.

  • UDTFs werden nicht unterstützt.

  • Die Zeit wird in Echtzeit (Wall Clock Time) und nicht in CPU-Zeit gemessen.

Verwendung

Sobald Sie den Profiler eingerichtet haben, können Sie ihn verwenden, indem Sie einfach die UDF ausführen, um die Profiler-Ausgabe zu generieren. Sobald die Ausführung der UDF beendet ist, wird die Profiler-Ausgabe in eine interne Ereignistabelle geschrieben. Sie können die Profiler-Ausgabe mit einer Systemfunktion abrufen.

Folgen Sie diesen Schritten in Ihrem Code, um den Profiler einzurichten und zu verwenden:

  1. Aktivieren Sie den Profiler und legen Sie fest, worauf sich der Profilbericht konzentrieren soll.

  2. Führen Sie die UDF aus.

  3. Profiling-Ausgabe ansehen.

Aktivieren Sie den Profiler, indem Sie seinen Fokus festlegen

Um den Profiler zu aktivieren, legen Sie einen der folgenden Sitzungsparameter fest:

-- 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';

Bemerkung

Profiling führt zu einem Performance-Overhead bei der Python-Ausführung. Sie sollten das Profiling für Ihren Code während der Entwicklung und des Testens vornehmen. Aktivieren Sie Profiling nicht für kontinuierliche Produktions-Workloads.

Angabe des zu Codes für das Profiling

Standardmäßig erstellt der Profiler ein Profil von Methoden, die inline mit der UDF-Deklaration definiert wurden. Mit anderen Worten: Der Profiler profiliert alle im Handler definierten Methoden.

Beim folgendenUDF-Beispiel erstellt der Profiler beispielsweise ein Profil der handler-Methode und der helper-Methode.

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():
...
$$;

Angeben des externen Codes für das Profiling

Sie können angeben, dass der Profiler ein Profil für Handler-Code erstellen soll, der außerhalb der UDF-Deklaration definiert wurde, wie z. B. aus einem Stagingbereich importierter Code.

Um externen Code für das Profiling anzugeben, legen Sie den Wert des Sitzungsparameters PYTHON_UDF_PROFILER_MODULES auf eine durch Kommas getrennte Liste der Module fest, die den Code enthalten.

ALTER SESSION SET PYTHON_UDF_PROFILER_MODULES = 'test_python_import_main, test_python_import_module';

Der Profiler fügt die angegebenen Module in seine Profiling-Ausgabe ein, wenn Sie eine UDF ausführen, die sie importiert.

Der Code im folgenden Beispiel zeigt eine UDF, die Code aus den angegebenen Modulen importiert:

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');

Ausführen der benutzerdefinierten Funktion

Nachdem Sie den Profiler aktiviert haben, führen Sie die benutzerdefinierte Funktion (UDF) aus, um mit dem Profiling zu beginnen.

Standardmäßig profiliert der Profiler Methoden, die in Ihrem Modul definiert sind. Weitere Informationen zum Registrieren anderer Module aus importierten Dateien im Profil finden Sie unter:ref:label-snowpark_python_udf_profiler_additional.

SELECT return_mean(my_col) FROM MY_TABLE;

Profiling-Ausgabe ansehen

  • Um die Ausgabe des Profilings anzuzeigen, fragen Sie eine interne Ereignistabelle ab.

Die Ergebnisse des Profilings sind normalerweise 15–20 Sekunden, nachdem die UDF die Ausführung beendet hat, in der Ereignistabelle verfügbar. Sie können auf die Ausgabe zugreifen, indem Sie die Tabellensystemfunktion GET_PYTHON_UDF_PROFILER_OUTPUT verwenden.

Der Code im folgenden Beispiel zeigt eine Abfrage der Ereignistabelle nach Profiler-Ergebnissen an. Die als Argument angegebene query_id ist die Abfrage-ID derUDF-Abfrage, für die das Profiling aktiviert wurde.

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

Profiling-Ergebnisse

Wenn Sie die Profiler-Ergebnisse anzeigen, sehen Sie einen Bericht, der sich je nachdem, ob Sie das Profiling für einen Zeilenbericht oder einen Speicherbericht angegeben haben, unterscheidet.

Die Ausgabe des Speicher-Profilers sieht wie folgt aus:

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

Die Ausgabe des Zeilen-Profilers sieht wie folgt aus:

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