Snowpark Pythonストアドプロシージャハンドラーのプロファイリング

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

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

  • 1行あたりの実行時間 には、このレポートでは、1行の実行回数、実行にかかった時間などが表示されます。

  • 1行あたりのメモリ使用量 には、このレポートでは1行あたりのメモリ使用量が表示されます。

プロファイルは、生成されたレポートを指定したSnowflake 内部ユーザーステージ に保存します。 プロファイル出力StoredProcedureProfiler.get_output 関数を使用して読むことができます。

注釈

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

必要な権限

StoredProcedureProfiler.set_active_profiler 関数が呼び出された後にストアドプロシージャが実行されると、Snowflake はプロシージャを実行するユーザーの以下の権限をチェックします:

  • プロファイリング出力ステージの読み取り書き込み権限が必要です。

  • プロファイルされたストアドプロシージャが 発呼者権限ストアドプロシージャ の場合、ストアドプロシージャで USAGE 権限を持つロールを使用する必要があります。

  • プロファイルされたストアドプロシージャが 所有者の権限のストアドプロシージャ の場合、そのストアドプロシージャで OWNERSHIP 権限のロールを使用する必要があります。

制限事項

  • ストアドプロシージャのみがサポートされています。UDFs のサポートはまだ可用性はありません。

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

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

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

  • システム定義のストアドプロシージャはプロファイルできません。出力はゼロです。

  • プロファイリング API は、プロシージャが呼び出されたのと同じスレッドで使用する必要があります。

使用状況

プロファイラーを使用するようにセットアップしたら、ストアドプロシージャをコールしてプロファイラー出力を生成するだけで使用できます。プロシージャの実行が終了すると、プロファイラーの出力は指定したステージのファイルに書き出されます。以下に説明するように、システム関数を使用してプロファイル出力を取得することができます。

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

  1. Session オブジェクトからプロファイルオブジェクトを取得します。

  2. プロファイル出力を書き込むSnowflakeステージを指定します。

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

  4. ストアドプロシージャを呼び出します。

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

プロファイルオブジェクトの取得

Pythonで、プロファイラーの構成と実行を行う StoredProcedureProfiler 型の変数を作成します。

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

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

プロファイル出力を書き込むSnowflakeステージを指定します。

プロファイラーを実行する前に、出力を保存するステージを指定する必要があります。ステージを指定するには、 StoredProcedureProfiler.set_target_stage を呼び出し、レポートが書き込まれるべき内部ステージ Snowflakeステージ の完全修飾名を指定します。

次のことに留意してください。

  • ステージ名は完全修飾名でなければなりません。

  • この関数に入れたステージが存在しない場合、Snowflakeはその名前で仮ステージを作成します。

  • セッションの範囲外でプロファイラーの出力を保持したい場合は、 set_target_stage を実行する前にパーマネントステージを作成し、関数呼び出しでそのパーマネントステージの名前を指定します。

  • set_target_stage でターゲットステージをセットしない場合、Snowflake は現在のセッションの仮ステージをターゲットステージとして設定します。その仮ステージを発見するには、 Session.get_session_stage を呼び出します。

以下の例のコードは、プロファイラー出力を受け取るための仮の profiler_output ステージ を作成します。

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

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

StoredProcedureProfiler.set_active_profiler 関数を使用して、生成したいプロファイル・レポートの種類を示す値を指定します。

  • 回線使用アクティビティをプロファイラーにレポートさせるには、以下のようにパラメーターを LINE の値にセットします(大文字と小文字は区別されません)。

    profiler.set_active_profiler("LINE")
    
    Copy
  • プロファイラーにメモリ使用アクティビティをレポートさせるには、以下のようにパラメーターを MEMORY の値(大文字と小文字は区別されない)にセットします。

    profiler.set_active_profiler("MEMORY")
    
    Copy

プロファイルを無効にするには、 StoredProcedureProfiler.disable 関数を使用します。

ストアドプロシージャを呼び出します。

プロファイラーを有効にした後、 ストアドプロシージャを呼び出します

session.call("my_stored_procedure")
Copy

プロファイル出力表示

実行の最後に、 StoredProcedureProfiler.get_output 関数を使用して出力にアクセスできます。

profiler.get_output()
Copy

プロファイル用追加モジュールの追加

プロファイルを作成するときに、デフォルトでは含まれていないモジュールを含めることができます。

デフォルトでは、モジュールで定義されたメソッドがプロファイルされます。これらの方法には以下のようなものがあります。

  • ハンドラーメソッド

  • モジュールで定義されているメソッド

  • パッケージや他のモジュールからインポートされたメソッド

追加モジュールをプロファイルに含めるには、 StoredProcedureProfiler.register_modules 関数を使用して、含めたいモジュールを指定します。

次の例のコードは、モジュール module_A と module_B をプロファイル用に登録します。

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

登録されたモジュールの登録を解除するには、次の例のように引数なしで register_modules を使用します。

profiler.register_modules()
Copy

以下の例では、プロファイラーを使用して回線使用量のレポートを生成および取得する方法を説明します。

この例のコードは、プロシージャ 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

次の例のコードは、プロファイラーをセットアップし、 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

生成されたライン・プロファイラーの出力は以下のようになります。

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]