From 7cc34e61bb41cf541b1015d9baf5ece6f1804820 Mon Sep 17 00:00:00 2001 From: Wei Wu Date: Wed, 3 Jun 2026 19:31:10 -0700 Subject: [PATCH 1/7] new nvtx --- CMakeLists.txt | 10 +- src/realm/nvtx.cc | 257 +++++++++++++++++++--------------------------- src/realm/nvtx.h | 97 +++++++++++------ 3 files changed, 178 insertions(+), 186 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 00015af86f9..2d8f6c3f811 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -423,17 +423,11 @@ if(REALM_ENABLE_CUDA OR (REALM_ENABLE_HIP AND CMAKE_HIP_PLATFORM STREQUAL "nvidi PATH_SUFFIXES include ../include extras/CUPTI/include ../extras/CUPTI/include ) - # Deal with the library name change in older versions of cmake. - # - # nvtx3 was added in cmake 3.25, and while later versions (at least 3.30+) claim that - # the CUDA::nvtx3 and CUDA::nvToolsExt targets are mutually exclusive, they don't - # appear to be so in earlier versions. So if we have nvtx3, prefer that, otherwise - # fall back to nvToolsExt. + # We use the header-only NVTX v3 C++ API, which is exposed via the + # CUDA::nvtx3 target (added in cmake 3.25). Require it; otherwise disable NVTX. set(REALM_USE_NVTX ${REALM_ENABLE_NVTX}) if(TARGET CUDA::nvtx3) list(APPEND REALM_LIBRARIES CUDA::nvtx3) - elseif(TARGET CUDA::nvToolsExt) - list(APPEND REALM_LIBRARIES CUDA::nvToolsExt) else() # Silently disable NVTX if we cannot find it set(REALM_USE_NVTX OFF) diff --git a/src/realm/nvtx.cc b/src/realm/nvtx.cc index d2bd4b51b21..b9a0745acff 100644 --- a/src/realm/nvtx.cc +++ b/src/realm/nvtx.cc @@ -17,89 +17,108 @@ #include "realm/nvtx.h" -#include +#include #include +#include #ifdef REALM_ON_WINDOWS #include #else +#include #include #endif namespace Realm { - struct realm_nvtx_domain { - static constexpr char const *name{"Realm"}; - }; + // Definitions of the predefined category handles declared in the header. + NvtxCategory *nvtx_amsg = nullptr; + NvtxCategory *nvtx_bgwork = nullptr; +#ifdef REALM_USE_CUDA + NvtxCategory *nvtx_cuda = nullptr; +#endif +#ifdef REALM_USE_HIP + NvtxCategory *nvtx_hip = nullptr; +#endif +#ifdef REALM_USE_GASNET1 + NvtxCategory *nvtx_gasnet1 = nullptr; +#endif +#ifdef REALM_USE_GASNETEX + NvtxCategory *nvtx_gasnetex = nullptr; +#endif +#ifdef REALM_USE_MPI + NvtxCategory *nvtx_mpi = nullptr; +#endif +#ifdef REALM_USE_OPENMP + NvtxCategory *nvtx_openmp = nullptr; +#endif +#ifdef REALM_USE_PYTHON + NvtxCategory *nvtx_python = nullptr; +#endif - struct nvtx_category_id_color { + // Static description of a predefined category: its id, default color, and the + // global handle to point at the created object. `slot` is a reference to one + // of the `nvtx_*` pointers defined above. + struct nvtx_category_def { uint32_t id; uint32_t color; + std::reference_wrapper slot; }; - static std::map nvtx_categories_predefined = { - {"amsg", {1, nvtx_color::red}}, - {"bgwork", {2, nvtx_color::blue}}, + static const std::map nvtx_categories_predefined = { + {"amsg", {1, nvtx_color::red, std::ref(nvtx_amsg)}}, + {"bgwork", {2, nvtx_color::blue, std::ref(nvtx_bgwork)}}, #ifdef REALM_USE_CUDA - {"cuda", {100, nvtx_color::green}}, + {"cuda", {100, nvtx_color::green, std::ref(nvtx_cuda)}}, #endif #ifdef REALM_USE_HIP - {"hip", {101, nvtx_color::purple}}, + {"hip", {101, nvtx_color::purple, std::ref(nvtx_hip)}}, #endif #ifdef REALM_USE_GASNET1 - {"gasnet1", {102, nvtx_color::lawn_green}}, + {"gasnet1", {102, nvtx_color::lawn_green, std::ref(nvtx_gasnet1)}}, #endif #ifdef REALM_USE_GASNETEX - {"gasnetex", {103, nvtx_color::cyan}}, + {"gasnetex", {103, nvtx_color::cyan, std::ref(nvtx_gasnetex)}}, #endif #ifdef REALM_USE_MPI - {"mpi", {104, nvtx_color::maroon}}, + {"mpi", {104, nvtx_color::maroon, std::ref(nvtx_mpi)}}, #endif #ifdef REALM_USE_OPENMP - {"openmp", {105, nvtx_color::navy}}, + {"openmp", {105, nvtx_color::navy, std::ref(nvtx_openmp)}}, #endif #ifdef REALM_USE_PYTHON - {"python", {106, nvtx_color::magenta}}, + {"python", {106, nvtx_color::magenta, std::ref(nvtx_python)}}, #endif }; - thread_local std::map *nvtx_categories; - - static nvtxDomainHandle_t nvtxRealmDomain = nullptr; + // Owns the category objects created at init; cleared at finalize. The global + // `nvtx_*` handles point into these. + static std::vector> nvtx_owned_categories; static std::vector enabled_nvtx_modules; - static inline NvtxCategory *find_category_by_name(const std::string &name) + // Build a fresh event on the stack for each annotation. NVTX consumes the + // attributes synchronously, so there is no need to keep one around. A `white` + // color means "use the category's default color". + static inline nvtx3::event_attributes make_event(const NvtxCategory &category, + const char *message, uint32_t color, + int32_t payload) { - std::map::iterator it = nvtx_categories->find(name); - if(it != nvtx_categories->end()) { - return it->second; - } else { - return nullptr; - } + return nvtx3::event_attributes{ + category.category, + nvtx3::color{color != nvtx_color::white ? color : category.default_color}, + nvtx3::payload{payload}, nvtx3::message{message}}; } - //////////////////////////////////////////////////////////////////////// - // - // class nvtxCategory - - NvtxCategory::NvtxCategory(const std::string &category_name, uint32_t category_id, - uint32_t color) - : name(category_name) + static inline nvtxDomainHandle_t realm_domain() { - // name the category - nvtxDomainNameCategoryA(nvtxRealmDomain, category_id, category_name.c_str()); + return nvtx3::domain::get(); + } - // create nvtx event attribute and set values - memset(&nvtx_event, 0, NVTX_EVENT_ATTRIB_STRUCT_SIZE); - nvtx_event.version = NVTX_VERSION; - nvtx_event.size = NVTX_EVENT_ATTRIB_STRUCT_SIZE; - nvtx_event.category = category_id; - nvtx_event.messageType = NVTX_MESSAGE_TYPE_ASCII; - nvtx_event.message.ascii = ""; - nvtx_event.payloadType = NVTX_PAYLOAD_TYPE_INT32; - nvtx_event.payload.iValue = 0; - nvtx_event.colorType = NVTX_COLOR_ARGB; - nvtx_event.color = color; + // Create the category object, take ownership, and point its global handle at it. + static void create_category(const std::string &name, const nvtx_category_def &def) + { + nvtx_owned_categories.push_back( + std::make_unique(name, def.id, def.color)); + def.slot.get() = nvtx_owned_categories.back().get(); } //////////////////////////////////////////////////////////////////////// @@ -108,43 +127,40 @@ namespace Realm { nvtxScopedRange::nvtxScopedRange(NvtxCategory *category, char const *message, int32_t payload) + : active(false) { - category->nvtx_event.message.ascii = message; - category->nvtx_event.payload.iValue = payload; - nvtxDomainRangePushEx(nvtxRealmDomain, &(category->nvtx_event)); + if(category) { + nvtx3::event_attributes attr = + make_event(*category, message, nvtx_color::white, payload); + nvtxDomainRangePushEx(realm_domain(), attr.get()); + active = true; + } } - nvtxScopedRange::nvtxScopedRange(const std::string &name, char const *message, - int32_t payload) + nvtxScopedRange::~nvtxScopedRange() { - NvtxCategory *category = find_category_by_name(name); - if(category) { - category->nvtx_event.message.ascii = message; - category->nvtx_event.payload.iValue = payload; - nvtxDomainRangePushEx(nvtxRealmDomain, &(category->nvtx_event)); + if(active) { + nvtxDomainRangePop(realm_domain()); } } - nvtxScopedRange::~nvtxScopedRange() { nvtxDomainRangePop(nvtxRealmDomain); } - void init_nvtx_thread(const char *thread_name) { #ifdef REALM_ON_WINDOWS - nvtxNameOsThread(GetCurrentThreadId(), thread_name) + nvtxNameOsThread(GetCurrentThreadId(), thread_name); #else nvtxNameOsThread(pthread_self(), thread_name); #endif + } - nvtx_categories = new std::map(); - nvtx_categories->clear(); + void init_nvtx(std::vector &nvtx_modules) + { + enabled_nvtx_modules = nvtx_modules; - if(enabled_nvtx_modules.size() == 1 and enabled_nvtx_modules[0] == "all") { + if(enabled_nvtx_modules.size() == 1 && enabled_nvtx_modules[0] == "all") { // handle -ll:nvtx_modules all - std::map::const_iterator it; - for(it = nvtx_categories_predefined.cbegin(); - it != nvtx_categories_predefined.cend(); it++) { - nvtx_categories->insert(std::pair( - it->first, new NvtxCategory(it->first, it->second.id, it->second.color))); + for(const auto &entry : nvtx_categories_predefined) { + create_category(entry.first, entry.second); } } else { for(const std::string &name : enabled_nvtx_modules) { @@ -152,123 +168,66 @@ namespace Realm { std::cerr << "If all specified, then no other modules are needed." << std::endl; abort(); } - std::map::const_iterator it = + std::map::const_iterator it = nvtx_categories_predefined.find(name); if(it == nvtx_categories_predefined.end()) { std::cerr << "Unable to find specified nvtx module: " << name << std::endl; abort(); } - nvtx_categories->insert(std::pair( - name, new NvtxCategory(name, it->second.id, it->second.color))); + create_category(it->first, it->second); } } - } - - void finalize_nvtx_thread(void) - { - std::map::iterator it; - for(it = nvtx_categories->begin(); it != nvtx_categories->end(); it++) { - assert(it->second != nullptr); - delete it->second; - } - delete nvtx_categories; - } - void init_nvtx(std::vector &nvtx_modules) - { - enabled_nvtx_modules = nvtx_modules; - nvtxInitialize(nullptr); - nvtxRealmDomain = nvtxDomainCreateA(realm_nvtx_domain::name); init_nvtx_thread("MainThread"); } void finalize_nvtx(void) { - nvtxDomainDestroy(nvtxRealmDomain); - finalize_nvtx_thread(); - } - - void nvtx_range_push(NvtxCategory *category, const char *message, uint32_t color, - int32_t payload) - { - uint32_t origin_color; - if(color != nvtx_color::white) { - origin_color = category->nvtx_event.color; - category->nvtx_event.color = color; - } - category->nvtx_event.message.ascii = message; - category->nvtx_event.payload.iValue = payload; - nvtxDomainRangePushEx(nvtxRealmDomain, &(category->nvtx_event)); - if(color != nvtx_color::white) { - category->nvtx_event.color = origin_color; + // The nvtx3 domain is intentionally never destroyed (see nvtx3 docs). Drop + // the owned categories and reset the global handles so none dangle. + nvtx_owned_categories.clear(); + for(const auto &entry : nvtx_categories_predefined) { + entry.second.slot.get() = nullptr; } } - void nvtx_range_push(const std::string &name, const char *message, uint32_t color, + void nvtx_range_push(NvtxCategory *category, const char *message, uint32_t color, int32_t payload) { - NvtxCategory *category = find_category_by_name(name); - if(category) { - nvtx_range_push(category, message, color, payload); + if(!category) { + return; } + nvtx3::event_attributes attr = make_event(*category, message, color, payload); + nvtxDomainRangePushEx(realm_domain(), attr.get()); } - void nvtx_range_pop(void) { nvtxDomainRangePop(nvtxRealmDomain); } + void nvtx_range_pop(void) { nvtxDomainRangePop(realm_domain()); } - nvtxRangeId_t nvtx_range_start(NvtxCategory *category, const char *message, - uint32_t color, int32_t payload) + nvtx3::range_handle nvtx_range_start(NvtxCategory *category, const char *message, + uint32_t color, int32_t payload) { - uint32_t origin_color; - if(color != nvtx_color::white) { - origin_color = category->nvtx_event.color; - category->nvtx_event.color = color; - } - category->nvtx_event.message.ascii = message; - category->nvtx_event.payload.iValue = payload; - nvtxRangeId_t id = nvtxDomainRangeStartEx(nvtxRealmDomain, &(category->nvtx_event)); - if(color != nvtx_color::white) { - category->nvtx_event.color = origin_color; + if(!category) { + return nullptr; } - return id; + nvtx3::event_attributes attr = make_event(*category, message, color, payload); + return nvtx3::start_range_in(attr); } - nvtxRangeId_t nvtx_range_start(const std::string &name, const char *message, - uint32_t color, int32_t payload) + void nvtx_range_end(nvtx3::range_handle id) { - NvtxCategory *category = find_category_by_name(name); - if(category) { - return nvtx_range_start(category, message, color, payload); - } else { - return 0; + if(id) { + nvtx3::end_range_in(id); } } - void nvtx_range_end(nvtxRangeId_t id) { nvtxDomainRangeEnd(nvtxRealmDomain, id); } - void nvtx_mark(NvtxCategory *category, const char *message, uint32_t color, int32_t payload) { - uint32_t origin_color; - if(color != nvtx_color::white) { - origin_color = category->nvtx_event.color; - category->nvtx_event.color = color; - } - category->nvtx_event.message.ascii = message; - category->nvtx_event.color = color; - category->nvtx_event.payload.iValue = payload; - nvtxDomainMarkEx(nvtxRealmDomain, &(category->nvtx_event)); - if(color != nvtx_color::white) { - category->nvtx_event.color = origin_color; - } - } - - void nvtx_mark(const std::string &name, const char *message, uint32_t color, - int32_t payload) - { - NvtxCategory *category = find_category_by_name(name); - if(category) { - nvtx_mark(category, message, color, payload); + if(!category) { + return; } + nvtx3::event_attributes attr = make_event(*category, message, color, payload); + nvtx3::mark_in(attr); } }; // namespace Realm diff --git a/src/realm/nvtx.h b/src/realm/nvtx.h index c73fbc9376a..48ce226b421 100644 --- a/src/realm/nvtx.h +++ b/src/realm/nvtx.h @@ -20,12 +20,12 @@ #include "realm/realm_config.h" +// Realm's NVTX support is built on the nvtx3 C++ API. The C-only headers +// () are intentionally not used as a fallback. #if __has_include() #include -#elif __has_include() -#include #else -#error "Configuration failed to find suitable NVTX headers" +#error "Realm NVTX support requires the nvtx3 C++ headers ()" #endif #include @@ -71,56 +71,95 @@ namespace Realm { olive = NvtxARGB(128, 128, 0).to_uint(), }; + // The single NVTX domain that groups all of Realm's annotations. This is an + // nvtx3 domain tag type: it just needs a static `name` member. + struct realm_nvtx_domain { + static constexpr char const *name{"Realm"}; + }; + + // Metadata for one NVTX category. Immutable after construction. Categories are + // created only during init_nvtx (on the main thread, before worker threads are + // started), so no synchronization is needed. + // + // `category` carries the category id (via get_id()) and, on construction, + // registers the id<->name association with NVTX so tools display the name. struct NvtxCategory { - NvtxCategory(const std::string &category_name, uint32_t category_id, uint32_t color); - const std::string name; - nvtxEventAttributes_t nvtx_event; + NvtxCategory(const std::string &category_name, uint32_t category_id, uint32_t color) + : name(category_name) + , category(category_id, category_name.c_str()) + , default_color(color) + {} + std::string name; + nvtx3::named_category_in category; + uint32_t default_color; }; + // Handles to the predefined categories. Each is non-null only if its module + // was enabled via -ll:nvtx_modules; otherwise it stays null and annotation + // calls on it are cheap no-ops. Call sites pass these directly, with no name + // lookup. Defined in nvtx.cc. + extern NvtxCategory *nvtx_amsg; + extern NvtxCategory *nvtx_bgwork; +#ifdef REALM_USE_CUDA + extern NvtxCategory *nvtx_cuda; +#endif +#ifdef REALM_USE_HIP + extern NvtxCategory *nvtx_hip; +#endif +#ifdef REALM_USE_GASNET1 + extern NvtxCategory *nvtx_gasnet1; +#endif +#ifdef REALM_USE_GASNETEX + extern NvtxCategory *nvtx_gasnetex; +#endif +#ifdef REALM_USE_MPI + extern NvtxCategory *nvtx_mpi; +#endif +#ifdef REALM_USE_OPENMP + extern NvtxCategory *nvtx_openmp; +#endif +#ifdef REALM_USE_PYTHON + extern NvtxCategory *nvtx_python; +#endif + + // RAII nested range. A null `category` (disabled module) pushes nothing and + // the destructor pops nothing, keeping the NVTX range stack balanced. struct nvtxScopedRange { - nvtxScopedRange(NvtxCategory *category, char const *message, int32_t payload); - nvtxScopedRange(const std::string &name, char const *message, int32_t payload); + nvtxScopedRange(NvtxCategory *category, char const *message, int32_t payload = 0); ~nvtxScopedRange(); + + private: + bool active; }; + // Base category id for application-defined categories (predefined ones use + // small ids). An application that wants its own category can construct an + // NvtxCategory with an id >= this value and pass its address to the calls below. static constexpr uint32_t nvtx_proc_starting_category_id = 1000; - // called by each kernel thread to init thread local variables. + // Called by each kernel thread to name the OS thread in NVTX tools. void init_nvtx_thread(const char *thread_name); - // called by RuntimeImpl::configure_from_command_line from the main thread to init nvtx - // and its thread local variables. + // Called by RuntimeImpl::configure_from_command_line on the main thread to + // create the enabled categories. Must run before worker threads start. void init_nvtx(std::vector &nvtx_modules); - // called by each kernel thread to delete thread local variables. - void finalize_nvtx_thread(void); - - // called by RuntimeImpl::wait_for_shutdown from the main thread to finalize nvtx - // and delete its thread local variables. + // Called by RuntimeImpl::wait_for_shutdown on the main thread. void finalize_nvtx(void); - // TODO(@Wei Wu): template it wih type T for payload void nvtx_range_push(NvtxCategory *category, const char *message, uint32_t color = nvtx_color::white, int32_t payload = 0); - void nvtx_range_push(const std::string &name, const char *message, - uint32_t color = nvtx_color::white, int32_t payload = 0); - void nvtx_range_pop(void); - // TODO(@Wei Wu): template it wih type T for payload - nvtxRangeId_t nvtx_range_start(NvtxCategory *category, const char *message, - uint32_t color = nvtx_color::white, int32_t payload = 0); - nvtxRangeId_t nvtx_range_start(const std::string &name, const char *message, - uint32_t color = nvtx_color::white, int32_t payload = 0); + nvtx3::range_handle nvtx_range_start(NvtxCategory *category, const char *message, + uint32_t color = nvtx_color::white, + int32_t payload = 0); - void nvtx_range_end(nvtxRangeId_t id); + void nvtx_range_end(nvtx3::range_handle id); - // TODO(@Wei Wu): template it wih type T for payload void nvtx_mark(NvtxCategory *category, const char *message, uint32_t color = nvtx_color::white, int32_t payload = 0); - void nvtx_mark(const std::string &name, const char *message, - uint32_t color = nvtx_color::white, int32_t payload = 0); }; // namespace Realm From 56f284529b94143896487bc18bed0b1c53907262 Mon Sep 17 00:00:00 2001 From: Wei Wu Date: Wed, 3 Jun 2026 23:27:07 -0700 Subject: [PATCH 2/7] check --- src/realm/nvtx.cc | 82 ++------------------ src/realm/nvtx.h | 180 ++++++++++++++++++++++++++++--------------- src/realm/threads.cc | 8 -- 3 files changed, 123 insertions(+), 147 deletions(-) diff --git a/src/realm/nvtx.cc b/src/realm/nvtx.cc index b9a0745acff..9593c9a0966 100644 --- a/src/realm/nvtx.cc +++ b/src/realm/nvtx.cc @@ -20,6 +20,7 @@ #include #include #include +#include "realm/atomics.h" #ifdef REALM_ON_WINDOWS #include #else @@ -59,7 +60,7 @@ namespace Realm { // of the `nvtx_*` pointers defined above. struct nvtx_category_def { uint32_t id; - uint32_t color; + nvtx3::color color; std::reference_wrapper slot; }; @@ -95,23 +96,7 @@ namespace Realm { static std::vector enabled_nvtx_modules; - // Build a fresh event on the stack for each annotation. NVTX consumes the - // attributes synchronously, so there is no need to keep one around. A `white` - // color means "use the category's default color". - static inline nvtx3::event_attributes make_event(const NvtxCategory &category, - const char *message, uint32_t color, - int32_t payload) - { - return nvtx3::event_attributes{ - category.category, - nvtx3::color{color != nvtx_color::white ? color : category.default_color}, - nvtx3::payload{payload}, nvtx3::message{message}}; - } - - static inline nvtxDomainHandle_t realm_domain() - { - return nvtx3::domain::get(); - } + static atomic nvtx_proc_starting_category_id{1000}; // Create the category object, take ownership, and point its global handle at it. static void create_category(const std::string &name, const nvtx_category_def &def) @@ -121,29 +106,6 @@ namespace Realm { def.slot.get() = nvtx_owned_categories.back().get(); } - //////////////////////////////////////////////////////////////////////// - // - // class nvtxScopedRange - - nvtxScopedRange::nvtxScopedRange(NvtxCategory *category, char const *message, - int32_t payload) - : active(false) - { - if(category) { - nvtx3::event_attributes attr = - make_event(*category, message, nvtx_color::white, payload); - nvtxDomainRangePushEx(realm_domain(), attr.get()); - active = true; - } - } - - nvtxScopedRange::~nvtxScopedRange() - { - if(active) { - nvtxDomainRangePop(realm_domain()); - } - } - void init_nvtx_thread(const char *thread_name) { #ifdef REALM_ON_WINDOWS @@ -191,43 +153,9 @@ namespace Realm { } } - void nvtx_range_push(NvtxCategory *category, const char *message, uint32_t color, - int32_t payload) + uint32_t nvtx_get_next_category_id(void) { - if(!category) { - return; - } - nvtx3::event_attributes attr = make_event(*category, message, color, payload); - nvtxDomainRangePushEx(realm_domain(), attr.get()); - } - - void nvtx_range_pop(void) { nvtxDomainRangePop(realm_domain()); } - - nvtx3::range_handle nvtx_range_start(NvtxCategory *category, const char *message, - uint32_t color, int32_t payload) - { - if(!category) { - return nullptr; - } - nvtx3::event_attributes attr = make_event(*category, message, color, payload); - return nvtx3::start_range_in(attr); - } - - void nvtx_range_end(nvtx3::range_handle id) - { - if(id) { - nvtx3::end_range_in(id); - } - } - - void nvtx_mark(NvtxCategory *category, const char *message, uint32_t color, - int32_t payload) - { - if(!category) { - return; - } - nvtx3::event_attributes attr = make_event(*category, message, color, payload); - nvtx3::mark_in(attr); + return nvtx_proc_starting_category_id.fetch_add(1); } }; // namespace Realm diff --git a/src/realm/nvtx.h b/src/realm/nvtx.h index 48ce226b421..ed48f0bb5b9 100644 --- a/src/realm/nvtx.h +++ b/src/realm/nvtx.h @@ -29,47 +29,31 @@ #endif #include +#include #include #include namespace Realm { - struct NvtxARGB { - constexpr NvtxARGB(uint8_t red_, uint8_t green_, uint8_t blue_, - uint8_t alpha_ = 0xFF) noexcept - : red{red_} - , green{green_} - , blue{blue_} - , alpha{alpha_} - {} - constexpr uint32_t to_uint(void) const - { - return uint32_t{alpha} << 24 | uint32_t{red} << 16 | uint32_t{green} << 8 | - uint32_t{blue}; - } - uint8_t const red{}; - uint8_t const green{}; - uint8_t const blue{}; - uint8_t const alpha{}; - }; - - enum nvtx_color : uint32_t - { - white = NvtxARGB(255, 255, 255).to_uint(), - red = NvtxARGB(255, 0, 0).to_uint(), - green = NvtxARGB(0, 255, 0).to_uint(), - blue = NvtxARGB(0, 0, 255).to_uint(), - purple = NvtxARGB(128, 0, 128).to_uint(), - lawn_green = NvtxARGB(124, 252, 0).to_uint(), - cyan = NvtxARGB(0, 255, 255).to_uint(), - maroon = NvtxARGB(128, 0, 0).to_uint(), - navy = NvtxARGB(0, 0, 128).to_uint(), - magenta = NvtxARGB(255, 0, 255).to_uint(), - yellow = NvtxARGB(255, 255, 0).to_uint(), - gray = NvtxARGB(128, 128, 128).to_uint(), - teal = NvtxARGB(0, 128, 128).to_uint(), - olive = NvtxARGB(128, 128, 0).to_uint(), - }; + // Named colors for NVTX events, as nvtx3::color values (fully opaque ARGB, + // since rgb{} fills the alpha channel). Pass one of these to an annotation + // call, or pass std::nullopt to fall back to the category's default color. + namespace nvtx_color { + inline constexpr nvtx3::color white{nvtx3::rgb{255, 255, 255}}; + inline constexpr nvtx3::color red{nvtx3::rgb{255, 0, 0}}; + inline constexpr nvtx3::color green{nvtx3::rgb{0, 255, 0}}; + inline constexpr nvtx3::color blue{nvtx3::rgb{0, 0, 255}}; + inline constexpr nvtx3::color purple{nvtx3::rgb{128, 0, 128}}; + inline constexpr nvtx3::color lawn_green{nvtx3::rgb{124, 252, 0}}; + inline constexpr nvtx3::color cyan{nvtx3::rgb{0, 255, 255}}; + inline constexpr nvtx3::color maroon{nvtx3::rgb{128, 0, 0}}; + inline constexpr nvtx3::color navy{nvtx3::rgb{0, 0, 128}}; + inline constexpr nvtx3::color magenta{nvtx3::rgb{255, 0, 255}}; + inline constexpr nvtx3::color yellow{nvtx3::rgb{255, 255, 0}}; + inline constexpr nvtx3::color gray{nvtx3::rgb{128, 128, 128}}; + inline constexpr nvtx3::color teal{nvtx3::rgb{0, 128, 128}}; + inline constexpr nvtx3::color olive{nvtx3::rgb{128, 128, 0}}; + } // namespace nvtx_color // The single NVTX domain that groups all of Realm's annotations. This is an // nvtx3 domain tag type: it just needs a static `name` member. @@ -77,6 +61,13 @@ namespace Realm { static constexpr char const *name{"Realm"}; }; + // Sentinel payload meaning "no payload". NVTX ignores the payload value when + // its type is NVTX_PAYLOAD_UNKNOWN, so events built with this carry no + // payload at all (rather than an int32 value of 0). Used as the default for + // the annotation calls below so callers can simply omit the payload. + inline constexpr nvtx3::payload nvtx_no_payload{NVTX_PAYLOAD_UNKNOWN, + nvtx3::payload::value_type{}}; + // Metadata for one NVTX category. Immutable after construction. Categories are // created only during init_nvtx (on the main thread, before worker threads are // started), so no synchronization is needed. @@ -84,14 +75,15 @@ namespace Realm { // `category` carries the category id (via get_id()) and, on construction, // registers the id<->name association with NVTX so tools display the name. struct NvtxCategory { - NvtxCategory(const std::string &category_name, uint32_t category_id, uint32_t color) + NvtxCategory(const std::string &category_name, uint32_t category_id, + nvtx3::color color) : name(category_name) , category(category_id, category_name.c_str()) , default_color(color) {} std::string name; nvtx3::named_category_in category; - uint32_t default_color; + nvtx3::color default_color; }; // Handles to the predefined categories. Each is non-null only if its module @@ -122,21 +114,6 @@ namespace Realm { extern NvtxCategory *nvtx_python; #endif - // RAII nested range. A null `category` (disabled module) pushes nothing and - // the destructor pops nothing, keeping the NVTX range stack balanced. - struct nvtxScopedRange { - nvtxScopedRange(NvtxCategory *category, char const *message, int32_t payload = 0); - ~nvtxScopedRange(); - - private: - bool active; - }; - - // Base category id for application-defined categories (predefined ones use - // small ids). An application that wants its own category can construct an - // NvtxCategory with an id >= this value and pass its address to the calls below. - static constexpr uint32_t nvtx_proc_starting_category_id = 1000; - // Called by each kernel thread to name the OS thread in NVTX tools. void init_nvtx_thread(const char *thread_name); @@ -147,19 +124,98 @@ namespace Realm { // Called by RuntimeImpl::wait_for_shutdown on the main thread. void finalize_nvtx(void); - void nvtx_range_push(NvtxCategory *category, const char *message, - uint32_t color = nvtx_color::white, int32_t payload = 0); + // Called by an application to get the next available category id. + uint32_t nvtx_get_next_category_id(void); - void nvtx_range_pop(void); + // Internal helpers for the inline annotation calls below. Kept in the header + // (inline) so the thin wrappers fully inline at call sites; in particular the + // common "disabled module" case (null category) collapses to nothing. - nvtx3::range_handle nvtx_range_start(NvtxCategory *category, const char *message, - uint32_t color = nvtx_color::white, - int32_t payload = 0); + // Build a fresh event on the stack for each annotation. NVTX consumes the + // attributes synchronously, so there is no need to keep one around. An empty + // `color` means "use the category's default color". + inline nvtx3::event_attributes nvtx_make_event(const NvtxCategory &category, + const char *message, + std::optional color, + nvtx3::payload payload) + { + return nvtx3::event_attributes{category.category, + color.value_or(category.default_color), payload, + nvtx3::message{message}}; + } - void nvtx_range_end(nvtx3::range_handle id); + inline nvtxDomainHandle_t nvtx_realm_domain() + { + return nvtx3::domain::get(); + } - void nvtx_mark(NvtxCategory *category, const char *message, - uint32_t color = nvtx_color::white, int32_t payload = 0); + // RAII nested range. A null `category` (disabled module) pushes nothing and + // the destructor pops nothing, keeping the NVTX range stack balanced. + struct [[nodiscard]] nvtxScopedRange { + nvtxScopedRange(NvtxCategory *category, char const *message, + nvtx3::payload payload = nvtx_no_payload) noexcept + : active(false) + { + if(category) { + nvtx3::event_attributes attr = + nvtx_make_event(*category, message, std::nullopt, payload); + nvtxDomainRangePushEx(nvtx_realm_domain(), attr.get()); + active = true; + } + } + ~nvtxScopedRange() + { + if(active) { + nvtxDomainRangePop(nvtx_realm_domain()); + } + } + + private: + bool active; + }; + + inline void nvtx_range_push(NvtxCategory *category, const char *message, + std::optional color = std::nullopt, + nvtx3::payload payload = nvtx_no_payload) + { + if(!category) { + return; + } + nvtx3::event_attributes attr = nvtx_make_event(*category, message, color, payload); + nvtxDomainRangePushEx(nvtx_realm_domain(), attr.get()); + } + + inline void nvtx_range_pop(void) { nvtxDomainRangePop(nvtx_realm_domain()); } + + inline nvtx3::range_handle nvtx_range_start(NvtxCategory *category, + const char *message, + std::optional color = std::nullopt, + nvtx3::payload payload = nvtx_no_payload) + { + if(!category) { + return nullptr; + } + nvtx3::event_attributes attr = nvtx_make_event(*category, message, color, payload); + return nvtx3::start_range_in(attr); + } + + inline void nvtx_range_end(nvtx3::range_handle id) + { + if(id) { + nvtx3::end_range_in(id); + } + } + + inline void nvtx_mark(NvtxCategory *category, const char *message, + std::optional color = std::nullopt, + nvtx3::payload payload = nvtx_no_payload) + { + if(!category) { + return; + } + nvtx3::event_attributes attr = nvtx_make_event(*category, message, color, payload); + nvtx3::mark_in(attr); + } }; // namespace Realm diff --git a/src/realm/threads.cc b/src/realm/threads.cc index aff85cf930e..a2fe56d767a 100644 --- a/src/realm/threads.cc +++ b/src/realm/threads.cc @@ -910,10 +910,6 @@ namespace Realm { if(thread->scheduler) thread->scheduler->thread_terminating(thread); -#ifdef REALM_USE_NVTX - finalize_nvtx_thread(); -#endif - return 0; } #endif @@ -946,10 +942,6 @@ namespace Realm { if(thread->scheduler) thread->scheduler->thread_terminating(thread); -#ifdef REALM_USE_NVTX - finalize_nvtx_thread(); -#endif - return 0; } #endif From 04eadfdc489db8d15c8feabfd39b6291bc7aa913 Mon Sep 17 00:00:00 2001 From: Wei Wu Date: Wed, 3 Jun 2026 23:39:28 -0700 Subject: [PATCH 3/7] color --- src/realm/nvtx.cc | 16 ++++++++++++++++ src/realm/nvtx.h | 7 ++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/realm/nvtx.cc b/src/realm/nvtx.cc index 9593c9a0966..3bb2e618cc2 100644 --- a/src/realm/nvtx.cc +++ b/src/realm/nvtx.cc @@ -98,6 +98,15 @@ namespace Realm { static atomic nvtx_proc_starting_category_id{1000}; + // Palette of visually distinct colors handed out by nvtx_get_next_color(). + static constexpr nvtx3::color nvtx_auto_color_palette[] = { + nvtx_color::red, nvtx_color::green, nvtx_color::blue, + nvtx_color::purple, nvtx_color::cyan, nvtx_color::maroon, + nvtx_color::navy, nvtx_color::magenta, nvtx_color::yellow, + nvtx_color::teal, nvtx_color::olive, nvtx_color::lawn_green, + }; + static atomic nvtx_next_color_index{0}; + // Create the category object, take ownership, and point its global handle at it. static void create_category(const std::string &name, const nvtx_category_def &def) { @@ -158,4 +167,11 @@ namespace Realm { return nvtx_proc_starting_category_id.fetch_add(1); } + nvtx3::color nvtx_get_next_color(void) + { + constexpr size_t n = + sizeof(nvtx_auto_color_palette) / sizeof(nvtx_auto_color_palette[0]); + return nvtx_auto_color_palette[nvtx_next_color_index.fetch_add(1) % n]; + } + }; // namespace Realm diff --git a/src/realm/nvtx.h b/src/realm/nvtx.h index ed48f0bb5b9..96b4856441f 100644 --- a/src/realm/nvtx.h +++ b/src/realm/nvtx.h @@ -124,9 +124,14 @@ namespace Realm { // Called by RuntimeImpl::wait_for_shutdown on the main thread. void finalize_nvtx(void); - // Called by an application to get the next available category id. + // Returns the next available category id. uint32_t nvtx_get_next_category_id(void); + // Returns the next auto-assigned color, cycling through a fixed palette of + // visually distinct colors. Handy for giving dynamically created categories + // distinguishable colors without picking one by hand. + nvtx3::color nvtx_get_next_color(void); + // Internal helpers for the inline annotation calls below. Kept in the header // (inline) so the thin wrappers fully inline at call sites; in particular the // common "disabled module" case (null category) collapses to nothing. From 7efb7029b7f92ed1493cc29e489b7f713e7d0356 Mon Sep 17 00:00:00 2001 From: Wei Wu Date: Thu, 4 Jun 2026 00:15:31 -0700 Subject: [PATCH 4/7] fix --- src/realm/cuda/cuda_module.cc | 5 +++++ src/realm/nvtx.cc | 4 +++- src/realm/proc_impl.cc | 13 ++++++++++++- src/realm/proc_impl.h | 11 +++++++++++ 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/realm/cuda/cuda_module.cc b/src/realm/cuda/cuda_module.cc index 4170b858146..d46d7d57624 100644 --- a/src/realm/cuda/cuda_module.cc +++ b/src/realm/cuda/cuda_module.cc @@ -1531,6 +1531,11 @@ namespace Realm { tte = &it->second; } +#ifdef REALM_USE_NVTX + std::string nvtx_msg = stringbuilder() << "task " << func_id; + nvtxScopedRange nvtx_range(nvtx_category.get(), nvtx_msg.c_str()); +#endif + if(tte->stream_aware_fnptr) { // shouldn't be here without a valid stream assert(ThreadLocal::current_gpu_stream != nullptr); diff --git a/src/realm/nvtx.cc b/src/realm/nvtx.cc index 3bb2e618cc2..548a6f8a743 100644 --- a/src/realm/nvtx.cc +++ b/src/realm/nvtx.cc @@ -25,6 +25,7 @@ #include #else #include +#include #include #endif @@ -120,7 +121,8 @@ namespace Realm { #ifdef REALM_ON_WINDOWS nvtxNameOsThread(GetCurrentThreadId(), thread_name); #else - nvtxNameOsThread(pthread_self(), thread_name); + // NVTX wants the OS-native (kernel) thread id, which on Linux is gettid(), + nvtxNameOsThread(static_cast(syscall(SYS_gettid)), thread_name); #endif } diff --git a/src/realm/proc_impl.cc b/src/realm/proc_impl.cc index 0f7f5fabbf7..505725f5426 100644 --- a/src/realm/proc_impl.cc +++ b/src/realm/proc_impl.cc @@ -475,7 +475,13 @@ namespace Realm { , me(_me) , kind(_kind) , num_cores(_num_cores) - {} + { +#ifdef REALM_USE_NVTX + std::string nvtx_name = stringbuilder() << me; + nvtx_category = std::make_unique( + nvtx_name, nvtx_get_next_category_id(), nvtx_get_next_color()); +#endif + } ProcessorImpl::~ProcessorImpl(void) {} @@ -1151,6 +1157,11 @@ namespace Realm { log_taskreg.debug() << "task " << func_id << " executing on " << me << ": " << ((void *)(tte.fnptr)); +#ifdef REALM_USE_NVTX + std::string nvtx_msg = stringbuilder() << "task " << func_id; + nvtxScopedRange nvtx_range(nvtx_category.get(), nvtx_msg.c_str()); +#endif + (tte.fnptr)(task_args.base(), task_args.size(), tte.user_data.base(), tte.user_data.size(), me); } diff --git a/src/realm/proc_impl.h b/src/realm/proc_impl.h index d8a3ae1078e..f914a343f53 100644 --- a/src/realm/proc_impl.h +++ b/src/realm/proc_impl.h @@ -37,6 +37,11 @@ #include "realm/threads.h" #include "realm/codedesc.h" +#ifdef REALM_USE_NVTX +#include "realm/nvtx.h" +#include +#endif + namespace Realm { class ProcessorGroupImpl; @@ -134,6 +139,12 @@ namespace Realm { Processor me; Processor::Kind kind; int num_cores; +#ifdef REALM_USE_NVTX + // Per-processor NVTX category, created at construction (on the main thread, + // before worker threads start). Tasks executed on this processor are + // annotated under it so they group per processor in NVTX tools. + std::unique_ptr nvtx_category; +#endif }; // generic local task processor - subclasses must create and configure a task From f654aa7a3c372fd42ac06db4f5ce7fbe05c7016c Mon Sep 17 00:00:00 2001 From: Wei Wu Date: Thu, 4 Jun 2026 00:25:57 -0700 Subject: [PATCH 5/7] check --- src/realm/tasks.cc | 17 +++++++++++++++++ src/realm/threads.cc | 4 ++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/realm/tasks.cc b/src/realm/tasks.cc index 308a0a6ed03..f4b503bbb04 100644 --- a/src/realm/tasks.cc +++ b/src/realm/tasks.cc @@ -21,6 +21,7 @@ #include "realm/runtime_impl.h" #include "realm/proc_impl.h" +#include "realm/utils.h" #if defined(REALM_USE_CACHING_ALLOCATOR) #include "realm/caching_allocator.h" @@ -1393,6 +1394,14 @@ namespace Realm { void KernelThreadTaskScheduler::thread_starting(Thread *thread) { +#ifdef REALM_USE_NVTX + // Name this OS (kernel) worker thread after the processor it serves so the + // proc's tasks show up under a clearly labeled row in NVTX tools. + std::string nvtx_thread_name = stringbuilder() + << "Realm " << Processor::get_kind_name(proc.kind()) << " " << proc; + init_nvtx_thread(nvtx_thread_name.c_str()); +#endif + log_sched.info() << "scheduler worker started: sched=" << this << " worker=" << thread; @@ -1673,6 +1682,14 @@ namespace Realm { void UserThreadTaskScheduler::host_thread_loop(void) { +#ifdef REALM_USE_NVTX + // With user threading the worker user threads are multiplexed on this host + // OS thread, so name the host thread after the processor it serves. + std::string nvtx_thread_name = stringbuilder() + << "Realm " << Processor::get_kind_name(proc.kind()) << " " << proc; + init_nvtx_thread(nvtx_thread_name.c_str()); +#endif + log_sched.debug() << "host thread started: sched=" << this << " thread=" << Thread::self(); AutoLock al(lock); diff --git a/src/realm/threads.cc b/src/realm/threads.cc index a2fe56d767a..ce4161dd667 100644 --- a/src/realm/threads.cc +++ b/src/realm/threads.cc @@ -838,7 +838,7 @@ namespace Realm { /*static*/ void *KernelThread::pthread_entry(void *data) { #ifdef REALM_USE_NVTX - init_nvtx_thread("RealmKernalThread"); + init_nvtx_thread("RealmKernelThread"); #endif KernelThread *thread = (KernelThread *)data; @@ -918,7 +918,7 @@ namespace Realm { /*static*/ DWORD WINAPI KernelThread::winthread_entry(LPVOID data) { #ifdef REALM_USE_NVTX - init_nvtx_thread("RealmKernalThread"); + init_nvtx_thread("RealmKernelThread"); #endif KernelThread *thread = (KernelThread *)data; From 55c1283cf82850087a7992fc1f9bdf71d15c3151 Mon Sep 17 00:00:00 2001 From: Wei Wu Date: Thu, 4 Jun 2026 12:35:05 -0700 Subject: [PATCH 6/7] fix --- src/realm/cuda/cuda_module.cc | 2 +- src/realm/nvtx.h | 20 ++++++++++++++++++++ src/realm/proc_impl.cc | 2 +- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/realm/cuda/cuda_module.cc b/src/realm/cuda/cuda_module.cc index d46d7d57624..df59952e7c0 100644 --- a/src/realm/cuda/cuda_module.cc +++ b/src/realm/cuda/cuda_module.cc @@ -1533,7 +1533,7 @@ namespace Realm { #ifdef REALM_USE_NVTX std::string nvtx_msg = stringbuilder() << "task " << func_id; - nvtxScopedRange nvtx_range(nvtx_category.get(), nvtx_msg.c_str()); + nvtxUniqueRange nvtx_range(nvtx_category.get(), nvtx_msg.c_str()); #endif if(tte->stream_aware_fnptr) { diff --git a/src/realm/nvtx.h b/src/realm/nvtx.h index 96b4856441f..eedee19cb82 100644 --- a/src/realm/nvtx.h +++ b/src/realm/nvtx.h @@ -211,6 +211,26 @@ namespace Realm { } } + // RAII range built on the Start/End API (nvtxDomainRangeStartEx/End) rather + // than Push/Pop. Start/End ranges are process-scoped, so NVTX tools display + // them grouped by category under a single domain row (and, for ranges whose + // start and end land on the same thread, also under that thread) instead of + // only nesting under the originating thread. A null `category` (disabled + // module) starts nothing and the destructor ends nothing. + struct [[nodiscard]] nvtxUniqueRange { + explicit nvtxUniqueRange(NvtxCategory *category, char const *message, + nvtx3::payload payload = nvtx_no_payload) + : handle(nvtx_range_start(category, message, std::nullopt, payload)) + {} + ~nvtxUniqueRange() { nvtx_range_end(handle); } + + nvtxUniqueRange(const nvtxUniqueRange &) = delete; + nvtxUniqueRange &operator=(const nvtxUniqueRange &) = delete; + + private: + nvtx3::range_handle handle; + }; + inline void nvtx_mark(NvtxCategory *category, const char *message, std::optional color = std::nullopt, nvtx3::payload payload = nvtx_no_payload) diff --git a/src/realm/proc_impl.cc b/src/realm/proc_impl.cc index 505725f5426..29375e50606 100644 --- a/src/realm/proc_impl.cc +++ b/src/realm/proc_impl.cc @@ -1159,7 +1159,7 @@ namespace Realm { #ifdef REALM_USE_NVTX std::string nvtx_msg = stringbuilder() << "task " << func_id; - nvtxScopedRange nvtx_range(nvtx_category.get(), nvtx_msg.c_str()); + nvtxUniqueRange nvtx_range(nvtx_category.get(), nvtx_msg.c_str()); #endif (tte.fnptr)(task_args.base(), task_args.size(), tte.user_data.base(), From 501083c5b18fadd04d3c69042e03e27852531d59 Mon Sep 17 00:00:00 2001 From: Wei Wu Date: Tue, 9 Jun 2026 12:51:32 -0700 Subject: [PATCH 7/7] checkpoint --- src/realm/proc_impl.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/realm/proc_impl.cc b/src/realm/proc_impl.cc index 29375e50606..1b1670aba8c 100644 --- a/src/realm/proc_impl.cc +++ b/src/realm/proc_impl.cc @@ -477,7 +477,8 @@ namespace Realm { , num_cores(_num_cores) { #ifdef REALM_USE_NVTX - std::string nvtx_name = stringbuilder() << me; + std::string nvtx_name = + stringbuilder() << Processor::get_kind_name(kind) << " " << me; nvtx_category = std::make_unique( nvtx_name, nvtx_get_next_category_id(), nvtx_get_next_color()); #endif