Snowpark Python 사용자 정의 함수 핸들러 프로파일링하기

기본 제공 코드 프로파일러를 사용하여 처리기 코드를 실행하는 데 얼마나 많은 시간이나 메모리가 사용되었는지 확인할 수 있습니다. 프로파일러는 핸들러의 각 라인을 실행하는 데 소요된 시간 또는 메모리를 설명하는 정보를 생성합니다.

프로파일러를 사용하면 다음 중 하나에 초점을 맞춘 보고서를 한 번에 생성할 수 있습니다.

  • 라인당 시간: 보고서에 라인이 실행된 횟수, 실행에 걸린 시간 등이 표시됩니다.

  • 라인당 메모리 사용량: 보고서에 라인당 사용된 메모리의 양이 표시됩니다.

프로파일러는 생성된 보고서를 내부 :doc:`이벤트 테이블 </developer-guide/logging-tracing/event-table-columns>`에 저장합니다. 테이블에 액세스하도록 설계된 함수를 사용하여 결과를 검색할 수 있습니다.

참고

프로파일링은 Python 실행에 성능 오버헤드를 유발하며 쿼리 성능에 영향을 미칠 수 있습니다. 이 기능은 개발 및 테스트를 위한 것으로 지속적인 프로덕션 워크로드에서는 활성화해서는 안 됩니다.

필수 권한

SNOWFLAKE.LOCAL.PROFILER_EVENTS_RAW 이벤트 테이블에 저장된 프로파일러 결과 데이터를 관리하고 사용하려면 다음 역할을 사용해야 합니다.

애플리케이션 역할

참고

PROFILER_EVENTS_ADMIN

레코드 선택, 자르기, 삭제를 포함하여 프로파일러 데이터가 저장된 이벤트 테이블의 데이터를 관리하는 데 필요합니다.

PROFILER_USER

이벤트 테이블에서 프로파일러 결과를 읽는 데 필요합니다.

애플리케이션 역할 부여에 대한 자세한 내용은 GRANT APPLICATION ROLE 섹션을 참조하세요. 다음 예제에서는 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가 실행을 마치면 프로파일러의 출력이 내부 이벤트 테이블에 기록됩니다. 시스템 함수를 사용하여 프로파일러 출력을 가져올 수 있습니다.

프로파일러를 설정하고 사용하려면 코드에서 다음 단계를 따르십시오.

  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 예제의 경우, 프로파일러는 handler 메서드 및 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)를 실행하여 프로파일링을 시작합니다.

기본적으로 프로파일러는 해당 모듈에 정의된 메서드를 프로파일링합니다. 가져온 파일의 다른 모듈을 프로필에 등록하는 방법에 대한 자세한 내용은 프로파일링할 코드 지정하기 섹션을 참조하세요.

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