Ce projet est désormais archivé et en lecture seule
timemory sur GitHub (code source)
Documentation générale de timemory (ReadTheDocs)
Documentation du code source de timemory (Doxygen)
Tableau de bord de test timemory (CDash)
Tutoriels timemory
Tutoriel ECP 2021, jour 1 (YouTube)
Tutoriel ECP 2021, jour 2 (YouTube)
Wiki Timemory
GitHub | git clone https://github.com/NERSC/timemory.git |
PyPi | pip install timemory |
Pack | spack install timemory |
conda-forge | conda install -c conda-forge timemory |
L'objectif de timemory est de créer un package open source de mesure et d'analyse des performances avec des composants modulaires et réutilisables qui peuvent être utilisés pour s'adapter à toute API de mesure et d'analyse des performances C/C++ existante et qui sont arbitrairement extensibles par les utilisateurs au sein de leur application. Timemory n'est pas simplement un autre outil de profilage, c'est une boîte à outils de profilage qui rationalise la création d'outils de profilage personnalisés grâce à la modularité, puis utilise la boîte à outils pour fournir plusieurs outils prédéfinis.
En d'autres termes, timemory fournit de nombreux outils, bibliothèques et interfaces prédéfinis mais, en raison de sa modularité, les codes ne peuvent réutiliser que des éléments individuels - tels que les classes permettant de mesurer différents intervalles de temps, l'utilisation de la mémoire et les compteurs matériels - - sans la "gestion du runtime" de la mémoire temporelle.
Timemory utilise une installation CMake standard. Plusieurs exemples d'installation peuvent être trouvés dans le Wiki. Consultez la documentation d'installation pour des informations détaillées sur les options CMake.
La documentation complète est disponible sur timemory.readthedocs.io. Une documentation source détaillée est fournie dans la section doygen de la documentation complète. Les didacticiels sont disponibles sur github.com/NERSC/timemory-tutorials.
L'objectif principal de timemory est le développement d'un cadre commun pour relier le code de surveillance des logiciels (c'est-à-dire l'analyse des performances, le débogage, la journalisation) dans une interface compacte et hautement efficace.
Timemory est né du besoin d'un kit d'adaptateur universel pour les différentes API, fournissant plusieurs outils existants et une méthode simple et intuitive pour créer de nouveaux outils. Timemory permet de regrouper les mesures de performances déterministes, les mesures de performances statistiques (c'est-à-dire l'échantillonnage), les messages de débogage, l'enregistrement des données et la validation des données dans la même interface pour des interfaces de surveillance logicielle personnalisées spécifiques aux applications, créant ainsi facilement des outils tels que time
, netstat
, instrumentation. profileurs, profileurs d'échantillonnage et implémentations d'écriture pour MPI-P, MPI-T, OMPT, KokkosP, etc. De plus, timemory peut transmettre ses marqueurs à plusieurs profileurs tiers tels que LIKWID, Caliper, TAU, gperftools, Perfetto, VTune, Allinea-MAP, CrayPAT, Nsight-Systems, Nsight-Compute et NVProf.
Timemory fournit une API frontale C/C++/Fortran et une API Python qui permettent la sélection arbitraire de plus de 50 composants différents, des minuteries aux compteurs matériels en passant par les interfaces avec des outils tiers. Tout cela est construit de manière générique à partir de l'API de la boîte à outils avec des ensembles d'outils de type sécurisé tels que : component_tuple<wall_clock, papi_vector, nvtx_marker, user_bundle>
où wall_clock
est une minuterie d'horloge murale, papi_vector
est un handle pour les compteurs matériels, nvxt_marker
crée des notations dans les profileurs NVIDIA CUDA et user_bundle
est un composant générique dans lequel les utilisateurs en aval peuvent insérer plus de composants au moment de l'exécution.
Les composants de mesure des performances écrits avec timemory sont arbitrairement évolutifs jusqu'à n'importe quel nombre de threads et de processus et prennent entièrement en charge le mélange de différentes mesures à différents endroits du programme. Cela permet de manière unique à timemory d'être déployé pour collecter des données de performances à grande échelle dans HPC, car une collecte très détaillée peut se produire à des endroits spécifiques du programme où une collecte omniprésente dégraderait simultanément les performances de manière significative et nécessiterait une quantité de mémoire prohibitive.
Timemory peut être utilisé comme backend pour regrouper des outils d'instrumentation et d'échantillonnage, prendre en charge la sérialisation au format JSON/XML et fournir des statistiques, entre autres utilisations. Il peut également être utilisé comme frontal pour invoquer des outils d’instrumentation et d’échantillonnage personnalisés. Timemory utilise le terme abstrait « composant » pour une structure qui encapsule une opération d'analyse des performances. La structure peut encapsuler les appels de fonction à un autre outil, enregistrer les horodatages pour le timing, enregistrer les valeurs fournies par l'application, fournir un opérateur pour remplacer dynamiquement une fonction dans le code, auditer les arguments entrants et/ou la valeur de retour sortante de la fonction, ou simplement fournir stubs qui peuvent être surchargés par l’éditeur de liens.
Le format de sortie natif de timemory est JSON et texte ; d'autres formats de sortie tels que XML sont également pris en charge. Le format texte est destiné à être lisible par l'homme. Les données JSON sont destinées à l'analyse et se déclinent en deux versions : hiérarchique et plate. Les capacités de traçage de base sont disponibles via timemory-plotting
mais les utilisateurs sont fortement encouragés à utiliser Hatet pour analyser les données JSON hiérarchiques dans les trames de données Pandas. Hatchet prend en charge le filtrage, les unions, l'addition, les soustractions, la sortie aux formats dot
et flamegraph, ainsi qu'un bloc-notes Jupyter interactif. À l'heure actuelle, timemory prend en charge plus de 45 types de métriques pour l'analyse dans Hatchet.
Il existe 4 catégories principales dans Timemory : les composants, les opérations, les bundles et le stockage. Les composants fournissent les détails sur la façon d'exécuter un comportement particulier, les opérations fournissent l'échafaudage permettant de demander à un composant d'effectuer une opération dans des scénarios complexes, les bundlers regroupent les composants dans un seul descripteur générique et le stockage gère la collecte de données pendant toute la durée de vie de l'application. Lorsque les quatre catégories sont combinées, timemory ressemble effectivement à un outil d'analyse des performances standard qui collecte passivement des données et fournit des rapports et des analyses à la fin de l'application. Timemory, cependant, permet de soustraire très facilement le stockage de l'équation et, ce faisant, transforme timemory en une boîte à outils pour la collecte de données personnalisée.
tim::component::wall_clock
: une simple minuterie d'horloge muraletim::component::vtune_profiler
: un composant simple qui active et désactive le VTune Profiler (lorsque VTune profile activement l'application)tim::component::data_tracker_integer
: associe des valeurs entières à une étiquette lors de l'exécution de l'application (par exemple, nombre d'itérations de boucle utilisées quelque part)tim::component::papi_vector
: utilise la bibliothèque PAPI pour collecter les valeurs des compteurs matérielstim::component::user_bundle
: encapsule un tableau de composants que l'utilisateur peut manipuler dynamiquement pendant l'exécutiontim::operation::start<wall_clock>
tentera d'appeler la fonction membre start()
sur une instance de composant wall_clock
wall_clock
a-t-il une fonction store(int)
? store()
?)tim::operation::start
: demande à un composant de démarrer la collectiontim::operation::sample
: demande à un composant de prendre une mesure individuelletim::operation::derive
: données supplémentaires provenant d'autres composants si elles sont disponiblestim::auto_tuple
tim::component_tuple
tim::component_list
tim::lightweight_tuple
auto_tuple
démarre tous les composants lorsqu'ils sont construits et arrête tous les composants lorsqu'ils sont détruits alors que component_tuple
nécessite un démarrage explicitecomponent_tuple
alloue tous les composants sur la pile et les composants sont "toujours activés" alors que component_list
alloue des composants sur le tas et donc les composants peuvent être activés/désactivés au moment de l'exécutionlightweight_tuple
n'effectue implicitement aucune action coûteuse, telle que le suivi de la pile d'appels dans « Stockage »REMARQUE :
tim::lightweight_tuple
est le package recommandé pour ceux qui cherchent à utiliser timemory comme boîte à outils pour implémenter des outils et des interfaces personnalisés.
timemory-avail
est fourni en tant que classe Python autonometime
UNIX qui inclut des informations supplémentaires sur l'utilisation de la mémoire, les changements de contexte et les compteurs matérielsnvidia-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
Diverses macros sont définies pour C dans source/timemory/compat/timemory_c.h et source/timemory/variadic/macros.hpp. De nombreux exemples de leur utilisation peuvent être trouvés dans les exemples.
# 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 est à l'origine un outil très simple pour enregistrer des mesures de synchronisation et de mémoire (d'où son nom) en C, C++ et Python et ne prenait en charge que trois modes avant la version 3.0.0 : un ensemble fixe de minuteries, une paire de mesures de mémoire, et la combinaison des deux. Avant la version 3.0.0, timemory était presque entièrement réécrit à partir de zéro, à la seule exception de certaines macros C/C++, par exemple TIMEMORY_AUTO_TIMER
, et de certains décorateurs et gestionnaires de contexte Python, par exemple timemory.util.auto_timer
, dont le comportement était capable de être entièrement répliqué dans la nouvelle version. Ainsi, même s'il peut sembler que timemory soit un projet mature en v3.0+, il en est essentiellement encore à sa première version majeure.
Pour référencer timemory dans une publication, veuillez citer l'article suivant :
Pour plus d'informations, reportez-vous à la documentation.