Dieses Projekt ist jetzt archiviert und schreibgeschützt
timemory auf GitHub (Quellcode)
Allgemeine Dokumentation zu timemory (ReadTheDocs)
TimeMory-Quellcode-Dokumentation (Doxygen)
TimeMory-Test-Dashboard (CDash)
timemory-Tutorials
ECP 2021 Tutorial Tag 1 (YouTube)
ECP 2021 Tutorial Tag 2 (YouTube)
Timemory-Wiki
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 |
Das Ziel von timemory besteht darin, ein Open-Source-Paket zur Leistungsmessung und -analyse mit modularen und wiederverwendbaren Komponenten zu erstellen, das zur Anpassung an jede vorhandene C/C++-API zur Leistungsmessung und -analyse verwendet werden kann und von Benutzern innerhalb ihrer Anwendung beliebig erweiterbar ist. Timemory ist nicht nur ein weiteres Profiling-Tool, sondern ein Profiling -Toolkit , das die Erstellung benutzerdefinierter Profiling-Tools durch Modularität rationalisiert und dann das Toolkit nutzt, um mehrere vorgefertigte Tools bereitzustellen.
Mit anderen Worten: Timemory bietet viele vorgefertigte Tools, Bibliotheken und Schnittstellen, aber aufgrund seiner Modularität können Codes nur einzelne Teile wiederverwenden – etwa die Klassen zum Messen verschiedener Zeitintervalle, Speichernutzung und Hardwarezähler. - ohne den Zeitspeicher „Laufzeitmanagement“.
Timemory verwendet eine standardmäßige CMake-Installation. Mehrere Installationsbeispiele finden Sie im Wiki. Ausführliche Informationen zu den CMake-Optionen finden Sie in der Installationsdokumentation.
Die vollständige Dokumentation ist unter timemory.readthedocs.io verfügbar. Eine ausführliche Quelldokumentation finden Sie im Doygen-Abschnitt der vollständigen Dokumentation. Tutorials sind unter github.com/NERSC/timemory-tutorials verfügbar.
Das Hauptziel des Timemory ist die Entwicklung eines gemeinsamen Frameworks zur Zusammenführung von Software-Überwachungscode (z. B. Leistungsanalyse, Debugging, Protokollierung) in einer kompakten und hocheffizienten Schnittstelle.
Timemory entstand aus der Notwendigkeit eines universellen Adapterkits für die verschiedenen APIs, der mehrere vorhandene Tools und eine einfache und intuitive Methode zum Erstellen neuer Tools bereitstellte. Timemory ermöglicht es, deterministische Leistungsmessungen, statistische Leistungsmessungen (z. B. Stichproben), Debug-Meldungen, Datenprotokollierung und Datenvalidierung in derselben Schnittstelle für benutzerdefinierte anwendungsspezifische Software-Überwachungsschnittstellen zu bündeln und so problemlos Tools wie time
, netstat
und Instrumentation zu erstellen Profiler, Sampling-Profiler und Schreibimplementierungen für MPI-P, MPI-T, OMPT, KokkosP usw. Darüber hinaus kann timemory seine Marker an mehrere Profiler von Drittanbietern wie LIKWID weiterleiten, Caliper, TAU, gperftools, Perfetto, VTune, Allinea-MAP, CrayPAT, Nsight-Systems, Nsight-Compute und NVProf.
Timemory bietet eine Front-End-C/C++/Fortran-API und eine Python-API, die eine beliebige Auswahl von über 50 verschiedenen Komponenten von Timern über Hardwarezähler bis hin zu Schnittstellen mit Tools von Drittanbietern ermöglicht. Dies alles wird generisch aus der Toolkit-API mit typsicheren Werkzeugpaketen erstellt, wie zum Beispiel: component_tuple<wall_clock, papi_vector, nvtx_marker, user_bundle>
wobei wall_clock
ein Wanduhr-Timer ist, papi_vector
ein Handle für Hardwarezähler ist und nvxt_marker
Notationen erstellt die NVIDIA CUDA-Profiler, und user_bundle
ist eine generische Komponente, in die nachgeschaltete Benutzer zur Laufzeit weitere Komponenten einfügen können.
Mit Timemory geschriebene Leistungsmesskomponenten sind beliebig auf eine beliebige Anzahl von Threads und Prozessen skalierbar und unterstützen vollständig die Vermischung verschiedener Messungen an verschiedenen Stellen innerhalb des Programms – dies ermöglicht aufgrund der hochdetaillierten Erfassung auf einzigartige Weise den Einsatz von Timemory zur Erfassung von Leistungsdaten im großen Maßstab in HPC kann an bestimmten Stellen innerhalb des Programms auftreten, wo eine allgegenwärtige Sammlung gleichzeitig die Leistung erheblich beeinträchtigen und eine unerschwingliche Menge an Speicher erfordern würde.
Timemory kann als Backend verwendet werden, um Instrumentierungs- und Sampling-Tools zu bündeln, die Serialisierung in JSON/XML zu unterstützen und unter anderem Statistiken bereitzustellen. Es kann auch als Frontend zum Aufrufen benutzerdefinierter Instrumentierungs- und Sampling-Tools verwendet werden. Timemory verwendet den abstrakten Begriff „Komponente“ für eine Struktur, die einige Leistungsanalyseoperationen kapselt. Die Struktur kann Funktionsaufrufe an ein anderes Tool kapseln, Zeitstempel für das Timing aufzeichnen, von der Anwendung bereitgestellte Werte protokollieren, einen Operator zum dynamischen Ersetzen einer Funktion im Code bereitstellen, die eingehenden Argumente und/oder den ausgehenden Rückgabewert der Funktion prüfen oder einfach nur bereitstellen Stubs, die vom Linker überlastet werden können.
Das native Ausgabeformat von timemory ist JSON und Text; Andere Ausgabeformate wie XML werden ebenfalls unterstützt. Das Textformat soll für Menschen lesbar sein. Die JSON-Daten dienen der Analyse und sind in zwei Varianten erhältlich: hierarchisch und flach. Grundlegende Darstellungsfunktionen stehen über timemory-plotting
zur Verfügung. Benutzern wird jedoch dringend empfohlen, Hatchet für die Analyse der hierarchischen JSON-Daten in Pandas-Datenrahmen zu verwenden. Hatchet unterstützt Filterung, Vereinigungen, Addition, Subtraktion, Ausgabe in dot
und Flamegraph-Formate sowie ein interaktives Jupyter-Notizbuch. Derzeit unterstützt Timemory mehr als 45 Metriktypen für die Analyse in Hatchet.
Es gibt vier Hauptkategorien im Zeitspeicher: Komponenten, Operationen, Bundler und Speicher. Komponenten stellen die Einzelheiten zur Ausführung eines bestimmten Verhaltens bereit, Operationen stellen das Gerüst für die Anforderung bereit, dass eine Komponente in komplexen Szenarien einen Vorgang ausführt, Bundler gruppieren Komponenten in einem einzigen generischen Handle und der Speicher verwaltet die Datenerfassung über die Lebensdauer der Anwendung. Wenn alle vier Kategorien kombiniert werden, ähnelt Timemory tatsächlich einem Standard-Leistungsanalysetool, das passiv Daten sammelt und nach Beendigung der Anwendung Berichte und Analysen bereitstellt. Timemory macht es jedoch sehr einfach , den Speicher aus der Gleichung zu entfernen und verwandelt Timemory so in ein Toolkit für die maßgeschneiderte Datenerfassung.
tim::component::wall_clock
: ein einfacher Wanduhr-Timertim::component::vtune_profiler
: eine einfache Komponente, die den VTune-Profiler ein- und ausschaltet (wenn VTune die Anwendung aktiv profiliert)tim::component::data_tracker_integer
: Ordnet einen ganzzahligen Wert einem Label zu, während die Anwendung ausgeführt wird (z. B. Anzahl der Schleifeniterationen, die irgendwo verwendet werden).tim::component::papi_vector
: verwendet die PAPI-Bibliothek, um Hardware-Zählerwerte zu sammelntim::component::user_bundle
: kapselt ein Array von Komponenten, die der Benutzer während der Laufzeit dynamisch bearbeiten kanntim::operation::start<wall_clock>
, versuchen, die start()
-Memberfunktion für eine wall_clock
Komponenteninstanz aufzurufenwall_clock
über eine store(int)
-Funktion? store()
?)tim::operation::start
: Weist eine Komponente an, die Sammlung zu startentim::operation::sample
: Weisen Sie eine Komponente an, eine einzelne Messung durchzuführentim::operation::derive
: zusätzliche Daten von anderen Komponenten, falls verfügbartim::auto_tuple
tim::component_tuple
tim::component_list
tim::lightweight_tuple
auto_tuple
startet alle Komponenten beim Erstellen und stoppt alle Komponenten beim Zerstören, wohingegen component_tuple
einen expliziten Start erfordertcomponent_tuple
ordnet alle Komponenten auf dem Stack zu und die Komponenten sind „immer aktiv“, wohingegen component_list
Komponenten auf dem Heap zuordnet und somit Komponenten zur Laufzeit aktiviert/deaktiviert werden könnenlightweight_tuple
führt implizit keine teuren Aktionen aus, wie z. B. Call-Stack-Tracking in „Storage“.HINWEIS:
tim::lightweight_tuple
ist das empfohlene Paket für diejenigen, die Timemory als Toolkit für die Implementierung benutzerdefinierter Tools und Schnittstellen verwenden möchten
timemory-avail
wird als eigenständige Python-Klasse bereitgestellttime
, das zusätzliche Informationen zur Speichernutzung, Kontextwechsel und Hardwarezähler enthältnvidia-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
Für C sind in source/timemory/compat/timemory_c.h und source/timemory/variadic/macros.hpp verschiedene Makros definiert. Zahlreiche Anwendungsbeispiele finden Sie in den Beispielen.
# 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 entstand als sehr einfaches Tool zum Aufzeichnen von Timing- und Speichermessungen (daher der Name) in C, C++ und Python und unterstützte vor der Veröffentlichung 3.0.0 nur drei Modi: einen festen Satz von Timern, ein Paar von Speichermessungen, und die Kombination aus beidem. Vor der Veröffentlichung 3.0.0 wurde timemory fast vollständig von Grund auf neu geschrieben, mit Ausnahme einiger C/C++-Makros, z. B. TIMEMORY_AUTO_TIMER
, und einiger Python-Dekoratoren und Kontextmanager, z. B. timemory.util.auto_timer
, deren Verhalten dazu in der Lage war werden in der neuen Version vollständig repliziert. Auch wenn es den Anschein hat, dass Timemory mit Version 3.0+ ein ausgereiftes Projekt ist, befindet es sich im Wesentlichen noch in der ersten Hauptversion.
Um in einer Veröffentlichung auf Timemory zu verweisen, zitieren Sie bitte den folgenden Artikel:
Weitere Informationen finden Sie in der Dokumentation.