Criação de perfil do código do manipulador de procedimento Python

Você pode descobrir quanto tempo ou memória foi gasto na execução do código do manipulador usando o criador de perfil do código integrado. O criador de perfil gera informações que descrevem quanto tempo ou memória foi gasto na execução de cada linha do manipulador de procedimento.

Usando o criador de perfil, você pode gerar relatórios que se concentram em um dos seguintes itens de cada vez:

  • Quantidade de tempo por linha, no qual o relatório mostra o número de vezes que uma linha foi executada, quanto tempo levou a execução e assim por diante.

  • Quantidade de uso de memória por linha, no qual o relatório mostra a quantidade de memória consumida por linha.

O criador de perfil salva o relatório gerado em um estágio de usuário interno do Snowflake que você especificar. Você pode ler a saída do criador de perfil usando a função de sistema GET_PYTHON_PROFILER_OUTPUT (SNOWFLAKE.CORE).

Nota

A criação de perfil introduz uma sobrecarga de desempenho na execução do Python e pode afetar o desempenho da consulta. Ele se destina ao desenvolvimento, testes e solução de problemas e não deve ser ativado em cargas de trabalho de produção contínua.

Privilégios obrigatórios

A configuração do parâmetro de nível de sessão não aciona a verificação de privilégios, mas quando um procedimento armazenado é executado com o parâmetro de sessão ACTIVE_PYTHON_PROFILER como LINE ou MEMORY, o Snowflake verifica os seguintes privilégios.

Limitações

  • Somente procedimentos armazenados são suportados. O suporte para UDFs ainda não está disponível.

  • Não há suporte para criação de perfil recursiva. Somente as funções de nível superior dos módulos especificados são analisadas. As funções definidas dentro de funções não são.

  • Não há suporte para a criação de perfis de procedimentos armazenados criados no lado do cliente por meio da snowflake.snowpark API (por exemplo, procedimentos armazenados criados em Session.sproc.register).

  • As funções Python executadas em paralelo por meio do joblib não serão analisadas.

  • Os procedimentos armazenados definidos pelo sistema não podem ser analisados. Eles não produzirão nenhum resultado.

Uso

Depois de configurar o criador de perfil para uso, você pode usá-lo simplesmente chamando o procedimento armazenado para gerar a saída do criador de perfil. Depois que o procedimento termina de ser executado, a saída do criador de perfil é gravada em um arquivo no estágio que você especificar. Você pode obter a saída do criador de perfil usando uma função do sistema.

Siga estas etapas para configurar e usar o criador de perfil:

  1. Especifique o estágio do Snowflake onde a saída do perfil deve ser gravada.

    Defina o parâmetro PYTHON_PROFILER_TARGET_STAGE como o nome totalmente qualificado do estágio.

  2. Ative o criador de perfil e especifique em que o perfil deve se concentrar.

    Defina o parâmetro de sessão ACTIVE_PYTHON_PROFILER.

  3. Chame o procedimento armazenado.

    Depois que o criador de perfil estiver ativado, chame seu procedimento armazenado.

  4. Visualize a saída de criação de perfil.

    No fim da execução, o resultado da criação de perfil será carregado como um arquivo no estágio de saída com o padrão de nomenclatura <query_id>_<sproc_name>.lprof ou <query_id>_<sproc_name>.mprof.

Especifique o estágio do Snowflake em que a saída do perfil deve ser gravada

Antes de executar o criador de perfil, você deve especificar um estágio no qual o relatório será salvo. Para especificar o estágio, defina o parâmetro PYTHON_PROFILER_TARGET_STAGE como o nome totalmente qualificado do estágio.

  • Use um estágio temporário para armazenar a saída somente durante a sessão.

  • Use um estágio permanente para preservar a saída do criador de perfil fora do escopo de uma sessão.

O código no exemplo a seguir cria um estágio temporário de profiler_output para receber a saída do criador de perfil.

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";
Copy

Como ativar o criador de perfil e especificar em que o perfil deve se concentrar

Defina o parâmetro de sessão ACTIVE_PYTHON_PROFILER com um valor que especifique o tipo de relatório de perfil que você deseja gerar.

  • Para que o perfil se concentre na atividade de uso da linha, defina o parâmetro como o valor LINE (não diferencia maiúsculas de minúsculas), conforme mostrado abaixo:

    ALTER SESSION SET ACTIVE_PYTHON_PROFILER = 'LINE';
    
    Copy
  • Para que o perfil se concentre na atividade de uso de memória, defina o parâmetro como o valor MEMORY (não diferencia maiúsculas de minúsculas), conforme mostrado abaixo:

    ALTER SESSION SET ACTIVE_PYTHON_PROFILER = 'MEMORY';
    
    Copy

Como chamar o procedimento armazenado

Depois que o criador de perfil estiver ativado, chame seu procedimento armazenado.

CALL YOUR_STORED_PROCEDURE();
Copy

Por padrão, o criador de perfil traçará o perfil dos métodos definidos no módulo do usuário. Você também pode registrar outros módulos no perfil. Para obter mais informações, consulte Módulos adicionais de perfil.

Como exibir a saída de criação de perfil

No fim da execução, o resultado da criação de perfil será carregado como um arquivo no estágio de saída com o padrão de nomenclatura <query_id>_<sproc_name>.lprof ou <query_id>_<sproc_name>.mprof.

A saída pode ser acessada por meio de uma função do sistema GET_PYTHON_PROFILER_OUTPUT no banco de dados SNOWFLAKE.

O formato da assinatura da função do sistema é o seguinte:

SELECT SNOWFLAKE.CORE.GET_PYTHON_PROFILER_OUTPUT(<query_id>);
Copy

Substitua <query_id> pelo ID da consulta do procedimento armazenado para a qual a criação de perfil foi ativada.

Você também pode acessar diretamente o arquivo de saída no estágio de saída. Para obter mais informações, consulte Exibição de arquivos preparados.

Nota

A função do sistema procura arquivos de saída de criação de perfil do estágio especificado com o parâmetro PYTHON_PROFILER_TARGET_STAGE.

A saída de criação de perfil para procedimentos armazenados filhos não é anexada à saída do procedimento pai. Para visualizar a saída de um procedimento armazenado filho, chame explicitamente a função do sistema no ID de consulta do procedimento filho.

Inclusão de módulos adicionais para criação de perfis

Você pode incluir módulos de criação de perfil que não são incluídos por padrão. Para incluir módulos adicionais para criação de perfil, defina o parâmetro PYTHON_PROFILER_MODULES com os nomes dos módulos que você deseja incluir.

Por padrão, os métodos definidos no seu módulo serão analisados. Esses métodos incluem os seguintes:

  • O método do manipulador

  • Métodos definidos no módulo

  • Métodos importados de pacotes ou outros módulos.

No exemplo abaixo, handler, helper e some_method terão todos criação de perfil por padrão.

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

Inclusão de módulos com o parâmetro PYTHON_PROFILER_MODULES

Você pode usar o parâmetro PYTHON_PROFILER_MODULES para incluir módulos de criação de perfil que não seriam incluídos por padrão. Quando você inclui um módulo dessa forma, todas as funções usadas a partir desse módulo serão incluídas na saída do criador de perfil. Por padrão, o valor do parâmetro PYTHON_PROFILER_MODULES é uma cadeia de caracteres vazia (''), na qual o perfil traçaria o perfil apenas do código do manipulador em linha, se houver.

Para incluir módulos para criação de perfil, especifique seus nomes como o valor do parâmetro em uma lista separada por vírgulas, conforme ilustrado abaixo.

ALTER SESSION SET PYTHON_PROFILER_MODULES = 'module_a, my_module';
Copy

Criação de perfil do código do manipulador em estágios

Para traçar o perfil do código do manipulador que é preparado em vez de inline, inclusive funções auxiliares, você deve especificar explicitamente o manipulador preparado para a criação de perfil usando o parâmetro PYTHON_PROFILER_MODULES.

Por padrão, o criador de perfil não traça o perfil do código do manipulador que é em estágio, em vez de inline — ou seja, quando o módulo do manipulador é especificado com a cláusula IMPORTS.

Por exemplo, por padrão, esse procedimento não gerará nenhuma saída detalhada de criação de perfil.

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

Para incluir o código em estágios para a criação de perfil, especifique os nomes dos módulos em estágios como o valor do parâmetro PYTHON_PROFILER_MODULES em uma lista separada por vírgulas, conforme ilustrado abaixo.

ALTER SESSION SET PYTHON_PROFILER_MODULES = 'test_python_import_main, test_python_import_module';
Copy

Exemplo

O código neste exemplo ilustra como usar o criador de perfil para gerar e recuperar um relatório de uso de linha.

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());
Copy

A saída do criador de perfil de linha terá a seguinte aparência:

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

A saída do criador de perfil de memória terá a seguinte aparência:

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