Este projeto agora está arquivado e somente leitura
timemory no GitHub (código fonte)
Documentação geral do timemory (ReadTheDocs)
Documentação do código-fonte do timemory (Doxygen)
Painel de teste timemory (CDash)
Tutoriais de memória de tempo
Tutorial ECP 2021, dia 1 (YouTube)
Tutorial ECP 2021, dia 2 (YouTube)
Wiki do tempo
GitHub | git clone https://github.com/NERSC/timemory.git |
PyPi | pip install timemory |
Spack | spack install timemory |
conda-forge | conda install -c conda-forge timemory |
O objetivo do timemory é criar um pacote de medição e análise de desempenho de código aberto com componentes modulares e reutilizáveis que podem ser usados para se adaptar a qualquer API existente de medição e análise de desempenho C/C++ e que seja arbitrariamente extensível pelos usuários dentro de seu aplicativo. Timemory não é apenas mais uma ferramenta de criação de perfil, é um kit de ferramentas de criação de perfil que agiliza a construção de ferramentas de criação de perfil personalizadas por meio da modularidade e, em seguida, utiliza o kit de ferramentas para fornecer várias ferramentas pré-construídas.
Em outras palavras, o timemory fornece muitas ferramentas, bibliotecas e interfaces pré-construídas, mas, devido à sua modularidade, os códigos podem reutilizar apenas peças individuais - como as classes para medir diferentes intervalos de tempo, uso de memória e contadores de hardware - - sem o timemory "gerenciamento de tempo de execução".
Timemory usa uma instalação padrão do CMake. Vários exemplos de instalação podem ser encontrados no Wiki. Consulte a documentação de instalação para obter informações detalhadas sobre as opções do CMake.
A documentação completa está disponível em timemory.readthedocs.io. A documentação fonte detalhada é fornecida na seção doygen da documentação completa. Os tutoriais estão disponíveis em github.com/NERSC/timemory-tutorials.
O objetivo principal do timemory é o desenvolvimento de uma estrutura comum para vincular o código de monitoramento de software (ou seja, análise de desempenho, depuração, registro) em uma interface compacta e altamente eficiente.
O Timemory surgiu da necessidade de um kit adaptador universal para as diversas APIs, fornecendo diversas ferramentas existentes e um método direto e intuitivo para a criação de novas ferramentas. Timemory torna possível agrupar medições de desempenho determinísticas, medições estatísticas de desempenho (ou seja, amostragem), mensagens de depuração, registro de dados e validação de dados na mesma interface para interfaces de monitoramento de software específicas de aplicativos personalizados, criando facilmente ferramentas como time
, netstat
, instrumentação criadores de perfil, criadores de perfil de amostragem e implementações de escrita para MPI-P, MPI-T, OMPT, KokkosP, etc. Além disso, o timemory pode encaminhar seus marcadores para vários criadores de perfil de terceiros, como LIKWID, Caliper, TAU, gperftools, Perfetto, VTune, Allinea-MAP, CrayPAT, Nsight-Systems, Nsight-Compute e NVProf.
Timemory fornece uma API C/C++/Fortran front-end e uma API Python que permite a seleção arbitrária de mais de 50 componentes diferentes, de temporizadores a contadores de hardware e interfaces com ferramentas de terceiros. Tudo isso é construído genericamente a partir da API do kit de ferramentas com pacotes de ferramentas com segurança de tipo, como: component_tuple<wall_clock, papi_vector, nvtx_marker, user_bundle>
onde wall_clock
é um cronômetro de relógio de parede, papi_vector
é um identificador para contadores de hardware, nvxt_marker
cria notações em os criadores de perfil NVIDIA CUDA e user_bundle
é um componente genérico no qual os usuários downstream podem inserir mais componentes em tempo de execução.
Os componentes de medição de desempenho escritos com timemory são arbitrariamente escalonáveis para qualquer número de threads e processos e suportam totalmente a mistura de diferentes medições em diferentes locais do programa - isso permite exclusivamente que o timemory seja implantado para coletar dados de desempenho em escala em HPC porque a coleta altamente detalhada pode ocorrer em locais específicos do programa onde a coleta onipresente degradaria significativamente o desempenho e exigiria uma quantidade proibitiva de memória.
Timemory pode ser usado como back-end para agrupar ferramentas de instrumentação e amostragem, oferecer suporte à serialização para JSON/XML e fornecer estatísticas, entre outros usos. Ele também pode ser utilizado como front-end para invocar instrumentação personalizada e ferramentas de amostragem. Timemory usa o termo abstrato "componente" para uma estrutura que encapsula alguma operação de análise de desempenho. A estrutura pode encapsular chamadas de função para outra ferramenta, registrar carimbos de data e hora para tempo, registrar valores fornecidos pelo aplicativo, fornecer um operador para substituir uma função no código dinamicamente, auditar os argumentos recebidos e/ou o valor de retorno de saída da função ou apenas fornecer stubs que podem ser sobrecarregados pelo vinculador.
O formato de saída nativo do timemory é JSON e texto; outros formatos de saída, como XML, também são suportados. O formato do texto deve ser legível por humanos. Os dados JSON destinam-se à análise e vêm em dois tipos: hierárquicos e planos. Recursos básicos de plotagem estão disponíveis por meio de timemory-plotting
, mas os usuários são altamente incentivados a usar o machado para analisar os dados JSON heirárquicos em dataframes do pandas. Hatchet suporta filtragem, uniões, adição, subtrações, saída para formatos de dot
e flamegraph e um notebook Jupyter interativo. Atualmente, o timemory suporta mais de 45 tipos de métricas para análise no Hatchet.
Existem 4 categorias principais em timemory: componentes, operações, empacotadores e armazenamento. Os componentes fornecem as especificações de como executar um comportamento específico, as operações fornecem a estrutura para solicitar que um componente execute uma operação em cenários complexos, os empacotadores agrupam componentes em um único identificador genérico e o armazenamento gerencia a coleta de dados durante a vida útil do aplicativo. Quando todas as quatro categorias são combinadas, o timemory se assemelha efetivamente a uma ferramenta padrão de análise de desempenho que coleta dados passivamente e fornece relatórios e análises ao final da aplicação. O Timemory, no entanto, facilita muito a subtração do armazenamento da equação e, ao fazê-lo, transforma o timemory em um kit de ferramentas para coleta de dados personalizada.
tim::component::wall_clock
: um simples temporizador de relógio de paredetim::component::vtune_profiler
: um componente simples que ativa e desativa o VTune Profiler (quando o VTune está criando ativamente o perfil do aplicativo)tim::component::data_tracker_integer
: associa valores inteiros a um rótulo conforme o aplicativo é executado (por exemplo, número de iterações de loop usadas em algum lugar)tim::component::papi_vector
: usa a biblioteca PAPI para coletar valores de contadores de hardwaretim::component::user_bundle
: encapsula uma matriz de componentes que o usuário pode manipular dinamicamente durante o tempo de execuçãotim::operation::start<wall_clock>
tentarão chamar a função de membro start()
em uma instância do componente wall_clock
wall_clock
tem uma função store(int)
? store()
?)tim::operation::start
: instrui um componente para iniciar a coletatim::operation::sample
: instrui um componente a fazer medições individuaistim::operation::derive
: dados extras de outros componentes, se estiverem disponíveistim::auto_tuple
tim::component_tuple
tim::component_list
tim::lightweight_tuple
auto_tuple
inicia todos os componentes quando construído e interrompe todos os componentes quando destruídos, enquanto component_tuple
requer um início explícitocomponent_tuple
aloca todos os componentes na pilha e os componentes estão "sempre ligados", enquanto component_list
aloca componentes na pilha e, portanto, os componentes podem ser ativados/desativados em tempo de execuçãolightweight_tuple
não executa implicitamente nenhuma ação cara, como rastreamento de pilha de chamadas em "Armazenamento"NOTA:
tim::lightweight_tuple
é o pacote recomendado para aqueles que procuram usar o timemory como um kit de ferramentas para implementar ferramentas e interfaces personalizadas
timemory-avail
é fornecido como uma classe Python independentetime
UNIX que inclui informações adicionais sobre uso de memória, alternâncias de contexto e contadores de hardwarenvidia-smi
timemory-python-profiler
from timemory.profiler import Profile
timemory-python-trace
from timemory.trace import Trace
timemory-python-line-profiler
from timemory.line_profiler import LineProfiler
Várias macros são definidas para C em source/timemory/compat/timemory_c.h e source/timemory/variadic/macros.hpp. Numerosos exemplos de seu uso podem ser encontrados nos exemplos.
# include " timemory/timemory.hpp "
namespace comp = tim::component;
using namespace tim ;
// specific set of components
using specific_t = component_tuple<comp::wall_clock, comp::cpu_clock>;
using generic_t = component_tuple<comp::user_global_bundle>;
int
main ( int argc, char ** argv)
{
// configure default settings
settings::flat_profile () = true ;
settings::timing_units () = " msec " ;
// initialize with cmd-line
timemory_init (argc, argv);
// add argparse support
timemory_argparse (&argc, &argv);
// create a region "main"
specific_t m{ " main " };
m. start ();
m. stop ();
// pause and resume collection globally
settings::enabled () = false ;
specific_t h{ " hidden " };
h. start (). stop ();
settings::enabled () = true ;
// Add peak_rss component to specific_t
mpl:: push_back_t < specific_t , comp::peak_rss> wprss{ " with peak_rss " };
// create region collecting only peak_rss
component_tuple<comp::peak_rss> oprss{ " only peak_rss " };
// convert component_tuple to a type that starts/stops upon construction/destruction
{
scope::config _scope{};
if ( true ) _scope += scope::flat{};
if ( false ) _scope += scope::timeline{};
convert_t < specific_t , auto_tuple<>> scoped{ " scoped start/stop + flat " , _scope };
// will yield auto_tuple<comp::wall_clock, comp::cpu_clock>
}
// configure the generic bundle via set of strings
runtime::configure<comp::user_global_bundle>({ " wall_clock " , " peak_rss " });
// configure the generic bundle via set of enumeration ids
runtime::configure<comp::user_global_bundle>({ TIMEMORY_WALL_CLOCK, TIMEMORY_CPU_CLOCK });
// configure the generic bundle via component instances
comp::user_global_bundle::configure<comp::page_rss, comp::papi_vector>();
generic_t g{ " generic " , quirk::config<quirk::auto_start>{} };
g. stop ();
// Output the results
timemory_finalize ();
return 0 ;
}
# include " timemory/library.h "
# include " timemory/timemory.h "
int
main ( int argc, char ** argv)
{
// configure settings
int overwrite = 0 ;
int update_settings = 1 ;
// default to flat-profile
timemory_set_environ ( " TIMEMORY_FLAT_PROFILE " , " ON " , overwrite, update_settings);
// force timing units
overwrite = 1 ;
timemory_set_environ ( " TIMEMORY_TIMING_UNITS " , " msec " , overwrite, update_settings);
// initialize with cmd-line
timemory_init_library (argc, argv);
// check if inited, init with name
if (! timemory_library_is_initialized ())
timemory_named_init_library ( " ex-c " );
// define the default set of components
timemory_set_default ( " wall_clock, cpu_clock " );
// create a region "main"
timemory_push_region ( " main " );
timemory_pop_region ( " main " );
// pause and resume collection globally
timemory_pause ();
timemory_push_region ( " hidden " );
timemory_pop_region ( " hidden " );
timemory_resume ();
// Add/remove component(s) to the current set of components
timemory_add_components ( " peak_rss " );
timemory_remove_components ( " peak_rss " );
// get an identifier for a region and end it
uint64_t idx = timemory_get_begin_record ( " indexed " );
timemory_end_record (idx);
// assign an existing identifier for a region
timemory_begin_record ( " indexed/2 " , &idx);
timemory_end_record (idx);
// create region collecting a specific set of data
timemory_begin_record_enum ( " enum " , &idx, TIMEMORY_PEAK_RSS, TIMEMORY_COMPONENTS_END);
timemory_end_record (idx);
timemory_begin_record_types ( " types " , &idx, " peak_rss " );
timemory_end_record (idx);
// replace current set of components and then restore previous set
timemory_push_components ( " page_rss " );
timemory_pop_components ();
timemory_push_components_enum ( 2 , TIMEMORY_WALL_CLOCK, TIMEMORY_CPU_CLOCK);
timemory_pop_components ();
// Output the results
timemory_finalize_library ();
return 0 ;
}
program fortran_example
use timemory
use iso_c_binding, only : C_INT64_T
implicit none
integer (C_INT64_T) :: idx
! initialize with explicit name
call timemory_init_library( " ex-fortran " )
! initialize with name extracted from get_command_argument( 0 , ...)
! call timemory_init_library( " " )
! define the default set of components
call timemory_set_default( " wall_clock, cpu_clock " )
! Start region " main "
call timemory_push_region( " main " )
! Add peak_rss to the current set of components
call timemory_add_components( " peak_rss " )
! Nested region " inner " nested under " main "
call timemory_push_region( " inner " )
! End the " inner " region
call timemory_pop_region( " inner " )
! remove peak_rss
call timemory_remove_components( " peak_rss " )
! begin a region and get an identifier
idx = timemory_get_begin_record( " indexed " )
! replace current set of components
call timemory_push_components( " page_rss " )
! Nested region " inner " with only page_rss components
call timemory_push_region( " inner (pushed) " )
! Stop " inner " region with only page_rss components
call timemory_pop_region( " inner (pushed) " )
! restore previous set of components
call timemory_pop_components()
! end the " indexed " region
call timemory_end_record(idx)
! End " main "
call timemory_pop_region( " main " )
! Output the results
call timemory_finalize_library()
end program fortran_example
from timemory . bundle import marker
@ marker ([ "cpu_clock" , "peak_rss" ])
def foo ():
pass
from timemory . profiler import profile
def bar ():
with profile ([ "wall_clock" , "cpu_util" ]):
foo ()
from timemory . component import WallClock
def spam ():
wc = WallClock ( "spam" )
wc . start ()
bar ()
wc . stop ()
data = wc . get ()
print ( data )
import argparse
parser = argparse . ArgumentParser ( "example" )
# ...
timemory . add_arguments ( parser )
args = parser . parse_args ()
from timemory . storage import WallClockStorage
# data for current rank
data = WallClockStorage . get ()
# combined data on rank zero but all ranks must call it
dmp_data = WallClockStorage . dmp_get ()
Timemory originou-se como uma ferramenta muito simples para registrar medições de tempo e memória (daí o nome) em C, C++ e Python e suportava apenas três modos antes da versão 3.0.0: um conjunto fixo de temporizadores, um par de medições de memória, e a combinação dos dois. Antes do lançamento 3.0.0, o timemory foi quase completamente reescrito do zero, com as únicas exceções de algumas macros C/C++, por exemplo, TIMEMORY_AUTO_TIMER
e alguns decoradores e gerenciadores de contexto Python, por exemplo, timemory.util.auto_timer
, cujo comportamento foi capaz de ser totalmente replicado na nova versão. Assim, embora possa parecer que o timemory é um projeto maduro na v3.0+, ele ainda está essencialmente em seu primeiro grande lançamento.
Para fazer referência ao timemory em uma publicação, cite o seguinte artigo:
Para obter mais informações, consulte a documentação.