تم الآن أرشفة هذا المشروع وهو للقراءة فقط
الذاكرة الزمنية على جيثب (كود المصدر)
الوثائق العامة للذاكرة الزمنية (ReadTheDocs)
توثيق كود مصدر الذاكرة الزمنية (Doxygen)
لوحة تحكم اختبار الذاكرة الزمنية (CDash)
دروس الذاكرة الزمنية
اليوم التعليمي الأول لـ ECP 2021 (يوتيوب)
اليوم التعليمي الثاني لـ ECP 2021 (يوتيوب)
ويكي الذاكرة الزمنية
جيثب | git clone https://github.com/NERSC/timemory.git |
بيبي | pip install timemory |
سباك | spack install timemory |
كوندا فورج | conda install -c conda-forge timemory |
الهدف من timemory هو إنشاء حزمة قياس وتحليل أداء مفتوحة المصدر مع مكونات معيارية وقابلة لإعادة الاستخدام والتي يمكن استخدامها للتكيف مع أي واجهة برمجة تطبيقات موجودة لقياس وتحليل أداء C/C++ ويمكن توسيعها بشكل تعسفي من قبل المستخدمين داخل تطبيقهم. Timemory ليست مجرد أداة أخرى للتوصيف، بل هي عبارة عن مجموعة أدوات للتوصيف تعمل على تبسيط إنشاء أدوات إنشاء ملفات تعريف مخصصة من خلال النمطية ثم تستخدم مجموعة الأدوات لتوفير العديد من الأدوات المعدة مسبقًا.
بمعنى آخر، توفر الذاكرة الزمنية العديد من الأدوات والمكتبات والواجهات المعدة مسبقًا، ولكن نظرًا لنمطيتها، يمكن للرموز إعادة استخدام الأجزاء الفردية فقط - مثل فئات قياس فترات التوقيت المختلفة واستخدام الذاكرة وعدادات الأجهزة - - بدون الذاكرة الزمنية "إدارة وقت التشغيل".
يستخدم Timemory تثبيت CMake القياسي. يمكن العثور على العديد من أمثلة التثبيت في Wiki. راجع وثائق التثبيت للحصول على معلومات مفصلة حول خيارات CMake.
الوثائق الكاملة متاحة على timemory.readthedocs.io. يتم توفير وثائق المصدر التفصيلية في قسم doygen من الوثائق الكاملة. تتوفر البرامج التعليمية على github.com/NERSC/timemory-tutorials.
الهدف الأساسي للذاكرة الزمنية هو تطوير إطار عمل مشترك لربط كود مراقبة البرامج معًا (أي تحليل الأداء وتصحيح الأخطاء والتسجيل) في واجهة مدمجة وعالية الكفاءة.
نشأت Timemory من الحاجة إلى مجموعة أدوات محول عالمية لمختلف واجهات برمجة التطبيقات التي توفر العديد من الأدوات الموجودة وطريقة مباشرة وبديهية لإنشاء أدوات جديدة. يتيح Timemory إمكانية تجميع قياسات الأداء الحتمية وقياسات الأداء الإحصائية (أي أخذ العينات) ورسائل تصحيح الأخطاء وتسجيل البيانات والتحقق من صحة البيانات في نفس الواجهة لواجهات مراقبة البرامج المخصصة الخاصة بالتطبيقات، مما يسهل إنشاء أدوات مثل time
و netstat
والأجهزة ملفات التعريف، وملفات تعريف العينات، وتطبيقات الكتابة لـ MPI-P، وMPI-T، وOMPT، وKokkosP، وما إلى ذلك. علاوة على ذلك، يمكن لذاكرة الوقت إعادة توجيه علاماتها إلى العديد من الجهات الخارجية ملفات التعريف مثل LIKWID، وCaliper، وTAU، وgperftools، وPerfetto، وVTune، وAllinea-MAP، وCrayPAT، وNsight-Systems، وNsight-Compute، وNVProf.
يوفر Timemory واجهة برمجة تطبيقات C/C++/Fortran وPython API للواجهة الأمامية والتي تسمح بالاختيار التعسفي لأكثر من 50 مكونًا مختلفًا بدءًا من أجهزة ضبط الوقت وعدادات الأجهزة وحتى الواجهات باستخدام أدوات الطرف الثالث. تم إنشاء كل هذا بشكل عام من واجهة برمجة تطبيقات مجموعة الأدوات مع حزم آمنة من الأدوات مثل: component_tuple<wall_clock, papi_vector, nvtx_marker, user_bundle>
حيث wall_clock
هو مؤقت ساعة الحائط، papi_vector
هو مقبض لعدادات الأجهزة، nvxt_marker
ينشئ تدوينات في ملفات التعريف NVIDIA CUDA، و user_bundle
هو مكون عام يمكن للمستخدمين المصب إدراج المزيد المكونات في وقت التشغيل.
مكونات قياس الأداء المكتوبة باستخدام الذاكرة الزمنية قابلة للتطوير بشكل تعسفي إلى أي عدد من الخيوط والعمليات وتدعم بشكل كامل خلط القياسات المختلفة في مواقع مختلفة داخل البرنامج - وهذا يمكّن بشكل فريد من نشر الذاكرة الزمنية لجمع بيانات الأداء على نطاق واسع في HPC نظرًا لجمعها المفصل للغاية يمكن أن يحدث في مواقع محددة داخل البرنامج حيث يؤدي التجميع في كل مكان إلى انخفاض الأداء بشكل كبير ويتطلب قدرًا باهظًا من الذاكرة.
يمكن استخدام Timemory كواجهة خلفية لتجميع الأدوات وأدوات أخذ العينات معًا، ودعم التسلسل إلى JSON/XML، وتوفير الإحصائيات من بين الاستخدامات الأخرى. ويمكن استخدامه أيضًا كواجهة أمامية لاستدعاء الأدوات المخصصة وأدوات أخذ العينات. يستخدم Timemory المصطلح المجرد "مكون" للبنية التي تتضمن بعض عمليات تحليل الأداء. قد تقوم البنية بتغليف استدعاءات الوظائف إلى أداة أخرى، أو تسجيل الطوابع الزمنية للتوقيت، أو تسجيل القيم التي يوفرها التطبيق، أو توفير عامل تشغيل لاستبدال وظيفة في التعليمات البرمجية ديناميكيًا، أو تدقيق الوسائط الواردة و/أو قيمة الإرجاع الصادرة من الوظيفة، أو مجرد توفير بذرة والتي يمكن أن تكون محملة بشكل زائد بواسطة الرابط.
تنسيق الإخراج الأصلي للذاكرة الزمنية هو JSON والنص؛ كما يتم دعم تنسيقات الإخراج الأخرى مثل XML. يهدف تنسيق النص إلى أن يكون قابلاً للقراءة من قبل الإنسان. بيانات JSON مخصصة للتحليل وتأتي في نسختين: هرمي ومسطح. تتوفر إمكانات التخطيط الأساسية عبر timemory-plotting
ولكن يتم تشجيع المستخدمين بشدة على استخدام الأحقاد لتحليل بيانات JSON الهرمية في إطارات بيانات الباندا. يدعم Hatchet التصفية، والاتحادات، والجمع، والطرح، والإخراج إلى تنسيقات dot
ومخطط اللهب، ودفتر Jupyter التفاعلي. في الوقت الحاضر، تدعم الذاكرة الزمنية أكثر من 45 نوعًا من المقاييس للتحليل في Hatchet.
هناك 4 فئات أساسية في الذاكرة الزمنية: المكونات، والعمليات، والمجمعات، والتخزين. توفر المكونات تفاصيل حول كيفية تنفيذ سلوك معين، وتوفر العمليات السقالة لطلب أن يقوم أحد المكونات بإجراء عملية في سيناريوهات معقدة، وتقوم أدوات تجميع المكونات بتجميع المكونات في مقبض عام واحد، ويقوم التخزين بإدارة جمع البيانات على مدار عمر التطبيق. عند الجمع بين الفئات الأربع، تشبه الذاكرة الزمنية بشكل فعال أداة تحليل الأداء القياسية التي تجمع البيانات بشكل سلبي وتوفر التقارير والتحليلات عند إنهاء التطبيق. ومع ذلك، فإن 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>
ستحاول استدعاء وظيفة عضو start()
على مثيل مكون wall_clock
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
كفئة بايثون قائمة بذاتها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.0.0، تمت إعادة كتابة الذاكرة الزمنية بالكامل تقريبًا من الصفر مع الاستثناءات الوحيدة لبعض وحدات الماكرو C/C++، على سبيل المثال، TIMEMORY_AUTO_TIMER
، وبعض أدوات تزيين Python ومدير السياق، على سبيل المثال، timemory.util.auto_timer
، والتي كان سلوكها قادرًا على سيتم تكرارها بالكامل في الإصدار الجديد. وبالتالي، في حين أنه قد يبدو أن timemory هو مشروع ناضج في الإصدار 3.0+، إلا أنه لا يزال في أول إصدار رئيسي له.
للإشارة إلى الذاكرة الزمنية في أحد المنشورات، يرجى الاستشهاد بالمقالة التالية:
لمزيد من المعلومات، راجع الوثائق.