該項目現已存檔且唯讀
GitHub 上的 timemory(原始碼)
timemory 通用文檔 (ReadTheDocs)
timemory原始碼文檔(Doxygen)
timemory測試儀表板(CDash)
時間記憶教程
ECP 2021 教程第 1 天(YouTube)
ECP 2021 教程第 2 天(YouTube)
維基百科
GitHub | git clone https://github.com/NERSC/timemory.git |
皮皮 | pip install timemory |
史派克 | spack install timemory |
康達鍛造公司 | conda install -c conda-forge timemory |
timemory的目標是創建一個具有模組化和可重複使用組件的開源性能測量和分析包,可用於適應任何現有的C/C++效能測量和分析API,並且使用者可以在其應用程式中任意擴展。 Timemory 不僅僅是另一個分析工具,它還是一個分析工具包,它透過模組化簡化了自訂分析工具的構建,然後利用該工具包提供了多個預先建構的工具。
換句話說,timemory 提供了許多預先建構的工具、庫和接口,但由於它的模組化,程式碼只能重複使用單獨的部分——例如用於測量不同時間間隔、記憶體使用情況和硬體計數器的類別— — - 沒有timemory「運行時管理」。
Timemory 使用標準 CMake 安裝。可以在 Wiki 中找到幾個安裝範例。有關 CMake 選項的詳細信息,請參閱安裝文件。
完整文件可在 timemory.readthedocs.io 上取得。完整文件的 doygen 部分提供了詳細的來源文件。教學課程可在 github.com/NERSC/timemory-tutorials 中找到。
timemory的主要目標是開發一個通用框架,將軟體監控程式碼(即效能分析、調試、日誌記錄)綁定到一個緊湊且高效的介面。
Timemory 的出現是為了滿足對各種 API 的通用適配器套件的需求,提供了幾種現有工具以及用於創建新工具的直接直觀的方法。 Timemory 可以將確定性效能測量、統計效能測量(即取樣)、偵錯訊息、資料記錄和資料驗證捆綁到同一介面中,用於自訂特定於應用程式的軟體監控接口,輕鬆建立time
、 netstat
、 Instrumentation等工具分析器、取樣分析器以及MPI-P、MPI-T、OMPT、KokkosP 等的編寫實作。 Perfetto、VTune、 Allinea-MAP、CrayPAT、Nsight-Systems、Nsight-Compute 和 NVProf。
Timemory 提供前端 C/C++/Fortran API 和 Python API,允許任意選擇 50 多個不同的元件,從定時器到硬體計數器,再到與第三方工具的介面。這全部都是透過工具包 API 使用類型安全的工具包構建的,例如: component_tuple<wall_clock, papi_vector, nvtx_marker, user_bundle>
其中wall_clock
是掛鐘計時器, papi_vector
是硬體計數器的句柄, nvxt_marker
是在中建立建立關係計數器的句柄分析器,而user_bundle
是一個通用元件,下游用戶可以在運行時插入更多元件。
使用timemory 編寫的效能測量元件可以任意擴展至任意數量的執行緒和進程,並完全支援在程式內的不同位置混合不同的測量結果——這使得能夠部署timemory 以在HPC 中大規模收集效能數據,因為收集的數據非常詳細可能發生在程式內的特定位置,其中無處不在的收集會同時顯著降低效能並需要大量記憶體。
Timemory 可以用作後端,將偵測和採樣工具捆綁在一起,支援序列化為 JSON/XML,並提供其他用途的統計資訊。它也可以用作調用自訂儀器和採樣工具的前端。 Timemory 使用抽象術語「組件」來表示封裝了某些效能分析操作的結構。該結構可能封裝對另一個工具的函數調用,記錄計時時間戳,記錄應用程式提供的值,提供用於動態替換程式碼中函數的運算符,審核函數的傳入參數和/或傳出返回值,或只是提供可以由連結器重載的存根。
timemory原生輸出格式為JSON與文字;也支援其他輸出格式,例如 XML。文字格式旨在便於人類閱讀。 JSON 資料用於分析,有兩種類型:分層資料和扁平資料。基本的繪圖功能可以透過timemory-plotting
取得,但強烈鼓勵使用者使用 hashet 來分析 pandas 資料框中的層次結構 JSON 資料。 Hatchet 支援過濾、並集、加法、減法、輸出為dot
和火焰圖格式以及互動式 Jupyter 筆記本。目前,timemory支援45+種指標類型在Hatchet中進行分析。
timemory 有 4 個主要類別:組件、操作、捆綁器和儲存。元件提供如何執行特定行為的細節,操作提供用於請求元件在複雜場景中執行操作的支架,捆綁器將元件分組到單一通用句柄中,儲存管理應用程式生命週期內的資料收集。當所有四個類別組合在一起時,timemory 實際上類似於標準性能分析工具,它被動收集數據並在應用程式終止時提供報告和分析。然而,TiMemory 可以非常輕鬆地從方程式中減去儲存空間,將 TiMemory 轉變為用於自訂資料收集的工具包。
tim::component::wall_clock
:一個簡單的掛鐘定時器tim::component::vtune_profiler
:一個簡單的元件,用於開啟和關閉 VTune Profiler(當 VTune 主動分析應用程式時)tim::component::data_tracker_integer
:在應用程式執行時將整數值與標籤相關聯(例如,某處使用的循環迭代次數)tim::component::papi_vector
:使用 PAPI 庫收集硬體計數器值tim::component::user_bundle
:封裝了使用者可以在執行時動態操作的元件數組tim::operation::start<wall_clock>
會嘗試在wall_clock
元件實例上呼叫start()
成員函數wall_clock
是否有store(int)
函數? store()
?)tim::operation::start
:指示元件開始收集tim::operation::sample
:指示組件進行單獨測量tim::operation::derive
:來自其他元件的額外資料(如果可用)tim::auto_tuple
tim::component_tuple
tim::component_list
tim::lightweight_tuple
auto_tuple
在建構時啟動所有元件,在析構時停止所有元件,而component_tuple
需要明確啟動component_tuple
在堆疊上分配所有組件,並且組件“始終處於打開狀態”,而component_list
在堆上分配組件,因此可以在運行時激活/停用組件lightweight_tuple
不會隱式執行任何昂貴的操作,例如「儲存」中的呼叫堆疊追蹤注意:對於那些尋求使用 timemory 作為實現自訂工具和介面的工具包的人,建議使用
tim::lightweight_tuple
包
timemory-avail
中的每個元件都以獨立的 Python 類別提供time
命令列工具的擴展版本,包括有關記憶體使用情況、上下文切換和硬體計數器的附加信息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
在source/timemory/compat/timemory_c.h 和source/timemory/variadic/macros.hpp 中為C 定義了各種巨集。在範例中可以找到它們的許多用法範例。
# 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 最初是一個非常簡單的工具,用於以C、C++ 和Python 記錄計時和記憶體測量(因此得名),在3.0.0 版本之前僅支援三種模式:一組固定的計時器、一對內存測量、以及兩者的結合。在 3.0.0 版本之前,timemory 幾乎完全從頭開始重寫,唯一的例外是一些 C/C++ 宏,例如TIMEMORY_AUTO_TIMER
,以及一些 Python 裝飾器和上下文管理器,例如timemory.util.auto_timer
,其行為能夠在新版本中完全複製。因此,雖然 timemory 在 v3.0+ 上看起來是一個成熟的項目,但它本質上仍處於第一個主要版本。
若要在出版品中引用 timemory,請引用以下論文:
有關更多信息,請參閱文檔。