ขณะนี้โครงการนี้ถูกเก็บถาวรและอ่านอย่างเดียวแล้ว
timemory บน GitHub (ซอร์สโค้ด)
เอกสารทั่วไปของ 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-ฟอร์จ | conda install -c conda-forge timemory |
เป้าหมายของ Timemory คือการสร้างแพ็คเกจการวัดและการวิเคราะห์ประสิทธิภาพแบบโอเพ่นซอร์สที่มีส่วนประกอบแบบโมดูลาร์และนำมาใช้ซ้ำได้ ซึ่งสามารถใช้เพื่อปรับให้เข้ากับ API การวัดและการวิเคราะห์ประสิทธิภาพ C/C++ ที่มีอยู่ และขยายได้ตามใจชอบโดยผู้ใช้ภายในแอปพลิเคชันของพวกเขา 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 ฯลฯ นอกจากนี้ timemory สามารถส่งต่อเครื่องหมายไปยัง ผู้สร้างโปรไฟล์บุคคลที่สามหลายราย เช่น LIKWID, Caliper, TAU, gperftools, Perfetto, VTune, Allinea-MAP, CrayPAT, Nsight-Systems, Nsight-Compute และ NVProf
Timemory จัดเตรียม front-end C/C++/Fortran API และ Python API ซึ่งอนุญาตให้เลือกส่วนประกอบที่แตกต่างกันมากกว่า 50 รายการตั้งแต่ตัวจับเวลาไปจนถึงตัวนับฮาร์ดแวร์ไปจนถึงอินเทอร์เฟซกับเครื่องมือของบุคคลที่สาม ทั้งหมดนี้สร้างขึ้นโดยทั่วไปจากชุดเครื่องมือ API พร้อมด้วยชุดเครื่องมือประเภทที่ปลอดภัย เช่น: component_tuple<wall_clock, papi_vector, nvtx_marker, user_bundle>
โดยที่ wall_clock
เป็นตัวจับเวลานาฬิกาแขวน papi_vector
เป็นตัวจัดการสำหรับตัวนับฮาร์ดแวร์ nvxt_marker
สร้างสัญลักษณ์ใน ตัวสร้างโปรไฟล์ NVIDIA CUDA และ user_bundle
เป็นส่วนประกอบทั่วไปที่ผู้ใช้ดาวน์สตรีมสามารถแทรกส่วนประกอบเพิ่มเติมเข้าไปในรันไทม์ได้
องค์ประกอบการวัดประสิทธิภาพที่เขียนด้วย timemory สามารถปรับขนาดได้ตามต้องการตามจำนวนเธรดและกระบวนการต่างๆ และสนับสนุนการผสมผสานการวัดที่แตกต่างกันในตำแหน่งต่างๆ ภายในโปรแกรมได้อย่างเต็มที่ ซึ่งจะช่วยให้ใช้งาน timemory ได้อย่างมีเอกลักษณ์เพื่อรวบรวมข้อมูลประสิทธิภาพในขนาดใน HPC เนื่องจากการเก็บรวบรวมที่มีรายละเอียดสูง สามารถเกิดขึ้นได้ในสถานที่เฉพาะภายในโปรแกรมที่การเก็บรวบรวมที่แพร่หลายจะทำให้ประสิทธิภาพลดลงอย่างมากและต้องใช้หน่วยความจำในปริมาณที่ห้ามปราม
Timemory สามารถใช้เป็นแบ็กเอนด์เพื่อรวมเครื่องมือวัดและเครื่องมือสุ่มตัวอย่างเข้าด้วยกัน รองรับการทำให้เป็นอนุกรมกับ JSON/XML และจัดเตรียมสถิติรวมถึงการใช้งานอื่นๆ นอกจากนี้ยังสามารถใช้เป็นส่วนหน้าเพื่อเรียกใช้เครื่องมือวัดแบบกำหนดเองและเครื่องมือสุ่มตัวอย่างได้ Timemory ใช้คำว่า "ส่วนประกอบ" เชิงนามธรรมสำหรับโครงสร้างที่สรุปการดำเนินการวิเคราะห์ประสิทธิภาพบางอย่าง โครงสร้างอาจสรุปการเรียกใช้ฟังก์ชันไปยังเครื่องมืออื่น บันทึกการประทับเวลาสำหรับการกำหนดเวลา ค่าบันทึกที่แอปพลิเคชันจัดเตรียมให้ จัดเตรียมตัวดำเนินการสำหรับการแทนที่ฟังก์ชันในโค้ดแบบไดนามิก ตรวจสอบอาร์กิวเมนต์ขาเข้าและ/หรือค่าส่งคืนขาออกจากฟังก์ชัน หรือเพียงระบุ ต้นขั้วที่ตัวเชื่อมโยงสามารถโอเวอร์โหลดได้
รูปแบบเอาต์พุตดั้งเดิมของ timemory คือ JSON และข้อความ นอกจากนี้ยังรองรับรูปแบบเอาต์พุตอื่น ๆ เช่น XML รูปแบบข้อความมีวัตถุประสงค์เพื่อให้มนุษย์สามารถอ่านได้ ข้อมูล JSON มีไว้สำหรับการวิเคราะห์และมีสองรูปแบบ: แบบลำดับชั้นและแบบเรียบ ความสามารถในการวางแผนขั้นพื้นฐานนั้นมีให้ใช้งานผ่าน timemory-plotting
แต่ขอแนะนำให้ผู้ใช้ใช้ขวานเพื่อวิเคราะห์ข้อมูล JSON แบบลำดับชั้นในดาต้าเฟรมของแพนด้า Hatchet รองรับการกรอง การรวม การบวก การลบ เอาท์พุตเป็นรูปแบบ dot
และเฟลมกราฟ และสมุดบันทึก Jupyter แบบโต้ตอบ ปัจจุบัน Timemory รองรับประเภทเมตริกมากกว่า 45 ประเภทสำหรับการวิเคราะห์ใน Hatchet
ช่วงเวลามี 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>
จะพยายามเรียกใช้ฟังก์ชันสมาชิก 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
ไม่ได้ดำเนินการใดๆ ที่มีราคาแพงโดยปริยาย เช่น การติดตาม call-stack ใน "ที่เก็บข้อมูล"หมายเหตุ:
tim::lightweight_tuple
เป็นชุดที่แนะนำสำหรับผู้ที่ต้องการใช้ timemory เป็นชุดเครื่องมือสำหรับการใช้เครื่องมือและอินเทอร์เฟซที่กำหนดเอง
timemory-avail
มีให้เป็นคลาส Python แบบสแตนด์อโลนtime
UNIX เวอร์ชันขยายที่มีข้อมูลเพิ่มเติมเกี่ยวกับการใช้หน่วยความจำ การสลับบริบท และตัวนับฮาร์ดแวร์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 timemory เกือบจะถูกเขียนใหม่ทั้งหมดตั้งแต่ต้น โดยมีข้อยกเว้นเพียงอย่างเดียวของมาโคร C/C++ บางตัว เช่น TIMEMORY_AUTO_TIMER
และตัวตกแต่ง Python และตัวจัดการบริบทบางตัว เช่น timemory.util.auto_timer
ซึ่งมีพฤติกรรมที่สามารถทำได้ จะถูกจำลองแบบเต็มรูปแบบในรีลีสใหม่ ดังนั้น แม้ดูเหมือนว่า timemory จะเป็นโปรเจ็กต์ที่สมบูรณ์ในเวอร์ชัน 3.0+ แต่โดยพื้นฐานแล้วยังอยู่ในการเปิดตัวครั้งใหญ่ครั้งแรก
หากต้องการอ้างอิงช่วงเวลาในสิ่งพิมพ์ โปรดอ้างอิงเอกสารต่อไปนี้:
สำหรับข้อมูลเพิ่มเติม โปรดดูเอกสารประกอบ