Snowpark Pythonユーザー定義関数ハンドラーのプロファイリング

組み込みのコード・プロファイラーを使用することで、ハンドラーコードの実行に費やされた時間やメモリを調べることができます。プロファイラーは、ハンドラーの各行を実行するのに費やされた時間やメモリの情報を生成します。

プロファイラーを使用すると、以下のいずれかに焦点を当てたレポートを一度に作成できます。

  • 1行あたりの実行時間:1行の実行回数、実行にかかった時間などが表示されます。

  • 1行あたりのメモリ使用量:各行でのメモリ使用量が表示されます。

プロファイラーは生成されたレポートを内部の:doc:`イベントテーブル</developer-guide/logging-tracing/event-table-columns>`に保存します。テーブルにアクセスするための専用の関数を使用して、結果を取得できます。

注釈

プロファイリングはPythonの実行にパフォーマンスオーバーヘッドをもたらし、クエリのパフォーマンスに影響を与える可能性があります。これは開発およびテスト用であり、継続的な本番ワークロードでは有効にしないでください。

必要な権限

:code:`SNOWFLAKE.LOCAL.PROFILER_EVENTS_RAW`イベントテーブルに格納されているプロファイラー結果データを管理および使用するには、次のロールを使用する必要があります。

アプリケーションロール

メモ

PROFILER_EVENTS_ADMIN

プロファイラーデータが格納されているイベントテーブルのデータを管理(レコードの選択、切り捨て、ドロップなど)するために必要です。

PROFILER_USER

イベントテーブルからプロファイラーの結果を読み込むために必要です。

アプリケーションロールの付与の詳細については、:doc:`/sql-reference/sql/grant-application-role`をご参照ください。次の例では、:code:`ACCOUNTADMIN`ロールを使用して、ユーザーに:code:`PROFILER_USER`アプリケーションロールを付与しています。

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;

制限事項

  • クエリ実行後、プロファイラーからの結果が準備されるまでに15~20秒かかる場合があります。

  • UDFの実行が失敗した場合、プロファイラー出力は保存されません。

  • 再帰プロファイリングは非対応です。指定したモジュールのトップレベル関数のみがプロファイルされます。関数の内部で定義された関数はプロファイリングされません。

  • サードパーティモジュールのプロファイリングはサポートされていません。

  • snowflake.snowpark APIを介してクライアント側で作成されたUDFsのプロファイリングはサポートされていません。

  • joblib を通して並列実行される Python 関数はプロファイルされません。

  • UDTFs はサポートされていません。

  • 時間はCPU時間ではなく、実時間で測定されます。

使用状況

プロファイラーをセットアップしたら、UDFを実行するだけでプロファイラー出力を生成できます。UDFの実行終了後、プロファイラーの出力は内部イベントテーブルに書き込まれます。システム関数を使用して:ref:`プロファイル出力を取得<label-snowpark_python_udf_profiler_view_output>`できます。

プロファイラーをセットアップして使用するには、コード内で以下の手順に従ってください。

  1. プロファイラーを有効にし、プロファイルレポートの対象をセットします。

  2. UDFを実行します。

  3. プロファイル出力を表示します。

フォーカスを指定してプロファイルを有効にします。

プロファイラーを有効にするには、以下のセッションパラメーターのいずれかを設定します。

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

注釈

プロファイリングはPythonの実行にパフォーマンス上のオーバーヘッドをもたらします。コードのプロファイリングは開発中やテスト中に行うようにしてください。継続的な本番ワークロードでは、プロファイリングを有効にしないでください。

プロファイリングするコードの指定

デフォルトでは、プロファイラーはUDF宣言内にインラインで定義されたメソッドをプロファイリングします。言い換えると、プロファイラーはハンドラーで定義されたすべてのメソッドをプロファイリングします。

次のUDFの例では、プロファイラーは:code:`handler`メソッドと:code:`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():
...
$$;

プロファイリングする外部コードの指定

プロファイラーが、ステージからインポートされたコードなど、UDF宣言の外で定義されたハンドラーコードをプロファイリングするように指定することができます。

プロファイリング対象として外部コードを指定するには、PYTHON_UDF_PROFILER_MODULESセッションパラメーターの値を、そのコードを含むモジュールのコンマ区切りリストに設定します。

ALTER SESSION SET PYTHON_UDF_PROFILER_MODULES = 'test_python_import_main, test_python_import_module';

指定されたモジュールをインポートしているUDFを実行すると、プロファイラはそのモジュールをプロファイリング出力に含めます。

次の例のコードは、指定されたモジュールからコードをインポートするUDFの構成を示しています。

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

ユーザー定義関数の実行

プロファイラーを有効にしたら、ユーザー定義関数(UDF)を実行してプロファイリングを開始します。

デフォルトでは、プロファイラーはモジュールで定義されているメソッドをプロファイリングします。インポートされたファイルから他のモジュールをプロファイルに登録する方法の詳細については、:ref:`label-snowpark_python_udf_profiler_additional`をご参照ください。

SELECT return_mean(my_col) FROM MY_TABLE;

プロファイル出力表示

  • プロファイリングの出力を表示するには、内部:doc:`イベントテーブル</developer-guide/logging-tracing/event-table-columns>`に対してクエリを実行します。

プロファイリング結果は通常、UDF実行の終了後15~20秒後にイベントテーブルで利用可能になります。テーブルシステム関数GET_PYTHON_UDF_PROFILER_OUTPUTを使用して、この出力にアクセスできます。

次の例のコードは、プロファイラーの結果を取得するためにイベントテーブルをクエリする方法を示しています。引数として指定されている:code:`query_id`は、プロファイリングを有効にして実行したUDFクエリのクエリIDです。

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

プロファイリング結果

プロファイラーの結果を確認する際、プロファイリングの対象として行レポートとメモリレポートのどちらを指定したかに応じて、異なるレポートが表示されます。

メモリプロファイラーの出力は次のようになります。

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

ライン・プロファイラーの出力は次のようになります。

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