Profiling von Python-Handler-Code¶
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 des Prozedur-Handlers aufgewendet wurde.
Mit dem Profiler können Sie Berichte erstellen, die sich jeweils auf einen der folgenden Punkte konzentrieren:
Zeitaufwand pro Zeile – hier zeigt der Bericht die Anzahl der Ausführungen einer Zeile, die Ausführungsdauer usw. an.
Speicherverbrauch pro Zeile – hier zeigt der Bericht den verbrauchten Speicher pro Zeile an.
Der Profiler speichert den erstellten Bericht in einem von Ihnen festgelegten internen Benutzer-Stagingbereich in Snowflake. Sie können die Profiler-Ausgabe mit der Systemfunktion GET_PYTHON_PROFILER_OUTPUT (SNOWFLAKE.CORE) lesen.
Bemerkung
Profiling führt zu einem Leistungsaufwand bei der Python-Ausführung und kann die Leistung der Abfrage beeinträchtigen. Es ist für die Entwicklung, das Testen und die Problembehandlung vorgesehen und sollte nicht für kontinuierliche Produktions-Workloads aktiviert werden.
Erforderliche Berechtigungen¶
Die Einstellung des Parameters auf Sitzungsebene löst keine Berechtigungsprüfung aus, aber wenn eine gespeicherte Prozedur mit dem Sitzungsparameter ACTIVE_PYTHON_PROFILER entweder auf LINE oder MEMORY ausgeführt wird, überprüft Snowflake die folgenden Berechtigungen.
Sie müssen über Lese-/Schreibberechtigungen für den Stagingbereich verfügen.
Wenn es sich bei der profilierten gespeicherten Prozedur um eine gespeicherte Prozedur mit den Aufruferrechten handelt, müssen Sie eine Rolle mit der Berechtigung USAGE für die gespeicherte Prozedur verwenden.
Wenn es sich bei der profilierten gespeicherten Prozedur um eine gespeicherte Prozedur mit Eigentümerreichten handelt, müssen Sie eine Rolle mit der Berechtigung OWNERSHIP für die gespeicherte Prozedur verwenden.
Einschränkungen¶
Es werden nur gespeicherte Prozeduren unterstützt. UDFs-Unterstützung ist noch nicht verfügbar.
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.
Profiling von gespeicherten Prozeduren, die auf der Client-Seite über
snowflake.snowpark
API erstellt wurden, wird nicht unterstützt (z. B. gespeicherte Prozeduren, die vonSession.sproc.register
erstellt wurden).Python-Funktionen, die parallel über
joblib
laufen, werden nicht profiliert.Systemdefinierte gespeicherte Prozeduren können nicht profiliert werden. Sie produzieren keine Ausgabe.
Verwendung¶
Sobald Sie den Profiler für die Verwendung eingerichtet haben, können Sie ihn verwenden, indem Sie einfach die gespeicherte Prozedur aufrufen, um die Profiler-Ausgabe zu generieren. Nachdem die Prozedur beendet ist, wird die Profiler-Ausgabe in eine Datei im Stagingbereich geschrieben, den Sie angeben. Sie können die Profiler-Ausgabe mit einer Systemfunktion abrufen.
Folgen Sie diesen Schritten, um den Profiler einzurichten und zu verwenden:
Geben Sie den Snowflake Stagingbereich an, in den die Profilausgabe geschrieben werden soll.
Setzen Sie den Parameter PYTHON_PROFILER_TARGET_STAGE auf den vollqualifizierten Namen des Stagingbereichs.
Aktivieren Sie den Profiler und geben Sie an, worauf sich das Profil konzentrieren soll.
Legen Sie den Sitzungsparameter ACTIVE_PYTHON_PROFILER fest.
Rufen Sie die gespeicherte Prozedur auf.
Nachdem der Profiler aktiviert ist, rufen Sie Ihre gespeicherte Prozedur auf.
-
Am Ende der Ausführung wird die Profiling-Ausgabe als Datei in den Stagingbereich hochgeladen, mit dem Namensmuster
<query_id>_<sproc_name>.lprof
oder<query_id>_<sproc_name>.mprof
.
Geben Sie den Snowflake-Stagingbereich an, in den die Profilausgabe geschrieben werden soll¶
Bevor Sie den Profiler starten, müssen Sie einen Stagingbereich angeben, in dem der Bericht gespeichert werden soll. Um den Stagingbereich anzugeben, setzen Sie den Parameter PYTHON_PROFILER_TARGET_STAGE auf den vollqualifizierten Namen des Stagingbereichs.
Verwenden Sie einen temporären Stagingbereich, um die Ausgabe nur für die Dauer der Sitzung zu speichern.
Verwenden Sie einen permanenten Stagingbereich, um die Profiler-Ausgabe außerhalb einer Sitzung aufzubewahren.
Der Code im folgenden Beispiel erstellt einen temporären Stagingbereich profiler_output
, der die Profiler-Ausgabe empfängt.
USE DATABASE my_database;
USE SCHEMA my_schema;
CREATE TEMPORARY STAGE profiler_output;
ALTER SESSION SET PYTHON_PROFILER_TARGET_STAGE = "my_database.my_schema.profiler_output";
Aktivieren Sie den Profiler und legen Sie fest, worauf sich das Profil konzentrieren soll.¶
Setzen Sie den Sitzungsparameter ACTIVE_PYTHON_PROFILER auf einen Wert, der angibt, welche Art von Profilbericht Sie erstellen möchten.
Wenn Sie möchten, dass sich das Profil auf die Aktivität der Leitungsnutzung konzentriert, setzen Sie den Parameter auf den Wert
LINE
(unterscheidet nicht zwischen Groß-/Kleinschreibung), wie unten gezeigt:ALTER SESSION SET ACTIVE_PYTHON_PROFILER = 'LINE';
Wenn Sie möchten, dass sich das Profil auf die Aktivität der Speichernutzung konzentriert, setzen Sie den Parameter auf den Wert
MEMORY
(unterscheidet nicht zwischen Groß-/Kleinschreibung), wie unten gezeigt:ALTER SESSION SET ACTIVE_PYTHON_PROFILER = 'MEMORY';
Rufen Sie die gespeicherte Prozedur auf:¶
Nachdem der Profiler aktiviert ist, rufen Sie Ihre gespeicherte Prozedur auf.
CALL YOUR_STORED_PROCEDURE();
Standardmäßig erstellt der Profiler ein Profil der Methoden, die im Modul des Benutzers definiert sind. Sie können auch andere Module im Profil registrieren. Weitere Informationen finden Sie unter Profil Zusätzliche Module.
Profiling-Ausgabe ansehen¶
Am Ende der Ausführung wird die Profiling-Ausgabe als Datei in den Stagingbereich hochgeladen, mit dem Namensmuster <query_id>_<sproc_name>.lprof
oder <query_id>_<sproc_name>.mprof
.
Die Ausgabe kann über eine Systemfunktion GET_PYTHON_PROFILER_OUTPUT in der Datenbank SNOWFLAKE abgerufen werden.
Das Format der Signatur der Systemfunktion ist wie folgt:
SELECT SNOWFLAKE.CORE.GET_PYTHON_PROFILER_OUTPUT(<query_id>);
Ersetzen Sie <query_id>
durch die Abfrage-ID der Abfrage der gespeicherten Prozedur, für die Profiling aktiviert wurde.
Sie können auch direkt auf die Ausgabedatei im Stagingbereich zugreifen. Weitere Informationen finden Sie unter Anzeigen von Stagingdateien.
Bemerkung
Die Systemfunktion sucht nach Profiling-Ausgabedateien aus dem mit dem Parameter PYTHON_PROFILER_TARGET_STAGE angegebenen Stagingbereich.
Die Profiling-Ausgabe für untergeordnete gespeicherte Prozeduren wird nicht an die Ausgabe der übergeordneten Prozedur angehängt. Um die Ausgabe für eine untergeordnete gespeicherte Prozedur anzuzeigen, rufen Sie die Systemfunktion für die Abfrage-ID der untergeordneten Prozedur explizit auf.
Einschließlich zusätzlicher Module für die Profilerstellung¶
Sie können für die Profilerstellung Module einschließen, die nicht standardmäßig eingeschlossen sind. Um zusätzliche Module für die Profilerstellung einzubeziehen, setzen Sie den Parameter PYTHON_PROFILER_MODULES auf die Namen der Module, die Sie einbeziehen möchten.
Standardmäßig werden die in Ihrem Modul definierten Methoden profiliert. Dazu gehören die folgenden Methoden:
Der Methoden-Handler
Im Modul definierte Methoden
Aus Paketen oder anderen Modulen importierte Methoden
Im folgenden Beispiel werden die Profile handler
, helper
und some_method
standardmäßig erstellt.
CREATE OR REPLACE PROCEDURE my_sproc()
RETURNS VARIANT
LANGUAGE PYTHON
RUNTIME_VERSION = 3.10
PACKAGES = ('snowflake-snowpark-python', 'other_package')
HANDLER='handler'
AS $$
from other_package import some_method
def helper():
...
def handler(session):
...
$$;
Einbindung von Modulen mit dem Parameter PYTHON_PROFILER_MODULES¶
Sie können den Parameter PYTHON_PROFILER_MODULES verwenden, um Module für die Profilerstellung einzubeziehen, die standardmäßig nicht einbezogen werden. Wenn Sie ein Modul auf diese Weise einbinden, werden alle Funktionen, die von diesem Modul verwendet werden, in die Profiler-Ausgabe aufgenommen. Standardmäßig ist der Wert des Parameters PYTHON_PROFILER_MODULES eine leere Zeichenfolge (''
). In diesem Fall würde das Profil nur Inline-Handler-Code profilieren, falls vorhanden.
Um Module für das Profiling einzubeziehen, geben Sie deren Namen als Wert des Parameters in einer durch Kommas getrennten Liste an, wie unten dargestellt.
ALTER SESSION SET PYTHON_PROFILER_MODULES = 'module_a, my_module';
Profiling von Stagingbereich-Handler-Code¶
Um Handler-Code zu profilieren, der nicht inline, sondern im Stagingbereich liegt – einschließlich Hilfsfunktionen –, müssen Sie den Stagingbereich für das Profiling explizit mit dem Parameter PYTHON_PROFILER_MODULES angeben.
Standardmäßig erstellt der Profiler kein Profil für Handler-Code, der im Stagingbereich und nicht im Inlinebereich liegt –d. h. wenn das Handler-Modul mit der IMPORTS-Klausel angegeben ist.
Standardmäßig erzeugt diese Prozedur zum Beispiel keine detaillierten Profiling-Ausgaben.
CREATE OR REPLACE PROCEDURE test_udf_1()
RETURNS STRING
LANGUAGE PYTHON
RUNTIME_VERSION = '3.8'
PACKAGES=('snowflake-snowpark-python')
HANDLER = 'test_python_import_main.my_udf'
IMPORTS = ('@stage1/test_python_import_main.py', '@stage2/test_python_import_module.py');
Um Stagingcode für das Profiling einzubeziehen, geben Sie die Namen der Stagingmodule als Wert des Parameters PYTHON_PROFILER_MODULES in einer durch Komma getrennten Liste an, wie unten dargestellt.
ALTER SESSION SET PYTHON_PROFILER_MODULES = 'test_python_import_main, test_python_import_module';
Beispiel¶
Der Code in diesem Beispiel veranschaulicht, wie Sie den Profiler verwenden, um einen Bericht über die Zeilennutzung zu erstellen und abzurufen.
CREATE OR REPLACE PROCEDURE last_n_query_duration(last_n number, total number)
RETURNS string
LANGUAGE PYTHON
RUNTIME_VERSION=3.8
PACKAGES=('snowflake-snowpark-python')
HANDLER='main'
AS
$$
import snowflake.snowpark.functions as funcs
def main(session, last_n, total):
# create sample dataset to emulate id + elapsed time
session.sql('''
CREATE OR REPLACE TABLE sample_query_history (query_id INT, elapsed_time FLOAT)
''').collect()
session.sql('''
INSERT INTO sample_query_history
SELECT
seq8() AS query_id,
uniform(0::float, 100::float, random()) as elapsed_time
FROM table(generator(rowCount => {0}));'''.format(total)).collect()
# get the mean of the last n query elapsed time
df = session.table('sample_query_history').select(
funcs.col('query_id'),
funcs.col('elapsed_time')).limit(last_n)
pandas_df = df.to_pandas()
mean_time = pandas_df.loc[:, 'ELAPSED_TIME'].mean()
del pandas_df
return mean_time
$$;
CREATE TEMPORARY STAGE profiler_output;
ALTER SESSION SET PYTHON_PROFILER_TARGET_STAGE = "my_database.my_schema.profiler_output";
ALTER SESSION SET ACTIVE_PYTHON_PROFILER = 'LINE';
-- Sample 1 million from 10 million records
CALL last_n_query_duration(1000000, 10000000);
SELECT SNOWFLAKE.CORE.GET_PYTHON_PROFILER_OUTPUT(last_query_id());
Die Ausgabe des Zeilen-Profilers sieht wie folgt aus:
Handler Name: main
Python Runtime Version: 3.8
Modules Profiled: ['main_module']
Timer Unit: 0.001 s
Total Time: 8.96127 s
File: _udf_code.py
Function: main at line 4
Line # Hits Time Per Hit % Time Line Contents
==============================================================
4 def main(session, last_n, total):
5 # create sample dataset to emulate id + elapsed time
6 1 122.3 122.3 1.4 session.sql('''
7 CREATE OR REPLACE TABLE sample_query_history (query_id INT, elapsed_time FLOAT)''').collect()
8 2 7248.4 3624.2 80.9 session.sql('''
9 INSERT INTO sample_query_history
10 SELECT
11 seq8() AS query_id,
12 uniform(0::float, 100::float, random()) as elapsed_time
13 1 0.0 0.0 0.0 FROM table(generator(rowCount => {0}));'''.format(total)).collect()
14
15 # get the mean of the last n query elapsed time
16 3 58.6 19.5 0.7 df = session.table('sample_query_history').select(
17 1 0.0 0.0 0.0 funcs.col('query_id'),
18 2 0.0 0.0 0.0 funcs.col('elapsed_time')).limit(last_n)
19
20 1 1528.4 1528.4 17.1 pandas_df = df.to_pandas()
21 1 3.2 3.2 0.0 mean_time = pandas_df.loc[:, 'ELAPSED_TIME'].mean()
22 1 0.3 0.3 0.0 del pandas_df
23 1 0.0 0.0 0.0 return mean_time
Die Ausgabe des Speicher-Profilers sieht wie folgt aus:
ALTER SESSION SET ACTIVE_PYTHON_PROFILER = 'MEMORY';
Handler Name: main
Python Runtime Version: 3.8
Modules Profiled: ['main_module']
File: _udf_code.py
Function: main at line 4
Line # Mem usage Increment Occurrences Line Contents
=============================================================
4 245.3 MiB 245.3 MiB 1 def main(session, last_n, total):
5 # create sample dataset to emulate id + elapsed time
6 245.8 MiB 0.5 MiB 1 session.sql('''
7 CREATE OR REPLACE TABLE sample_query_history (query_id INT, elapsed_time FLOAT)''').collect()
8 245.8 MiB 0.0 MiB 2 session.sql('''
9 INSERT INTO sample_query_history
10 SELECT
11 seq8() AS query_id,
12 uniform(0::float, 100::float, random()) as elapsed_time
13 245.8 MiB 0.0 MiB 1 FROM table(generator(rowCount => {0}));'''.format(total)).collect()
14
15 # get the mean of the last n query elapsed time
16 245.8 MiB 0.0 MiB 3 df = session.table('sample_query_history').select(
17 245.8 MiB 0.0 MiB 1 funcs.col('query_id'),
18 245.8 MiB 0.0 MiB 2 funcs.col('elapsed_time')).limit(last_n)
19
20 327.9 MiB 82.1 MiB 1 pandas_df = df.to_pandas()
21 328.9 MiB 1.0 MiB 1 mean_time = pandas_df.loc[:, 'ELAPSED_TIME'].mean()
22 320.9 MiB -8.0 MiB 1 del pandas_df
23 320.9 MiB 0.0 MiB 1 return mean_time