Este proyecto ahora está archivado y es de solo lectura.
timemory en GitHub (Código fuente)
Documentación general de timemory (ReadTheDocs)
Documentación del código fuente de timemory (Doxygen)
Panel de pruebas de Timemory (CDash)
Tutoriales de tiempo de memoria
Tutorial de ECP 2021, día 1 (YouTube)
Tutorial de ECP 2021, día 2 (YouTube)
Wiki Tiempo de memoria
GitHub | git clone https://github.com/NERSC/timemory.git |
PyPi | pip install timemory |
paquete | spack install timemory |
conda-forja | conda install -c conda-forge timemory |
El objetivo de timemory es crear un paquete de análisis y medición del rendimiento de código abierto con componentes modulares y reutilizables que se puedan utilizar para adaptarse a cualquier API de análisis y medición del rendimiento C/C++ existente y que los usuarios puedan ampliar arbitrariamente dentro de su aplicación. Timemory no es simplemente otra herramienta de creación de perfiles, es un conjunto de herramientas de creación de perfiles que agiliza la creación de herramientas de creación de perfiles personalizadas a través de la modularidad y luego utiliza el conjunto de herramientas para proporcionar varias herramientas prediseñadas.
En otras palabras, timemory proporciona muchas herramientas, bibliotecas e interfaces prediseñadas pero, debido a su modularidad, los códigos solo pueden reutilizar piezas individuales, como las clases para medir diferentes intervalos de tiempo, uso de memoria y contadores de hardware. - sin la "gestión del tiempo de ejecución" de timemory.
Timemory utiliza una instalación estándar de CMake. Se pueden encontrar varios ejemplos de instalación en la Wiki. Consulte la documentación de instalación para obtener información detallada sobre las opciones de CMake.
La documentación completa está disponible en timemory.readthedocs.io. La documentación fuente detallada se proporciona en la sección anterior de la documentación completa. Los tutoriales están disponibles en github.com/NERSC/timemory-tutorials.
El objetivo principal de Timemory es el desarrollo de un marco común para unir el código de monitoreo de software (es decir, análisis de rendimiento, depuración, registro) en una interfaz compacta y altamente eficiente.
Timemory surgió de la necesidad de un kit de adaptador universal para las distintas API que proporcionaba varias herramientas existentes y un método sencillo e intuitivo para crear nuevas herramientas. Timemory hace posible agrupar mediciones de rendimiento deterministas, mediciones de rendimiento estadístico (es decir, muestreo), mensajes de depuración, registro de datos y validación de datos en la misma interfaz para interfaces de monitoreo de software específicas de aplicaciones personalizadas, creando fácilmente herramientas como time
, netstat
, instrumentación. perfiladores, perfiladores de muestreo e implementaciones de escritura para MPI-P, MPI-T, OMPT, KokkosP, etc. Además, timemory puede reenviar sus marcadores a varios perfiladores de terceros, como LIKWID, Caliper, TAU, gperftools, Perfetto, VTune, Allinea-MAP, CrayPAT, Nsight-Systems, Nsight-Compute y NVProf.
Timemory proporciona una API frontal de C/C++/Fortran y una API de Python que permite la selección arbitraria de más de 50 componentes diferentes, desde temporizadores hasta contadores de hardware e interfaces con herramientas de terceros. Todo esto se construye genéricamente desde la API del kit de herramientas con paquetes de herramientas con seguridad de tipos como: component_tuple<wall_clock, papi_vector, nvtx_marker, user_bundle>
donde wall_clock
es un temporizador de reloj de pared, papi_vector
es un identificador para contadores de hardware, nvxt_marker
crea notaciones en los perfiladores NVIDIA CUDA y user_bundle
es un componente genérico en el que los usuarios posteriores pueden insertar más componentes en tiempo de ejecución.
Los componentes de medición del rendimiento escritos con timemory son escalables arbitrariamente hasta cualquier número de subprocesos y procesos y admiten completamente la combinación de diferentes mediciones en diferentes ubicaciones dentro del programa; esto permite de manera única implementar timemory para recopilar datos de rendimiento a escala en HPC porque la recopilación es altamente detallada. puede ocurrir en ubicaciones específicas dentro del programa donde la recopilación ubicua degradaría simultáneamente el rendimiento de manera significativa y requeriría una cantidad prohibitiva de memoria.
Timemory se puede utilizar como backend para agrupar instrumentos y herramientas de muestreo, admitir la serialización a JSON/XML y proporcionar estadísticas, entre otros usos. También se puede utilizar como interfaz para invocar instrumentos personalizados y herramientas de muestreo. Timemory utiliza el término abstracto "componente" para una estructura que encapsula alguna operación de análisis de rendimiento. La estructura podría encapsular llamadas a funciones a otra herramienta, registrar marcas de tiempo para el tiempo, registrar valores proporcionados por la aplicación, proporcionar un operador para reemplazar una función en el código dinámicamente, auditar los argumentos entrantes y/o el valor de retorno saliente de la función, o simplemente proporcionar stubs que pueden ser sobrecargados por el enlazador.
El formato de salida nativo de timemory es JSON y texto; También se admiten otros formatos de salida como XML. El formato del texto está pensado para que sea legible por humanos. Los datos JSON están destinados al análisis y vienen en dos versiones: jerárquicos y planos. Las capacidades básicas de trazado están disponibles a través timemory-plotting
, pero se recomienda encarecidamente a los usuarios que utilicen Hatchet para analizar los datos JSON jerárquicos en los marcos de datos de Pandas. Hatchet admite filtrado, uniones, sumas, restas, salida a formatos de dot
y gráficos de llamas, y un cuaderno Jupyter interactivo. Actualmente, timemory admite más de 45 tipos de métricas para análisis en Hatchet.
Hay cuatro categorías principales en la memoria del tiempo: componentes, operaciones, paquetes y almacenamiento. Los componentes proporcionan los detalles de cómo realizar un comportamiento particular, las operaciones proporcionan la base para solicitar que un componente realice una operación en escenarios complejos, los paquetes agrupan componentes en un único identificador genérico y el almacenamiento gestiona la recopilación de datos durante la vida útil de la aplicación. Cuando se combinan las cuatro categorías, timemory se asemeja efectivamente a una herramienta de análisis de rendimiento estándar que recopila datos de forma pasiva y proporciona informes y análisis al finalizar la aplicación. Timemory, sin embargo, hace que sea muy fácil restar almacenamiento de la ecuación y, al hacerlo, transforma timemory en un conjunto de herramientas para la recopilación de datos personalizados.
tim::component::wall_clock
: un simple temporizador de paredtim::component::vtune_profiler
: un componente simple que activa y desactiva VTune Profiler (cuando VTune está perfilando activamente la aplicación)tim::component::data_tracker_integer
: asocia valores enteros con una etiqueta a medida que se ejecuta la aplicación (por ejemplo, número de iteraciones de bucle utilizadas en alguna parte)tim::component::papi_vector
: utiliza la biblioteca PAPI para recopilar valores de contadores de hardwaretim::component::user_bundle
: encapsula una matriz de componentes que el usuario puede manipular dinámicamente durante el tiempo de ejecucióntim::operation::start<wall_clock>
intentará llamar a la función miembro start()
en una instancia del componente wall_clock
wall_clock
tiene una función store(int)
? store()
?)tim::operation::start
: indica a un componente que inicie la coleccióntim::operation::sample
: indica a un componente que tome medidas individualestim::operation::derive
: datos adicionales de otros componentes si están disponiblestim::auto_tuple
tim::component_tuple
tim::component_list
tim::lightweight_tuple
auto_tuple
inicia todos los componentes cuando se construyen y detiene todos los componentes cuando se destruyen, mientras que component_tuple
requiere un inicio explícitocomponent_tuple
asigna todos los componentes en la pila y los componentes están "siempre activos", mientras que component_list
asigna componentes en el montón y, por lo tanto, los componentes se pueden activar/desactivar en tiempo de ejecuciónlightweight_tuple
no realiza implícitamente ninguna acción costosa, como el seguimiento de la pila de llamadas en "Almacenamiento"NOTA:
tim::lightweight_tuple
es el paquete recomendado para aquellos que buscan utilizar timemory como un conjunto de herramientas para implementar herramientas e interfaces personalizadas.
timemory-avail
se proporciona como una clase Python independientetime
de UNIX que incluye información adicional sobre el uso de memoria, cambios de contexto y contadores de hardware.nvidia-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
Se definen varias macros para C en source/timemory/compat/timemory_c.h y source/timemory/variadic/macros.hpp. En los ejemplos se pueden encontrar numerosos ejemplos de su uso.
# 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 se originó como una herramienta muy simple para registrar mediciones de tiempo y memoria (de ahí el nombre) en C, C++ y Python y solo admitía tres modos antes de la versión 3.0.0: un conjunto fijo de temporizadores, un par de mediciones de memoria, y la combinación de los dos. Antes de la versión 3.0.0, timemory se reescribió casi por completo desde cero con las únicas excepciones de algunas macros C/C++, por ejemplo, TIMEMORY_AUTO_TIMER
, y algunos decoradores y administradores de contexto de Python, por ejemplo, timemory.util.auto_timer
, cuyo comportamiento fue capaz de replicarse completamente en la nueva versión. Por lo tanto, si bien puede parecer que timemory es un proyecto maduro en v3.0+, esencialmente todavía se encuentra en su primera versión importante.
Para hacer referencia al tiempo en una publicación, cite el siguiente artículo:
Para obtener más información, consulte la documentación.