このプロジェクトは現在アーカイブされ、読み取り専用になっています
GitHub 上の timemory (ソースコード)
timemory 一般ドキュメント (ReadTheDocs)
timemory ソース コード ドキュメント (Doxygen)
timemory テスト ダッシュボード (CDash)
タイムメモリのチュートリアル
ECP 2021 チュートリアル 1 日目 (YouTube)
ECP 2021 チュートリアル 2 日目 (YouTube)
タイムメモリー Wiki
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 は、さまざまな API 用のユニバーサル アダプター キットの必要性から生まれ、いくつかの既存のツールと、新しいツールを作成するための簡単で直感的な方法を提供しました。 Timemory を使用すると、確定的パフォーマンス測定、統計的パフォーマンス測定 (つまり、サンプリング)、デバッグ メッセージ、データ ロギング、およびデータ検証を、カスタム アプリケーション固有のソフトウェア監視インターフェイス用に同じインターフェイスにバンドルして、 time
、 netstat
、instrumentation などのツールを簡単に構築できるようになります。プロファイラ、サンプリング プロファイラ、MPI-P、MPI-T、OMPT、KokkosP などの実装の作成が可能です。さらに、timemory はマーカーをいくつかのサードパーティ プロファイラに転送できます。 LIKWID、Caliper、TAU、gperftools、Perfetto、VTune、Allinea-MAP、CrayPAT、Nsight-Systems、Nsight-Compute、および NVProf。
Timemory は、フロントエンド C/C++/Fortran API および Python API を提供しており、タイマーからハードウェア カウンター、サードパーティ ツールとのインターフェイスに至るまで、50 以上の異なるコンポーネントを任意に選択できます。これはすべてcomponent_tuple<wall_clock, papi_vector, nvtx_marker, user_bundle>
wall_clock
のようなタイプ セーフなツール バンドルをpapi_vector
nvxt_marker
てツールキット API から一般的に構築されます。 NVIDIA CUDA プロファイラ、 user_bundle
、ダウンストリーム ユーザーが実行時にさらにコンポーネントを挿入できる汎用コンポーネントです。
timemory で記述されたパフォーマンス測定コンポーネントは、任意の数のスレッドとプロセスまで任意に拡張可能で、プログラム内の異なる場所での異なる測定の混合を完全にサポートします。これにより、timemory を導入して HPC で大規模なパフォーマンス データを収集できるようになります。これは、非常に詳細な収集が行われるためです。この問題はプログラム内の特定の場所で発生する可能性があり、ユビキタスなコレクションが同時にパフォーマンスを大幅に低下させ、法外な量のメモリを必要とします。
Timemory は、インストルメンテーション ツールとサンプリング ツールをまとめてバンドルし、JSON/XML へのシリアル化をサポートし、統計情報を提供するためのバックエンドとして使用できます。カスタム インストルメンテーション ツールやサンプリング ツールを呼び出すフロントエンドとしても利用できます。 Timemory では、パフォーマンス分析操作をカプセル化する構造に対して、抽象的な用語「コンポーネント」を使用します。この構造は、別のツールへの関数呼び出しをカプセル化し、タイミングのタイムスタンプを記録し、アプリケーションによって提供される値をログに記録し、コード内の関数を動的に置換するための演算子を提供し、受信する引数や関数から送信される戻り値を監査する、あるいは単に提供するだけの場合があります。リンカーによってオーバーロードされる可能性のあるスタブ。
timemory のネイティブ出力形式は JSON とテキストです。 XML などの他の出力形式もサポートされています。テキスト形式は人間が判読できるように設計されています。 JSON データは分析を目的としており、階層型とフラット型の 2 種類があります。基本的なプロット機能はtimemory-plotting
経由で利用できますが、pandas データフレームの階層的な JSON データを分析するには、hatchet を使用することを強くお勧めします。 Hatchet は、フィルタリング、結合、加算、減算、 dot
およびフレームグラフ形式への出力、およびインタラクティブな Jupyter ノートブックをサポートしています。現在、timemory は、Hatchet での分析のために 45 以上のメトリック タイプをサポートしています。
timemory には、コンポーネント、オペレーション、バンドラー、ストレージという 4 つの主要なカテゴリがあります。コンポーネントは特定の動作を実行する方法の詳細を提供し、オペレーションは複雑なシナリオでコンポーネントにオペレーションの実行を要求するための足場を提供し、バンドラーはコンポーネントを単一の汎用ハンドルにグループ化し、ストレージはアプリケーションの存続期間全体にわたるデータ収集を管理します。 4 つのカテゴリをすべて組み合わせると、timemory は事実上、データを受動的に収集し、アプリケーションの終了時にレポートと分析を提供する標準的なパフォーマンス分析ツールに似たものになります。ただし、Timemory を使用すると、方程式からストレージを差し引くことが非常に簡単になり、そうすることで、Timemory をカスタマイズされたデータ収集のためのツールキットに変換できます。
tim::component::wall_clock
: 単純な壁時計タイマーtim::component::vtune_profiler
: インテル® VTune プロファイラーをオンまたはオフにする単純なコンポーネント (インテル® 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
、「ストレージ」でのコールスタックの追跡などの高価なアクションを暗黙的に実行しません。注:
tim::lightweight_tuple
カスタム ツールとインターフェイスを実装するためのツールキットとして timemory を使用しようとしている人に推奨されるバンドルです
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
C 用のさまざまなマクロが、source/timemory/compat/timemory_c.h および source/timemory/variadic/macros.hpp に定義されています。使用例の多数のサンプルが例にあります。
# 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 つのモードのみをサポートしていました: 固定セットのタイマー、1 組のメモリ測定、そしてその二つの組み合わせ。 3.0.0 リリースより前に、timemory は、一部の C/C++ マクロ (例: TIMEMORY_AUTO_TIMER
)、および一部の Python デコレーターとコンテキスト マネージャー (例: timemory.util.auto_timer
) を唯一の例外として、ほぼ完全に最初から書き直されました。新しいリリースでは完全に複製されます。したがって、timemory は v3.0 以降では成熟したプロジェクトであるように見えるかもしれませんが、本質的にはまだ最初のメジャー リリースの段階にあります。
出版物でタイムメモリを参照するには、次の論文を引用してください。
詳細については、ドキュメントを参照してください。