2020-09-04 14:35:05 -07:00
/*
* Copyright (c) 2020, NVIDIA CORPORATION.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* Temporary helper #defines, #undef'ed at end of header */
#define NVTX3_CPP_VERSION_MAJOR 1
#define NVTX3_CPP_VERSION_MINOR 0
/* This section handles the decision of whether to provide unversioned symbols.
* If NVTX3_CPP_REQUIRE_EXPLICIT_VERSION is #defined, unversioned symbols are
* not provided, and explicit-version symbols such as nvtx3::v1::thread_range
* and NVTX3_V1_FUNC_RANGE must be used. By default, the first #include of this
* header will define the unversioned symbols such as nvtx3::thread_range and
* NVTX3_FUNC_RANGE. Subsequently including a different major version of this
* header without #defining NVTX3_CPP_REQUIRE_EXPLICIT_VERSION triggers an error
* since the symbols would conflict. Subsequently including of a different
* minor version within the same major version is allowed. Functionality of
* minor versions is cumulative, regardless of include order.
*
* Since NVTX3_CPP_REQUIRE_EXPLICIT_VERSION allows all combinations of versions
* to coexist without problems within a translation unit, the recommended best
* practice for instrumenting header-based libraries with NVTX C++ Wrappers is
* is to #define NVTX3_CPP_REQUIRE_EXPLICIT_VERSION before including nvtx3.hpp,
* #undef it afterward, and only use explicit-version symbols. This is not
* necessary in common cases, such as instrumenting a standalone application, or
* static/shared libraries in .cpp files or headers private to those projects.
*/
/* clang-format off */
#if !defined(NVTX3_CPP_REQUIRE_EXPLICIT_VERSION)
/* Define macro used by all definitions in this header to indicate the
* unversioned symbols should be defined in addition to the versioned ones.
*/
#define NVTX3_INLINE_THIS_VERSION
#if !defined(NVTX3_CPP_INLINED_VERSION_MAJOR)
/* First occurrence of this header in the translation unit. Define macros
* indicating which version shall be used for unversioned symbols.
*/
/**
* @brief Semantic major version number for NVTX C++ wrappers of unversioned symbols
*
* Breaking changes may occur between major versions, and different major versions
* cannot provide unversioned symbols in the same translation unit (.cpp file).
*
* Note: If NVTX3_CPP_REQUIRE_EXPLICIT_VERSION is defined, this macro is not defined.
*
* Not to be confused with the version number of the NVTX core library.
*/
#define NVTX3_CPP_INLINED_VERSION_MAJOR 1 // NVTX3_CPP_VERSION_MAJOR
/**
* @brief Semantic minor version number for NVTX C++ wrappers of unversioned symbols
*
* No breaking changes occur between minor versions -- minor version changes within
* a major version are purely additive.
*
* Note: If NVTX3_CPP_REQUIRE_EXPLICIT_VERSION is defined, this macro is not defined.
*
* Not to be confused with the version number of the NVTX core library.
*/
#define NVTX3_CPP_INLINED_VERSION_MINOR 0 // NVTX3_CPP_VERSION_MINOR
#elif NVTX3_CPP_INLINED_VERSION_MAJOR != NVTX3_CPP_VERSION_MAJOR
/* Unsupported case -- cannot define unversioned symbols for different major versions
* in the same translation unit.
*/
#error \
"Two different major versions of the NVTX C++ Wrappers are being included in a single .cpp file, with unversioned symbols enabled in both. Only one major version can enable unversioned symbols in a .cpp file. To disable unversioned symbols, #define NVTX3_CPP_REQUIRE_EXPLICIT_VERSION before #including nvtx3.hpp, and use the explicit-version symbols instead -- this is the preferred way to use nvtx3.hpp from a header file."
#elif (NVTX3_CPP_INLINED_VERSION_MAJOR == NVTX3_CPP_VERSION_MAJOR) && \
(NVTX3_CPP_INLINED_VERSION_MINOR < NVTX3_CPP_VERSION_MINOR)
/* An older minor version of the same major version already defined unversioned
* symbols. The new features provided in this header will be inlined
* redefine the minor version macro to this header's version.
*/
#undef NVTX3_CPP_INLINED_VERSION_MINOR
#define NVTX3_CPP_INLINED_VERSION_MINOR 0 // NVTX3_CPP_VERSION_MINOR
// else, already have this version or newer, nothing to do
#endif
#endif
/* clang-format on */
#include <nvtx3/nvToolsExt.h>
2022-11-29 04:27:46 -08:00
#include <nvtx3/nvToolsExtPayload.h>
2020-09-04 14:35:05 -07:00
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
/**
* @file nvtx3.hpp
*
* @brief Provides C++ constructs making the NVTX library safer and easier to
* use with zero overhead.
*/
/**
* \mainpage
* \tableofcontents
*
* \section QUICK_START Quick Start
*
* To add NVTX ranges to your code, use the `nvtx3::thread_range` RAII object. A
* range begins when the object is created, and ends when the object is
* destroyed.
*
* \code{.cpp}
* #include "nvtx3.hpp"
* void some_function(){
* // Begins a NVTX range with the messsage "some_function"
* // The range ends when some_function() returns and `r` is destroyed
* nvtx3::thread_range r{"some_function"};
*
* for(int i = 0; i < 6; ++i){
* nvtx3::thread_range loop{"loop range"};
* std::this_thread::sleep_for(std::chrono::seconds{1});
* }
* } // Range ends when `r` is destroyed
* \endcode
*
* The example code above generates the following timeline view in Nsight
* Systems:
*
* \image html
* https://raw.githubusercontent.com/jrhemstad/nvtx_wrappers/master/docs/example_range.png
*
* Alternatively, use the \ref MACROS like `NVTX3_FUNC_RANGE()` to add
* ranges to your code that automatically use the name of the enclosing function
* as the range's message.
*
* \code{.cpp}
* #include "nvtx3.hpp"
* void some_function(){
* // Creates a range with a message "some_function" that ends when the
* enclosing
* // function returns
* NVTX3_FUNC_RANGE();
* ...
* }
* \endcode
*
*
* \section Overview
*
* The NVTX library provides a set of functions for users to annotate their code
* to aid in performance profiling and optimization. These annotations provide
* information to tools like Nsight Systems to improve visualization of
* application timelines.
*
* \ref RANGES are one of the most commonly used NVTX constructs for annotating
* a span of time. For example, imagine a user wanted to see every time a
* function, `my_function`, is called and how long it takes to execute. This can
* be accomplished with an NVTX range created on the entry to the function and
* terminated on return from `my_function` using the push/pop C APIs:
*
* ```
* void my_function(...){
* nvtxRangePushA("my_function"); // Begins NVTX range
* // do work
* nvtxRangePop(); // Ends NVTX range
* }
* ```
*
* One of the challenges with using the NVTX C API is that it requires manually
* terminating the end of the range with `nvtxRangePop`. This can be challenging
* if `my_function()` has multiple returns or can throw exceptions as it
* requires calling `nvtxRangePop()` before all possible return points.
*
* NVTX++ solves this inconvenience through the "RAII" technique by providing a
* `nvtx3::thread_range` class that begins a range at construction and ends the
* range on destruction. The above example then becomes:
*
* ```
* void my_function(...){
* nvtx3::thread_range r{"my_function"}; // Begins NVTX range
* // do work
* } // Range ends on exit from `my_function` when `r` is destroyed
* ```
*
* The range object `r` is deterministically destroyed whenever `my_function`
* returns---ending the NVTX range without manual intervention. For more
* information, see \ref RANGES and `nvtx3::domain_thread_range`.
*
* Another inconvenience of the NVTX C APIs are the several constructs where the
* user is expected to initialize an object at the beginning of an application
* and reuse that object throughout the lifetime of the application. For example
* Domains, Categories, and Registered messages.
*
* Example:
* ```
* nvtxDomainHandle_t D = nvtxDomainCreateA("my domain");
* // Reuse `D` throughout the rest of the application
* ```
*
* This can be problematic if the user application or library does not have an
* explicit initialization function called before all other functions to
* ensure that these long-lived objects are initialized before being used.
*
* NVTX++ makes use of the "construct on first use" technique to alleviate this
* inconvenience. In short, a function local static object is constructed upon
* the first invocation of a function and returns a reference to that object on
* all future invocations. See the documentation for
* `nvtx3::registered_string`, `nvtx3::domain`, `nvtx3::named_category`, and
* https://isocpp.org/wiki/faq/ctors#static-init-order-on-first-use for more
* information.
*
* Using construct on first use, the above example becomes:
* ```
* struct my_domain{ static constexpr char const* name{"my domain"}; };
*
* // The first invocation of `domain::get` for the type `my_domain` will
* // construct a `nvtx3::domain` object and return a reference to it. Future
* // invocations simply return a reference.
* nvtx3::domain const& D = nvtx3::domain::get<my_domain>();
* ```
* For more information about NVTX and how it can be used, see
* https://docs.nvidia.com/cuda/profiler-users-guide/index.html#nvtx and
* https://devblogs.nvidia.com/cuda-pro-tip-generate-custom-application-profile-timelines-nvtx/
* for more information.
*
* \section RANGES Ranges
*
* Ranges are used to describe a span of time during the execution of an
* application. Common examples are using ranges to annotate the time it takes
* to execute a function or an iteration of a loop.
*
* NVTX++ uses RAII to automate the generation of ranges that are tied to the
* lifetime of objects. Similar to `std::lock_guard` in the C++ Standard
* Template Library.
*
* \subsection THREAD_RANGE Thread Range
*
* `nvtx3::domain_thread_range` is a class that begins a range upon construction
* and ends the range at destruction. This is one of the most commonly used
* constructs in NVTX++ and is useful for annotating spans of time on a
* particular thread. These ranges can be nested to arbitrary depths.
*
* `nvtx3::thread_range` is an alias for a `nvtx3::domain_thread_range` in the
* global NVTX domain. For more information about Domains, see \ref DOMAINS.
*
* Various attributes of a range can be configured constructing a
* `nvtx3::domain_thread_range` with a `nvtx3::event_attributes` object. For
* more information, see \ref ATTRIBUTES.
*
* Example:
*
* \code{.cpp}
* void some_function(){
* // Creates a range for the duration of `some_function`
* nvtx3::thread_range r{};
*
* while(true){
* // Creates a range for every loop iteration
* // `loop_range` is nested inside `r`
* nvtx3::thread_range loop_range{};
* }
* }
* \endcode
*
* \subsection PROCESS_RANGE Process Range
*
* `nvtx3::domain_process_range` is identical to `nvtx3::domain_thread_range`
* with the exception that a `domain_process_range` can be created and destroyed
* on different threads. This is useful to annotate spans of time that can
* bridge multiple threads.
*
* `nvtx3::domain_thread_range`s should be preferred unless one needs the
* ability to begin and end a range on different threads.
*
* \section MARKS Marks
*
* `nvtx3::mark` allows annotating an instantaneous event in an application's
* timeline. For example, indicating when a mutex is locked or unlocked.
*
* \code{.cpp}
* std::mutex global_lock;
* void lock_mutex(){
* global_lock.lock();
* // Marks an event immediately after the mutex is locked
* nvtx3::mark<my_domain>("lock_mutex");
* }
* \endcode
*
* \section DOMAINS Domains
*
* Similar to C++ namespaces, Domains allow for scoping NVTX events. By default,
* all NVTX events belong to the "global" domain. Libraries and applications
* should scope their events to use a custom domain to differentiate where the
* events originate from.
*
* It is common for a library or application to have only a single domain and
* for the name of that domain to be known at compile time. Therefore, Domains
* in NVTX++ are represented by _tag types_.
*
* For example, to define a custom domain, simply define a new concrete type
* (a `class` or `struct`) with a `static` member called `name` that contains
* the desired name of the domain.
*
* ```
* struct my_domain{ static constexpr char const* name{"my domain"}; };
* ```
*
* For any NVTX++ construct that can be scoped to a domain, the type `my_domain`
* can be passed as an explicit template argument to scope it to the custom
* domain.
*
* The tag type `nvtx3::domain::global` represents the global NVTX domain.
*
* \code{.cpp}
* // By default, `domain_thread_range` belongs to the global domain
* nvtx3::domain_thread_range<> r0{};
*
* // Alias for a `domain_thread_range` in the global domain
* nvtx3::thread_range r1{};
*
* // `r` belongs to the custom domain
* nvtx3::domain_thread_range<my_domain> r{};
* \endcode
*
* When using a custom domain, it is reccomended to define type aliases for NVTX
* constructs in the custom domain.
* ```
* using my_thread_range = nvtx3::domain_thread_range<my_domain>;
* using my_registered_string = nvtx3::registered_string<my_domain>;
* using my_named_category = nvtx3::named_category<my_domain>;
* ```
*
* See `nvtx3::domain` for more information.
*
* \section ATTRIBUTES Event Attributes
*
* NVTX events can be customized with various attributes to provide additional
* information (such as a custom message) or to control visualization of the
* event (such as the color used). These attributes can be specified per-event
* via arguments to a `nvtx3::event_attributes` object.
*
* NVTX events can be customized via four "attributes":
* - \ref COLOR : color used to visualize the event in tools.
* - \ref MESSAGES : Custom message string.
* - \ref PAYLOAD : User-defined numerical value.
* - \ref CATEGORY : Intra-domain grouping.
*
* It is possible to construct a `nvtx3::event_attributes` from any number of
* attribute objects (nvtx3::color, nvtx3::message, nvtx3::payload,
* nvtx3::category) in any order. If an attribute is not specified, a tool
* specific default value is used. See `nvtx3::event_attributes` for more
* information.
*
* \code{.cpp}
* // Custom color, message
* event_attributes attr{nvtx3::rgb{127, 255, 0},
* "message"};
*
* // Custom color, message, payload, category
* event_attributes attr{nvtx3::rgb{127, 255, 0},
* nvtx3::payload{42},
* "message",
* nvtx3::category{1}};
*
* // Arguments can be in any order
* event_attributes attr{nvtx3::payload{42},
* nvtx3::category{1},
* "message",
* nvtx3::rgb{127, 255, 0}};
*
* // "First wins" with multiple arguments of the same type
* event_attributes attr{ nvtx3::payload{42}, nvtx3::payload{7} }; // payload is
* 42 \endcode
*
* \subsection MESSAGES message
*
* A `nvtx3::message` allows associating a custom message string with an NVTX
* event.
*
* Example:
* \code{.cpp}
* // Create an `event_attributes` with the custom message "my message"
* nvtx3::event_attributes attr{nvtx3::Mesage{"my message"}};
*
* // strings and string literals implicitly assumed to be a `nvtx3::message`
* nvtx3::event_attributes attr{"my message"};
* \endcode
*
* \subsubsection REGISTERED_MESSAGE Registered Messages
*
* Associating a `nvtx3::message` with an event requires copying the contents of
* the message every time the message is used, i.e., copying the entire message
* string. This may cause non-trivial overhead in performance sensitive code.
*
* To eliminate this overhead, NVTX allows registering a message string,
* yielding a "handle" that is inexpensive to copy that may be used in place of
* a message string. When visualizing the events, tools such as Nsight Systems
* will take care of mapping the message handle to its string.
*
* A message should be registered once and the handle reused throughout the rest
* of the application. This can be done by either explicitly creating static
* `nvtx3::registered_string` objects, or using the
* `nvtx3::registered_string::get` construct on first use helper (recommended).
*
* Similar to \ref DOMAINS, `nvtx3::registered_string::get` requires defining a
* custom tag type with a static `message` member whose value will be the
* contents of the registered string.
*
* Example:
* \code{.cpp}
* // Explicitly constructed, static `registered_string`
* static registered_string<my_domain> static_message{"my message"};
*
* // Or use construct on first use:
* // Define a tag type with a `message` member string to register
* struct my_message{ static constexpr char const* message{ "my message" }; };
*
* // Uses construct on first use to register the contents of
* // `my_message::message`
* nvtx3::registered_string<my_domain> const& msg =
* nvtx3::registered_string<my_domain>::get<my_message>(); \endcode
*
* \subsection COLOR color
*
* Associating a `nvtx3::color` with an event allows controlling how the event
* is visualized in a tool such as Nsight Systems. This is a convenient way to
* visually differentiate among different events.
*
* \code{.cpp}
* // Define a color via rgb color values
* nvtx3::color c{nvtx3::rgb{127, 255, 0}};
* nvtx3::event_attributes attr{c};
*
* // rgb color values can be passed directly to an `event_attributes`
* nvtx3::event_attributes attr1{nvtx3::rgb{127,255,0}};
* \endcode
*
* \subsection CATEGORY category
*
* A `nvtx3::category` is simply an integer id that allows for fine-grain
* grouping of NVTX events. For example, one might use separate categories for
* IO, memory allocation, compute, etc.
*
* \code{.cpp}
* nvtx3::event_attributes{nvtx3::category{1}};
* \endcode
*
* \subsubsection NAMED_CATEGORIES Named Categories
*
* Associates a `name` string with a category `id` to help differentiate among
* categories.
*
* For any given category id `Id`, a `named_category{Id, "name"}` should only
* be constructed once and reused throughout an application. This can be done by
* either explicitly creating static `nvtx3::named_category` objects, or using
* the `nvtx3::named_category::get` construct on first use helper (recommended).
*
* Similar to \ref DOMAINS, `nvtx3::named_category::get` requires defining a
* custom tag type with static `name` and `id` members.
*
* \code{.cpp}
* // Explicitly constructed, static `named_category`
* static nvtx3::named_category static_category{42, "my category"};
*
* // OR use construct on first use:
* // Define a tag type with `name` and `id` members
* struct my_category{
* static constexpr char const* name{"my category"}; // category name
* static constexpr category::id_type id{42}; // category id
* };
*
* // Use construct on first use to name the category id `42`
* // with name "my category"
* nvtx3::named_category const& my_category =
* named_category<my_domain>::get<my_category>();
*
* // Range `r` associated with category id `42`
* nvtx3::event_attributes attr{my_category};
* \endcode
*
* \subsection PAYLOAD payload
*
* Allows associating a user-defined numerical value with an event.
*
* ```
* nvtx3:: event_attributes attr{nvtx3::payload{42}}; // Constructs a payload
* from
* // the `int32_t` value 42
* ```
*
*
* \section EXAMPLE Example
*
* Putting it all together:
* \code{.cpp}
* // Define a custom domain tag type
* struct my_domain{ static constexpr char const* name{"my domain"}; };
*
* // Define a named category tag type
* struct my_category{
* static constexpr char const* name{"my category"};
* static constexpr uint32_t id{42};
* };
*
* // Define a registered string tag type
* struct my_message{ static constexpr char const* message{"my message"}; };
*
* // For convenience, use aliases for domain scoped objects
* using my_thread_range = nvtx3::domain_thread_range<my_domain>;
* using my_registered_string = nvtx3::registered_string<my_domain>;
* using my_named_category = nvtx3::named_category<my_domain>;
*
* // Default values for all attributes
* nvtx3::event_attributes attr{};
* my_thread_range r0{attr};
*
* // Custom (unregistered) message, and unnamed category
* nvtx3::event_attributes attr1{"message", nvtx3::category{2}};
* my_thread_range r1{attr1};
*
* // Alternatively, pass arguments of `event_attributes` ctor directly to
* // `my_thread_range`
* my_thread_range r2{"message", nvtx3::category{2}};
*
* // construct on first use a registered string
* auto msg = my_registered_string::get<my_message>();
*
* // construct on first use a named category
* auto category = my_named_category::get<my_category>();
*
* // Use registered string and named category
* my_thread_range r3{msg, category, nvtx3::rgb{127, 255, 0},
* nvtx3::payload{42}};
*
* // Any number of arguments in any order
* my_thread_range r{nvtx3::rgb{127, 255,0}, msg};
*
* \endcode
* \section MACROS Convenience Macros
*
* Oftentimes users want to quickly and easily add NVTX ranges to their library
* or application to aid in profiling and optimization.
*
* A convenient way to do this is to use the \ref NVTX3_FUNC_RANGE and
* \ref NVTX3_FUNC_RANGE_IN macros. These macros take care of constructing an
* `nvtx3::domain_thread_range` with the name of the enclosing function as the
* range's message.
*
* \code{.cpp}
* void some_function(){
* // Automatically generates an NVTX range for the duration of the function
* // using "some_function" as the event's message.
* NVTX3_FUNC_RANGE();
* }
* \endcode
*
*/
/* Temporary helper #defines, removed with #undef at end of header */
/* Within this header, nvtx3::NVTX3_VERSION_NAMESPACE resolves to nvtx3::vX,
* where "X" is the major version number. */
#define NVTX3_CONCAT(A, B) A##B
#define NVTX3_NAMESPACE_FOR(VERSION) NVTX3_CONCAT(v, VERSION)
#define NVTX3_VERSION_NAMESPACE NVTX3_NAMESPACE_FOR(NVTX3_CPP_VERSION_MAJOR)
/* Avoid duplicating #if defined(NVTX3_INLINE_THIS_VERSION) for namespaces
* in each minor version by making a macro to use unconditionally, which
* resolves to "inline" or nothing as appropriate. */
#if defined(NVTX3_INLINE_THIS_VERSION)
#define NVTX3_INLINE_IF_REQUESTED inline
#else
#define NVTX3_INLINE_IF_REQUESTED
#endif
/* Enables the use of constexpr when support for C++14 relaxed constexpr
* is present.
*
* Initializing a legacy-C (i.e., no constructor) union member requires
* initializing in the constructor body. Non-empty constexpr constructors
* require C++14 relaxed constexpr. In strict C++11 compilation, fall back
* to using non-constexpr constructors for classes with union members.
*/
#if __cpp_constexpr >= 201304L
#define NVTX3_RELAXED_CONSTEXPR constexpr
#else
#define NVTX3_RELAXED_CONSTEXPR
#endif
/* Implementation sections, enclosed in guard macros for each minor version */
#ifndef NVTX3_CPP_DEFINITIONS_V1_0
#define NVTX3_CPP_DEFINITIONS_V1_0
namespace nvtx3 {
NVTX3_INLINE_IF_REQUESTED namespace NVTX3_VERSION_NAMESPACE
{
namespace detail {
/**
* @brief Verifies if a type `T` contains a member `T::name` of type `const
* char*` or `const wchar_t*`.
*
* @tparam T The type to verify
* @return True if `T` contains a member `T::name` of type `const char*` or
* `const wchar_t*`.
*/
template < typename T >
constexpr auto has_name_member () noexcept -> decltype ( T :: name , bool ())
{
return ( std :: is_same < char const * , typename std :: decay < decltype ( T :: name ) >:: type >:: value ||
std :: is_same < wchar_t const * , typename std :: decay < decltype ( T :: name ) >:: type >:: value );
}
} // namespace detail
/**
* @brief `domain`s allow for grouping NVTX events into a single scope to
* differentiate them from events in other `domain`s.
*
* By default, all NVTX constructs are placed in the "global" NVTX domain.
*
* A custom `domain` may be used in order to differentiate a library's or
* application's NVTX events from other events.
*
* `domain`s are expected to be long-lived and unique to a library or
* application. As such, it is assumed a domain's name is known at compile
* time. Therefore, all NVTX constructs that can be associated with a domain
* require the domain to be specified via a *type* `DomainName` passed as an
* explicit template parameter.
*
* The type `domain::global` may be used to indicate that the global NVTX
* domain should be used.
*
* None of the C++ NVTX constructs require the user to manually construct a
* `domain` object. Instead, if a custom domain is desired, the user is
* expected to define a type `DomainName` that contains a member
* `DomainName::name` which resolves to either a `char const*` or `wchar_t
* const*`. The value of `DomainName::name` is used to name and uniquely
* identify the custom domain.
*
* Upon the first use of an NVTX construct associated with the type
* `DomainName`, the "construct on first use" pattern is used to construct a
* function local static `domain` object. All future NVTX constructs
* associated with `DomainType` will use a reference to the previously
* constructed `domain` object. See `domain::get`.
*
* Example:
* ```
* // The type `my_domain` defines a `name` member used to name and identify
* the
* // `domain` object identified by `my_domain`.
* struct my_domain{ static constexpr char const* name{"my_domain"}; };
*
* // The NVTX range `r` will be grouped with all other NVTX constructs
* // associated with `my_domain`.
* nvtx3::domain_thread_range<my_domain> r{};
*
* // An alias can be created for a `domain_thread_range` in the custom domain
* using my_thread_range = nvtx3::domain_thread_range<my_domain>;
* my_thread_range my_range{};
*
* // `domain::global` indicates that the global NVTX domain is used
* nvtx3::domain_thread_range<domain::global> r2{};
*
* // For convenience, `nvtx3::thread_range` is an alias for a range in the
* // global domain
* nvtx3::thread_range r3{};
* ```
*/
class domain {
public :
domain ( domain const & ) = delete ;
domain & operator = ( domain const & ) = delete ;
domain ( domain && ) = delete ;
domain & operator = ( domain && ) = delete ;
/**
* @brief Returns reference to an instance of a function local static
* `domain` object.
*
* Uses the "construct on first use" idiom to safely ensure the `domain`
* object is initialized exactly once upon first invocation of
* `domain::get<DomainName>()`. All following invocations will return a
* reference to the previously constructed `domain` object. See
* https://isocpp.org/wiki/faq/ctors#static-init-order-on-first-use
*
* None of the constructs in this header require the user to directly invoke
* `domain::get`. It is automatically invoked when constructing objects like
* a `domain_thread_range` or `category`. Advanced users may wish to use
* `domain::get` for the convenience of the "construct on first use" idiom
* when using domains with their own use of the NVTX C API.
*
* This function is threadsafe as of C++11. If two or more threads call
* `domain::get<DomainName>` concurrently, exactly one of them is guaranteed
* to construct the `domain` object and the other(s) will receive a
* reference to the object after it is fully constructed.
*
* The domain's name is specified via the type `DomainName` pass as an
* explicit template parameter. `DomainName` is required to contain a
* member `DomainName::name` that resolves to either a `char const*` or
* `wchar_t const*`. The value of `DomainName::name` is used to name and
* uniquely identify the `domain`.
*
* Example:
* ```
* // The type `my_domain` defines a `name` member used to name and identify
* // the `domain` object identified by `my_domain`.
* struct my_domain{ static constexpr char const* name{"my domain"}; };
*
* auto D = domain::get<my_domain>(); // First invocation constructs a
* // `domain` with the name "my domain"
*
* auto D1 = domain::get<my_domain>(); // Simply returns reference to
* // previously constructed `domain`.
* ```
*
* @tparam DomainName Type that contains a `DomainName::name` member used to
* name the `domain` object.
* @return Reference to the `domain` corresponding to the type `DomainName`.
*/
template < typename DomainName >
static domain const & get ()
{
static_assert ( detail :: has_name_member < DomainName > (),
"Type used to identify a domain must contain a name member of"
"type const char* or const wchar_t*" );
static domain const d { DomainName :: name };
return d ;
}
/**
* @brief Conversion operator to `nvtxDomainHandle_t`.
*
* Allows transparently passing a domain object into an API expecting a
* native `nvtxDomainHandle_t` object.
*/
operator nvtxDomainHandle_t () const noexcept { return _domain ; }
/**
* @brief Tag type for the "global" NVTX domain.
*
* This type may be passed as a template argument to any function/class
* expecting a type to identify a domain to indicate that the global domain
* should be used.
*
* All NVTX events in the global domain across all libraries and
* applications will be grouped together.
*
*/
struct global {
};
private :
/**
* @brief Construct a new domain with the specified `name`.
*
* This constructor is private as it is intended that `domain` objects only
* be created through the `domain::get` function.
*
* @param name A unique name identifying the domain
*/
explicit domain ( char const * name ) noexcept : _domain { nvtxDomainCreateA ( name )} {}
/**
* @brief Construct a new domain with the specified `name`.
*
* This constructor is private as it is intended that `domain` objects only
* be created through the `domain::get` function.
*
* @param name A unique name identifying the domain
*/
explicit domain ( wchar_t const * name ) noexcept : _domain { nvtxDomainCreateW ( name )} {}
/**
* @brief Construct a new domain with the specified `name`.
*
* This constructor is private as it is intended that `domain` objects only
* be created through the `domain::get` function.
*
* @param name A unique name identifying the domain
*/
explicit domain ( std :: string const & name ) noexcept : domain { name . c_str ()} {}
/**
* @brief Construct a new domain with the specified `name`.
*
* This constructor is private as it is intended that `domain` objects only
* be created through the `domain::get` function.
*
* @param name A unique name identifying the domain
*/
explicit domain ( std :: wstring const & name ) noexcept : domain { name . c_str ()} {}
/**
* @brief Default constructor creates a `domain` representing the
* "global" NVTX domain.
*
* All events not associated with a custom `domain` are grouped in the
* "global" NVTX domain.
*
*/
domain () = default ;
/**
* @brief Intentionally avoid calling nvtxDomainDestroy on the `domain` object.
*
* No currently-available tools attempt to free domain resources when the
* nvtxDomainDestroy function is called, due to the thread-safety and
* efficiency challenges of freeing thread-local storage for other threads.
* Since libraries may be disallowed from introducing static destructors,
* and destroying the domain is likely to have no effect, the destructor
* for `domain` intentionally chooses to not destroy the domain.
*
* In a situation where domain destruction is necessary, either manually
* call nvtxDomainDestroy on the domain's handle, or make a class that
* derives from `domain` and calls nvtxDomainDestroy in its destructor.
*/
~ domain () = default ;
private :
nvtxDomainHandle_t const _domain {}; ///< The `domain`s NVTX handle
};
/**
* @brief Returns reference to the `domain` object that represents the global
* NVTX domain.
*
* This specialization for `domain::global` returns a default constructed,
* `domain` object for use when the "global" domain is desired.
*
* All NVTX events in the global domain across all libraries and applications
* will be grouped together.
*
* @return Reference to the `domain` corresponding to the global NVTX domain.
*
*/
template <>
inline domain const & domain :: get < domain :: global > ()
{
static domain const d {};
return d ;
}
/**
* @brief Indicates the values of the red, green, blue color channels for
* a rgb color code.
*
*/
struct rgb {
/// Type used for component values
using component_type = uint8_t ;
/**
* @brief Construct a rgb with red, green, and blue channels
* specified by `red_`, `green_`, and `blue_`, respectively.
*
* Valid values are in the range `[0,255]`.
*
* @param red_ Value of the red channel
* @param green_ Value of the green channel
* @param blue_ Value of the blue channel
*/
constexpr rgb ( component_type red_ , component_type green_ , component_type blue_ ) noexcept
: red { red_ }, green { green_ }, blue { blue_ }
{
}
component_type const red {}; ///< Red channel value
component_type const green {}; ///< Green channel value
component_type const blue {}; ///< Blue channel value
};
/**
* @brief Indicates the value of the alpha, red, green, and blue color
* channels for an argb color code.
*
*/
struct argb final : rgb {
/**
* @brief Construct an argb with alpha, red, green, and blue channels
* specified by `alpha_`, `red_`, `green_`, and `blue_`, respectively.
*
* Valid values are in the range `[0,255]`.
*
* @param alpha_ Value of the alpha channel (opacity)
* @param red_ Value of the red channel
* @param green_ Value of the green channel
* @param blue_ Value of the blue channel
*
*/
constexpr argb ( component_type alpha_ ,
component_type red_ ,
component_type green_ ,
component_type blue_ ) noexcept
: rgb { red_ , green_ , blue_ }, alpha { alpha_ }
{
}
component_type const alpha {}; ///< Alpha channel value
};
/**
* @brief Represents a custom color that can be associated with an NVTX event
* via it's `event_attributes`.
*
* Specifying colors for NVTX events is a convenient way to visually
* differentiate among different events in a visualization tool such as Nsight
* Systems.
*
*/
class color {
public :
/// Type used for the color's value
using value_type = uint32_t ;
/**
* @brief Constructs a `color` using the value provided by `hex_code`.
*
* `hex_code` is expected to be a 4 byte argb hex code.
*
* The most significant byte indicates the value of the alpha channel
* (opacity) (0-255)
*
* The next byte indicates the value of the red channel (0-255)
*
* The next byte indicates the value of the green channel (0-255)
*
* The least significant byte indicates the value of the blue channel
* (0-255)
*
* @param hex_code The hex code used to construct the `color`
*/
constexpr explicit color ( value_type hex_code ) noexcept : _value { hex_code } {}
/**
* @brief Construct a `color` using the alpha, red, green, blue components
* in `argb`.
*
* @param argb The alpha, red, green, blue components of the desired `color`
*/
constexpr color ( argb argb ) noexcept
: color { from_bytes_msb_to_lsb ( argb . alpha , argb . red , argb . green , argb . blue )}
{
}
/**
* @brief Construct a `color` using the red, green, blue components in
* `rgb`.
*
* Uses maximum value for the alpha channel (opacity) of the `color`.
*
* @param rgb The red, green, blue components of the desired `color`
*/
constexpr color ( rgb rgb ) noexcept
: color { from_bytes_msb_to_lsb ( 0xFF , rgb . red , rgb . green , rgb . blue )}
{
}
/**
* @brief Returns the `color`s argb hex code
*
*/
constexpr value_type get_value () const noexcept { return _value ; }
/**
* @brief Return the NVTX color type of the color.
*
*/
constexpr nvtxColorType_t get_type () const noexcept { return _type ; }
color () = delete ;
~ color () = default ;
color ( color const & ) = default ;
color & operator = ( color const & ) = default ;
color ( color && ) = default ;
color & operator = ( color && ) = default ;
private :
/**
* @brief Constructs an unsigned, 4B integer from the component bytes in
* most to least significant byte order.
*
*/
constexpr static value_type from_bytes_msb_to_lsb ( uint8_t byte3 ,
uint8_t byte2 ,
uint8_t byte1 ,
uint8_t byte0 ) noexcept
{
return uint32_t { byte3 } << 24 | uint32_t { byte2 } << 16 | uint32_t { byte1 } << 8 | uint32_t { byte0 };
}
value_type const _value {}; ///< color's argb color code
nvtxColorType_t const _type { NVTX_COLOR_ARGB }; ///< NVTX color type code
};
/**
* @brief Object for intra-domain grouping of NVTX events.
*
* A `category` is simply an integer id that allows for fine-grain grouping of
* NVTX events. For example, one might use separate categories for IO, memory
* allocation, compute, etc.
*
* Example:
* \code{.cpp}
* nvtx3::category cat1{1};
*
* // Range `r1` belongs to the category identified by the value `1`.
* nvtx3::thread_range r1{cat1};
*
* // Range `r2` belongs to the same category as `r1`
* nvtx3::thread_range r2{nvtx3::category{1}};
* \endcode
*
* To associate a name string with a category id, see `named_category`.
*
*/
class category {
public :
/// Type used for `category`s integer id.
using id_type = uint32_t ;
/**
* @brief Construct a `category` with the specified `id`.
*
* The `category` will be unnamed and identified only by its `id` value.
*
* All `category` objects sharing the same `id` are equivalent.
*
* @param[in] id The `category`'s identifying value
*/
constexpr explicit category ( id_type id ) noexcept : id_ { id } {}
/**
* @brief Returns the id of the category.
*
*/
constexpr id_type get_id () const noexcept { return id_ ; }
category () = delete ;
~ category () = default ;
category ( category const & ) = default ;
category & operator = ( category const & ) = default ;
category ( category && ) = default ;
category & operator = ( category && ) = default ;
private :
id_type const id_ {}; ///< category's unique identifier
};
/**
* @brief A `category` with an associated name string.
*
* Associates a `name` string with a category `id` to help differentiate among
* categories.
*
* For any given category id `Id`, a `named_category(Id, "name")` should only
* be constructed once and reused throughout an application. This can be done
* by either explicitly creating static `named_category` objects, or using the
* `named_category::get` construct on first use helper (recommended).
*
* Creating two or more `named_category` objects with the same value for `id`
* in the same domain results in undefined behavior.
*
* Similarly, behavior is undefined when a `named_category` and `category`
* share the same value of `id`.
*
* Example:
* \code{.cpp}
* // Explicitly constructed, static `named_category`
* static nvtx3::named_category static_category{42, "my category"};
*
* // Range `r` associated with category id `42`
* nvtx3::thread_range r{static_category};
*
* // OR use construct on first use:
*
* // Define a type with `name` and `id` members
* struct my_category{
* static constexpr char const* name{"my category"}; // category name
* static constexpr category::id_type id{42}; // category id
* };
*
* // Use construct on first use to name the category id `42`
* // with name "my category"
* auto my_category = named_category<my_domain>::get<my_category>();
*
* // Range `r` associated with category id `42`
* nvtx3::thread_range r{my_category};
* \endcode
*
* `named_category`'s association of a name to a category id is local to the
* domain specified by the type `D`. An id may have a different name in
* another domain.
*
* @tparam D Type containing `name` member used to identify the `domain` to
* which the `named_category` belongs. Else, `domain::global` to indicate
* that the global NVTX domain should be used.
*/
template < typename D = domain :: global >
class named_category final : public category {
public :
/**
* @brief Returns a global instance of a `named_category` as a
* function-local static.
*
* Creates a `named_category` with name and id specified by the contents of
* a type `C`. `C::name` determines the name and `C::id` determines the
* category id.
*
* This function is useful for constructing a named `category` exactly once
* and reusing the same instance throughout an application.
*
* Example:
* \code{.cpp}
* // Define a type with `name` and `id` members
* struct my_category{
* static constexpr char const* name{"my category"}; // category name
* static constexpr uint32_t id{42}; // category id
* };
*
* // Use construct on first use to name the category id `42`
* // with name "my category"
* auto cat = named_category<my_domain>::get<my_category>();
*
* // Range `r` associated with category id `42`
* nvtx3::thread_range r{cat};
* \endcode
*
* Uses the "construct on first use" idiom to safely ensure the `category`
* object is initialized exactly once. See
* https://isocpp.org/wiki/faq/ctors#static-init-order-on-first-use
*
* @tparam C Type containing a member `C::name` that resolves to either a
* `char const*` or `wchar_t const*` and `C::id`.
*/
template < typename C >
static named_category < D > const & get () noexcept
{
static_assert ( detail :: has_name_member < C > (),
"Type used to name a category must contain a name member." );
static named_category < D > const category { C :: id , C :: name };
return category ;
}
/**
* @brief Construct a `category` with the specified `id` and `name`.
*
* The name `name` will be registered with `id`.
*
* Every unique value of `id` should only be named once.
*
* @param[in] id The category id to name
* @param[in] name The name to associated with `id`
*/
named_category ( id_type id , char const * name ) noexcept : category { id }
{
#ifndef NVTX_DISABLE
nvtxDomainNameCategoryA ( domain :: get < D > (), get_id (), name );
#else
( void ) id ;
( void ) name ;
#endif
};
/**
* @brief Construct a `category` with the specified `id` and `name`.
*
* The name `name` will be registered with `id`.
*
* Every unique value of `id` should only be named once.
*
* @param[in] id The category id to name
* @param[in] name The name to associated with `id`
*/
named_category ( id_type id , wchar_t const * name ) noexcept : category { id }
{
#ifndef NVTX_DISABLE
nvtxDomainNameCategoryW ( domain :: get < D > (), get_id (), name );
#else
( void ) id ;
( void ) name ;
#endif
};
};
/**
* @brief A message registered with NVTX.
*
* Normally, associating a `message` with an NVTX event requires copying the
* contents of the message string. This may cause non-trivial overhead in
* highly performance sensitive regions of code.
*
* message registration is an optimization to lower the overhead of
* associating a message with an NVTX event. Registering a message yields a
* handle that is inexpensive to copy that may be used in place of a message
* string.
*
* A particular message should only be registered once and the handle
* reused throughout the rest of the application. This can be done by either
* explicitly creating static `registered_string` objects, or using the
* `registered_string::get` construct on first use helper (recommended).
*
* Example:
* \code{.cpp}
* // Explicitly constructed, static `registered_string`
* static registered_string<my_domain> static_message{"message"};
*
* // "message" is associated with the range `r`
* nvtx3::thread_range r{static_message};
*
* // Or use construct on first use:
*
* // Define a type with a `message` member that defines the contents of the
* // registered string
* struct my_message{ static constexpr char const* message{ "my message" }; };
*
* // Uses construct on first use to register the contents of
* // `my_message::message`
* auto msg = registered_string<my_domain>::get<my_message>();
*
* // "my message" is associated with the range `r`
* nvtx3::thread_range r{msg};
* \endcode
*
* `registered_string`s are local to a particular domain specified via
* the type `D`.
*
* @tparam D Type containing `name` member used to identify the `domain` to
* which the `registered_string` belongs. Else, `domain::global` to indicate
* that the global NVTX domain should be used.
*/
template < typename D = domain :: global >
class registered_string {
public :
/**
* @brief Returns a global instance of a `registered_string` as a function
* local static.
*
* Provides a convenient way to register a message with NVTX without having
* to explicitly register the message.
*
* Upon first invocation, constructs a `registered_string` whose contents
* are specified by `message::message`.
*
* All future invocations will return a reference to the object constructed
* in the first invocation.
*
* Example:
* \code{.cpp}
* // Define a type with a `message` member that defines the contents of the
* // registered string
* struct my_message{ static constexpr char const* message{ "my message" };
* };
*
* // Uses construct on first use to register the contents of
* // `my_message::message`
* auto msg = registered_string<my_domain>::get<my_message>();
*
* // "my message" is associated with the range `r`
* nvtx3::thread_range r{msg};
* \endcode
*
* @tparam M Type required to contain a member `M::message` that
* resolves to either a `char const*` or `wchar_t const*` used as the
* registered string's contents.
* @return Reference to a `registered_string` associated with the type `M`.
*/
template < typename M >
static registered_string < D > const & get () noexcept
{
static registered_string < D > const registered_string { M :: message };
return registered_string ;
}
/**
* @brief Constructs a `registered_string` from the specified `msg` string.
*
* Registers `msg` with NVTX and associates a handle with the registered
* message.
*
* A particular message should should only be registered once and the handle
* reused throughout the rest of the application.
*
* @param msg The contents of the message
*/
explicit registered_string ( char const * msg ) noexcept
: handle_ { nvtxDomainRegisterStringA ( domain :: get < D > (), msg )}
{
}
/**
* @brief Constructs a `registered_string` from the specified `msg` string.
*
* Registers `msg` with NVTX and associates a handle with the registered
* message.
*
* A particular message should should only be registered once and the handle
* reused throughout the rest of the application.
*
* @param msg The contents of the message
*/
explicit registered_string ( std :: string const & msg ) noexcept : registered_string { msg . c_str ()} {}
/**
* @brief Constructs a `registered_string` from the specified `msg` string.
*
* Registers `msg` with NVTX and associates a handle with the registered
* message.
*
* A particular message should should only be registered once and the handle
* reused throughout the rest of the application.
*
* @param msg The contents of the message
*/
explicit registered_string ( wchar_t const * msg ) noexcept
: handle_ { nvtxDomainRegisterStringW ( domain :: get < D > (), msg )}
{
}
/**
* @brief Constructs a `registered_string` from the specified `msg` string.
*
* Registers `msg` with NVTX and associates a handle with the registered
* message.
*
* A particular message should only be registered once and the handle
* reused throughout the rest of the application.
*
* @param msg The contents of the message
*/
explicit registered_string ( std :: wstring const & msg ) noexcept : registered_string { msg . c_str ()} {}
/**
* @brief Returns the registered string's handle
*
*/
nvtxStringHandle_t get_handle () const noexcept { return handle_ ; }
registered_string () = delete ;
~ registered_string () = default ;
registered_string ( registered_string const & ) = default ;
registered_string & operator = ( registered_string const & ) = default ;
registered_string ( registered_string && ) = default ;
registered_string & operator = ( registered_string && ) = default ;
private :
nvtxStringHandle_t const handle_ {}; ///< The handle returned from
///< registering the message with NVTX
};
/**
* @brief Allows associating a message string with an NVTX event via
* its `EventAttribute`s.
*
* Associating a `message` with an NVTX event through its `event_attributes`
* allows for naming events to easily differentiate them from other events.
*
* Every time an NVTX event is created with an associated `message`, the
* contents of the message string must be copied. This may cause non-trivial
* overhead in highly performance sensitive sections of code. Use of a
* `nvtx3::registered_string` is recommended in these situations.
*
* Example:
* \code{.cpp}
* // Creates an `event_attributes` with message "message 0"
* nvtx3::event_attributes attr0{nvtx3::message{"message 0"}};
*
* // `range0` contains message "message 0"
* nvtx3::thread_range range0{attr0};
*
* // `std::string` and string literals are implicitly assumed to be
* // the contents of an `nvtx3::message`
* // Creates an `event_attributes` with message "message 1"
* nvtx3::event_attributes attr1{"message 1"};
*
* // `range1` contains message "message 1"
* nvtx3::thread_range range1{attr1};
*
* // `range2` contains message "message 2"
* nvtx3::thread_range range2{nvtx3::Mesage{"message 2"}};
*
* // `std::string` and string literals are implicitly assumed to be
* // the contents of an `nvtx3::message`
* // `range3` contains message "message 3"
* nvtx3::thread_range range3{"message 3"};
* \endcode
*/
class message {
public :
using value_type = nvtxMessageValue_t ;
/**
* @brief Construct a `message` whose contents are specified by `msg`.
*
* @param msg The contents of the message
*/
NVTX3_RELAXED_CONSTEXPR message ( char const * msg ) noexcept : type_ { NVTX_MESSAGE_TYPE_ASCII }
{
value_ . ascii = msg ;
}
/**
* @brief Construct a `message` whose contents are specified by `msg`.
*
* @param msg The contents of the message
*/
message ( std :: string const & msg ) noexcept : message { msg . c_str ()} {}
/**
* @brief Disallow construction for `std::string` r-value
*
* `message` is a non-owning type and therefore cannot take ownership of an
* r-value. Therefore, constructing from an r-value is disallowed to prevent
* a dangling pointer.
*
*/
message ( std :: string && ) = delete ;
/**
* @brief Construct a `message` whose contents are specified by `msg`.
*
* @param msg The contents of the message
*/
NVTX3_RELAXED_CONSTEXPR message ( wchar_t const * msg ) noexcept : type_ { NVTX_MESSAGE_TYPE_UNICODE }
{
value_ . unicode = msg ;
}
/**
* @brief Construct a `message` whose contents are specified by `msg`.
*
* @param msg The contents of the message
*/
message ( std :: wstring const & msg ) noexcept : message { msg . c_str ()} {}
/**
* @brief Disallow construction for `std::wstring` r-value
*
* `message` is a non-owning type and therefore cannot take ownership of an
* r-value. Therefore, constructing from an r-value is disallowed to prevent
* a dangling pointer.
*
*/
message ( std :: wstring && ) = delete ;
/**
* @brief Construct a `message` from a `registered_string`.
*
* @tparam D Type containing `name` member used to identify the `domain`
* to which the `registered_string` belongs. Else, `domain::global` to
* indicate that the global NVTX domain should be used.
* @param msg The message that has already been registered with NVTX.
*/
template < typename D >
NVTX3_RELAXED_CONSTEXPR message ( registered_string < D > const & msg ) noexcept
: type_ { NVTX_MESSAGE_TYPE_REGISTERED }
{
value_ . registered = msg . get_handle ();
}
/**
* @brief Return the union holding the value of the message.
*
*/
NVTX3_RELAXED_CONSTEXPR value_type get_value () const noexcept { return value_ ; }
/**
* @brief Return the type information about the value the union holds.
*
*/
NVTX3_RELAXED_CONSTEXPR nvtxMessageType_t get_type () const noexcept { return type_ ; }
private :
nvtxMessageType_t const type_ {}; ///< message type
nvtxMessageValue_t value_ {}; ///< message contents
};
/**
* @brief A numerical value that can be associated with an NVTX event via
* its `event_attributes`.
*
* Example:
* ```
* nvtx3:: event_attributes attr{nvtx3::payload{42}}; // Constructs a payload
* from
* // the `int32_t` value 42
*
* // `range0` will have an int32_t payload of 42
* nvtx3::thread_range range0{attr};
*
* // range1 has double payload of 3.14
* nvtx3::thread_range range1{ nvtx3::payload{3.14} };
* ```
*/
class payload {
public :
using value_type = typename nvtxEventAttributes_v2 :: payload_t ;
/**
* @brief Construct a `payload` from a signed, 8 byte integer.
*
* @param value Value to use as contents of the payload
*/
NVTX3_RELAXED_CONSTEXPR explicit payload ( int64_t value ) noexcept
: type_ { NVTX_PAYLOAD_TYPE_INT64 }, value_ {}
{
value_ . llValue = value ;
}
/**
* @brief Construct a `payload` from a signed, 4 byte integer.
*
* @param value Value to use as contents of the payload
*/
NVTX3_RELAXED_CONSTEXPR explicit payload ( int32_t value ) noexcept
: type_ { NVTX_PAYLOAD_TYPE_INT32 }, value_ {}
{
value_ . iValue = value ;
}
/**
* @brief Construct a `payload` from an unsigned, 8 byte integer.
*
* @param value Value to use as contents of the payload
*/
NVTX3_RELAXED_CONSTEXPR explicit payload ( uint64_t value ) noexcept
: type_ { NVTX_PAYLOAD_TYPE_UNSIGNED_INT64 }, value_ {}
{
value_ . ullValue = value ;
}
/**
* @brief Construct a `payload` from an unsigned, 4 byte integer.
*
* @param value Value to use as contents of the payload
*/
NVTX3_RELAXED_CONSTEXPR explicit payload ( uint32_t value ) noexcept
: type_ { NVTX_PAYLOAD_TYPE_UNSIGNED_INT32 }, value_ {}
{
value_ . uiValue = value ;
}
/**
* @brief Construct a `payload` from a single-precision floating point
* value.
*
* @param value Value to use as contents of the payload
*/
NVTX3_RELAXED_CONSTEXPR explicit payload ( float value ) noexcept
: type_ { NVTX_PAYLOAD_TYPE_FLOAT }, value_ {}
{
value_ . fValue = value ;
}
/**
* @brief Construct a `payload` from a double-precision floating point
* value.
*
* @param value Value to use as contents of the payload
*/
NVTX3_RELAXED_CONSTEXPR explicit payload ( double value ) noexcept
: type_ { NVTX_PAYLOAD_TYPE_DOUBLE }, value_ {}
{
value_ . dValue = value ;
}
/**
* @brief Return the union holding the value of the payload
*
*/
NVTX3_RELAXED_CONSTEXPR value_type get_value () const noexcept { return value_ ; }
/**
* @brief Return the information about the type the union holds.
*
*/
NVTX3_RELAXED_CONSTEXPR nvtxPayloadType_t get_type () const noexcept { return type_ ; }
private :
nvtxPayloadType_t const type_ ; ///< Type of the payload value
value_type value_ ; ///< Union holding the payload value
};
/**
* @brief Describes the attributes of a NVTX event.
*
* NVTX events can be customized via four "attributes":
*
* - color: color used to visualize the event in tools such as Nsight
* Systems. See `color`.
* - message: Custom message string. See `message`.
* - payload: User-defined numerical value. See `payload`.
* - category: Intra-domain grouping. See `category`.
*
* These component attributes are specified via an `event_attributes` object.
* See `nvtx3::color`, `nvtx3::message`, `nvtx3::payload`, and
* `nvtx3::category` for how these individual attributes are constructed.
*
* While it is possible to specify all four attributes, it is common to want
* to only specify a subset of attributes and use default values for the
* others. For convenience, `event_attributes` can be constructed from any
* number of attribute components in any order.
*
* Example:
* \code{.cpp}
* event_attributes attr{}; // No arguments, use defaults for all attributes
*
* event_attributes attr{"message"}; // Custom message, rest defaulted
*
* // Custom color & message
* event_attributes attr{"message", nvtx3::rgb{127, 255, 0}};
*
* /// Custom color & message, can use any order of arguments
* event_attributes attr{nvtx3::rgb{127, 255, 0}, "message"};
*
*
* // Custom color, message, payload, category
* event_attributes attr{nvtx3::rgb{127, 255, 0},
* "message",
* nvtx3::payload{42},
* nvtx3::category{1}};
*
* // Custom color, message, payload, category, can use any order of arguments
* event_attributes attr{nvtx3::payload{42},
* nvtx3::category{1},
* "message",
* nvtx3::rgb{127, 255, 0}};
*
* // Multiple arguments of the same type are allowed, but only the first is
* // used. All others are ignored
* event_attributes attr{ nvtx3::payload{42}, nvtx3::payload{7} }; // payload
* is 42
*
* // Range `r` will be customized according the attributes in `attr`
* nvtx3::thread_range r{attr};
*
* // For convenience, the arguments that can be passed to the
* `event_attributes`
* // constructor may be passed to the `domain_thread_range` contructor where
* // they will be forwarded to the `EventAttribute`s constructor
* nvtx3::thread_range r{nvtx3::payload{42}, nvtx3::category{1}, "message"};
* \endcode
*
*/
class event_attributes {
public :
using value_type = nvtxEventAttributes_t ;
/**
* @brief Default constructor creates an `event_attributes` with no
* category, color, payload, nor message.
*/
constexpr event_attributes () noexcept
: attributes_ {
NVTX_VERSION , // version
sizeof ( nvtxEventAttributes_t ), // size
0 , // category
NVTX_COLOR_UNKNOWN , // color type
0 , // color value
NVTX_PAYLOAD_UNKNOWN , // payload type
0 , // reserved 4B
0 , // payload value (union)
NVTX_MESSAGE_UNKNOWN , // message type
0 // message value (union)
}
{
}
/**
* @brief Variadic constructor where the first argument is a `category`.
*
* Sets the value of the `EventAttribute`s category based on `c` and
* forwards the remaining variadic parameter pack to the next constructor.
*
*/
template < typename ... Args >
NVTX3_RELAXED_CONSTEXPR explicit event_attributes ( category const & c , Args const & ... args ) noexcept
: event_attributes ( args ...)
{
attributes_ . category = c . get_id ();
}
/**
* @brief Variadic constructor where the first argument is a `color`.
*
* Sets the value of the `EventAttribute`s color based on `c` and forwards
* the remaining variadic parameter pack to the next constructor.
*
*/
template < typename ... Args >
NVTX3_RELAXED_CONSTEXPR explicit event_attributes ( color const & c , Args const & ... args ) noexcept
: event_attributes ( args ...)
{
attributes_ . color = c . get_value ();
attributes_ . colorType = c . get_type ();
}
/**
* @brief Variadic constructor where the first argument is a `payload`.
*
* Sets the value of the `EventAttribute`s payload based on `p` and forwards
* the remaining variadic parameter pack to the next constructor.
*
*/
template < typename ... Args >
NVTX3_RELAXED_CONSTEXPR explicit event_attributes ( payload const & p , Args const & ... args ) noexcept
: event_attributes ( args ...)
{
attributes_ . payload = p . get_value ();
attributes_ . payloadType = p . get_type ();
}
/**
* @brief Variadic constructor where the first argument is a `message`.
*
* Sets the value of the `EventAttribute`s message based on `m` and forwards
* the remaining variadic parameter pack to the next constructor.
*
*/
template < typename ... Args >
NVTX3_RELAXED_CONSTEXPR explicit event_attributes ( message const & m , Args const & ... args ) noexcept
: event_attributes ( args ...)
{
attributes_ . message = m . get_value ();
attributes_ . messageType = m . get_type ();
}
2022-11-29 04:27:46 -08:00
/**
* @brief Variadic constructor where the first argument is a binary payload.
*
* Sets the value of the `EventAttribute`s message based on `m` and forwards
* the remaining variadic parameter pack to the next constructor.
*
*/
template < typename ... Args >
NVTX3_RELAXED_CONSTEXPR explicit event_attributes ( nvtxPayloadData_t const * bpl , Args const & ... args ) noexcept
: event_attributes ( args ...)
{
attributes_ . payloadType = NVTX_PAYLOAD_TYPE_BINARY ;
attributes_ . reserved0 = 1 ; // NCCL uses only a single binary payload per event.
attributes_ . payload . ullValue = NVTX_POINTER_AS_PAYLOAD_ULLVALUE ( bpl );
}
2020-09-04 14:35:05 -07:00
~ event_attributes () = default ;
event_attributes ( event_attributes const & ) = default ;
event_attributes & operator = ( event_attributes const & ) = default ;
event_attributes ( event_attributes && ) = default ;
event_attributes & operator = ( event_attributes && ) = default ;
/**
* @brief Get raw pointer to underlying NVTX attributes object.
*
*/
constexpr value_type const * get () const noexcept { return & attributes_ ; }
private :
value_type attributes_ {}; ///< The NVTX attributes structure
};
/**
* @brief A RAII object for creating a NVTX range local to a thread within a
* domain.
*
* When constructed, begins a nested NVTX range on the calling thread in the
* specified domain. Upon destruction, ends the NVTX range.
*
* Behavior is undefined if a `domain_thread_range` object is
* created/destroyed on different threads.
*
* `domain_thread_range` is neither moveable nor copyable.
*
* `domain_thread_range`s may be nested within other ranges.
*
* The domain of the range is specified by the template type parameter `D`.
* By default, the `domain::global` is used, which scopes the range to the
* global NVTX domain. The convenience alias `thread_range` is provided for
* ranges scoped to the global domain.
*
* A custom domain can be defined by creating a type, `D`, with a static
* member `D::name` whose value is used to name the domain associated with
* `D`. `D::name` must resolve to either `char const*` or `wchar_t const*`
*
* Example:
* ```
* // Define a type `my_domain` with a member `name` used to name the domain
* // associated with the type `my_domain`.
* struct my_domain{
* static constexpr const char * name{"my domain"};
* };
* ```
*
* Usage:
* ```
* nvtx3::domain_thread_range<> r0{"range 0"}; // Range in global domain
*
* nvtx3::thread_range r1{"range 1"}; // Alias for range in global domain
*
* nvtx3::domain_thread_range<my_domain> r2{"range 2"}; // Range in custom
* domain
*
* // specify an alias to a range that uses a custom domain
* using my_thread_range = nvtx3::domain_thread_range<my_domain>;
*
* my_thread_range r3{"range 3"}; // Alias for range in custom domain
* ```
*/
template < class D = domain :: global >
class domain_thread_range {
public :
/**
* @brief Construct a `domain_thread_range` with the specified
* `event_attributes`
*
* Example:
* ```
* nvtx3::event_attributes attr{"msg", nvtx3::rgb{127,255,0}};
* nvtx3::domain_thread_range<> range{attr}; // Creates a range with message
* contents
* // "msg" and green color
* ```
*
* @param[in] attr `event_attributes` that describes the desired attributes
* of the range.
*/
explicit domain_thread_range ( event_attributes const & attr ) noexcept
{
#ifndef NVTX_DISABLE
nvtxDomainRangePushEx ( domain :: get < D > (), attr . get ());
#else
( void ) attr ;
#endif
}
/**
* @brief Constructs a `domain_thread_range` from the constructor arguments
* of an `event_attributes`.
*
* Forwards the arguments `first, args...` to construct an
* `event_attributes` object. The `event_attributes` object is then
* associated with the `domain_thread_range`.
*
* For more detail, see `event_attributes` documentation.
*
* Example:
* ```
* // Creates a range with message "message" and green color
* nvtx3::domain_thread_range<> r{"message", nvtx3::rgb{127,255,0}};
* ```
*
* @note To prevent making needless copies of `event_attributes` objects,
* this constructor is disabled when the first argument is an
* `event_attributes` object, instead preferring the explicit
* `domain_thread_range(event_attributes const&)` constructor.
*
* @param[in] first First argument to forward to the `event_attributes`
* constructor.
* @param[in] args Variadic parameter pack of additional arguments to
* forward.
*
*/
template < typename First ,
typename ... Args ,
typename = typename std :: enable_if <
! std :: is_same < event_attributes , typename std :: decay < First >>:: value >>
explicit domain_thread_range ( First const & first , Args const & ... args ) noexcept
: domain_thread_range { event_attributes { first , args ...}}
{
}
/**
* @brief Default constructor creates a `domain_thread_range` with no
* message, color, payload, nor category.
*
*/
domain_thread_range () : domain_thread_range { event_attributes {}} {}
/**
* @brief Delete `operator new` to disallow heap allocated objects.
*
* `domain_thread_range` must follow RAII semantics to guarantee proper push/pop semantics.
*
*/
void * operator new ( std :: size_t ) = delete ;
domain_thread_range ( domain_thread_range const & ) = delete ;
domain_thread_range & operator = ( domain_thread_range const & ) = delete ;
domain_thread_range ( domain_thread_range && ) = delete ;
domain_thread_range & operator = ( domain_thread_range && ) = delete ;
/**
* @brief Destroy the domain_thread_range, ending the NVTX range event.
*/
~ domain_thread_range () noexcept
{
#ifndef NVTX_DISABLE
nvtxDomainRangePop ( domain :: get < D > ());
#endif
}
};
/**
* @brief Alias for a `domain_thread_range` in the global NVTX domain.
*
*/
using thread_range = domain_thread_range <> ;
/**
* @brief Handle used for correlating explicit range start and end events.
*
*/
struct range_handle {
/// Type used for the handle's value
using value_type = nvtxRangeId_t ;
/**
* @brief Construct a `range_handle` from the given id.
*
*/
constexpr explicit range_handle ( value_type id ) noexcept : _range_id { id } {}
/**
* @brief Returns the `range_handle`'s value
*
* @return value_type The handle's value
*/
constexpr value_type get_value () const noexcept { return _range_id ; }
private :
value_type _range_id {}; ///< The underlying NVTX range id
};
/**
* @brief Manually begin an NVTX range.
*
* Explicitly begins an NVTX range and returns a unique handle. To end the
* range, pass the handle to `end_range()`.
*
* `start_range/end_range` are the most explicit and lowest level APIs provided
* for creating ranges. Use of `nvtx3::domain_process_range` should be
* preferred unless one is unable to tie the range to the lifetime of an object.
*
* Example:
* ```
* nvtx3::event_attributes attr{"msg", nvtx3::rgb{127,255,0}};
* nvtx3::range_handle h = nvxt3::start_range(attr); // Manually begins a range
* ...
* nvtx3::end_range(h); // Ends the range
* ```
*
* @tparam D Type containing `name` member used to identify the `domain`
* to which the range belongs. Else, `domain::global` to indicate that the
* global NVTX domain should be used.
* @param[in] attr `event_attributes` that describes the desired attributes
* of the range.
* @return Unique handle to be passed to `end_range` to end the range.
*/
template < typename D = domain :: global >
range_handle start_range ( event_attributes const & attr ) noexcept
{
#ifndef NVTX_DISABLE
return range_handle { nvtxDomainRangeStartEx ( domain :: get < D > (), attr . get ())};
#else
( void ) attr ;
return range_handle {};
#endif
}
/**
* @brief Manually begin an NVTX range.
*
* Explicitly begins an NVTX range and returns a unique handle. To end the
* range, pass the handle to `end_range()`.
*
* Forwards the arguments `first, args...` to construct an `event_attributes`
* object. The `event_attributes` object is then associated with the range.
*
* For more detail, see `event_attributes` documentation.
*
* Example:
* ```
* nvtx3::range_handle h = nvxt3::start_range("msg", nvtx3::rgb{127,255,0}); //
* Begin range
* ...
* nvtx3::end_range(h); // Ends the range
* ```
*
* `start_range/end_range` are the most explicit and lowest level APIs provided
* for creating ranges. Use of `nvtx3::domain_process_range` should be
* preferred unless one is unable to tie the range to the lifetime of an object.
*
* @param first[in] First argument to pass to an `event_attributes`
* @param args[in] Variadiac parameter pack of the rest of the arguments for an
* `event_attributes`.
* @return Unique handle to be passed to `end_range` to end the range.
*/
template < typename First ,
typename ... Args ,
typename = typename std :: enable_if <
! std :: is_same < event_attributes , typename std :: decay < First >>:: value >>
range_handle start_range ( First const & first , Args const & ... args ) noexcept
{
#ifndef NVTX_DISABLE
return start_range ( event_attributes { first , args ...});
#else
( void ) first ;
return range_handle {};
#endif
}
/**
* @brief Manually end the range associated with the handle `r`.
*
* Explicitly ends the NVTX range indicated by the handle `r` returned from a
* prior call to `start_range`. The range may end on a different thread from
* where it began.
*
* This function does not have a Domain tag type template parameter as the
* handle `r` already indicates the domain to which the range belongs.
*
* @param r Handle to a range started by a prior call to `start_range`.
*/
inline void end_range ( range_handle r )
{
#ifndef NVTX_DISABLE
nvtxRangeEnd ( r . get_value ());
#else
( void ) r ;
#endif
}
/**
* @brief A RAII object for creating a NVTX range within a domain that can
* be created and destroyed on different threads.
*
* When constructed, begins a NVTX range in the specified domain. Upon
* destruction, ends the NVTX range.
*
* Similar to `nvtx3::domain_thread_range`, the only difference being that
* `domain_process_range` can start and end on different threads.
*
* Use of `nvtx3::domain_thread_range` should be preferred unless one needs
* the ability to start and end a range on different threads.
*
* `domain_process_range` is moveable, but not copyable.
*
* @tparam D Type containing `name` member used to identify the `domain`
* to which the `domain_process_range` belongs. Else, `domain::global` to
* indicate that the global NVTX domain should be used.
*/
template < typename D = domain :: global >
class domain_process_range {
public :
/**
* @brief Construct a new domain process range object
*
* @param attr
*/
explicit domain_process_range ( event_attributes const & attr ) noexcept
: handle_ { new range_handle { start_range < D > ( attr )}}
{
}
/**
* @brief Construct a new domain process range object
*
* @param first
* @param args
*/
template < typename First ,
typename ... Args ,
typename = typename std :: enable_if <
! std :: is_same < event_attributes , typename std :: decay < First >>:: value >>
explicit domain_process_range ( First const & first , Args const & ... args ) noexcept
: domain_process_range { event_attributes { first , args ...}}
{
}
/**
* @brief Construct a new domain process range object
*
*/
constexpr domain_process_range () noexcept : domain_process_range { event_attributes {}} {}
/**
* @brief Destroy the `domain_process_range` ending the range.
*
*/
~ domain_process_range ()
{
if ( handle_ ) { end_range ( * handle_ ); }
}
/**
* @brief Move constructor allows taking ownership of the NVTX range from
* another `domain_process_range`.
*
* @param other
*/
domain_process_range ( domain_process_range && other ) = default ;
/**
* @brief Move assignment operator allows taking ownership of an NVTX range
* from another `domain_process_range`.
*
* @param other
* @return domain_process_range&
*/
domain_process_range & operator = ( domain_process_range && other ) = default ;
/// Copy construction is not allowed to prevent multiple objects from owning
/// the same range handle
domain_process_range ( domain_process_range const & ) = delete ;
/// Copy assignment is not allowed to prevent multiple objects from owning the
/// same range handle
domain_process_range & operator = ( domain_process_range const & ) = delete ;
private :
std :: unique_ptr < range_handle > handle_ ; ///< Range handle used to correlate
///< the start/end of the range
};
/**
* @brief Alias for a `domain_process_range` in the global NVTX domain.
*
*/
using process_range = domain_process_range <> ;
/**
* @brief Annotates an instantaneous point in time with the attributes specified
* by `attr`.
*
* Unlike a "range", a mark is an instantaneous event in an application, e.g.,
* locking/unlocking a mutex.
*
* \code{.cpp}
* std::mutex global_lock;
* void lock_mutex(){
* global_lock.lock();
* nvtx3::mark("lock_mutex");
* }
* \endcode
*
* @tparam D Type containing `name` member used to identify the `domain`
* to which the `domain_process_range` belongs. Else, `domain::global` to
* indicate that the global NVTX domain should be used.
* @param[in] attr `event_attributes` that describes the desired attributes
* of the mark.
*/
template < typename D = domain :: global >
inline void mark ( event_attributes const & attr ) noexcept
{
#ifndef NVTX_DISABLE
nvtxDomainMarkEx ( domain :: get < D > (), attr . get ());
#else
( void )( attr );
#endif
}
} // namespace NVTX3_VERSION_NAMESPACE
} // namespace nvtx3
/**
* @brief Convenience macro for generating a range in the specified `domain`
* from the lifetime of a function
*
* This macro is useful for generating an NVTX range in `domain` from
* the entry point of a function to its exit. It is intended to be the first
* line of the function.
*
* Constructs a static `registered_string` using the name of the immediately
* enclosing function returned by `__func__` and constructs a
* `nvtx3::thread_range` using the registered function name as the range's
* message.
*
* Example:
* ```
* struct my_domain{static constexpr char const* name{"my_domain"};};
*
* void foo(...){
* NVTX3_FUNC_RANGE_IN(my_domain); // Range begins on entry to foo()
* // do stuff
* ...
* } // Range ends on return from foo()
* ```
*
* @param[in] D Type containing `name` member used to identify the
* `domain` to which the `registered_string` belongs. Else,
* `domain::global` to indicate that the global NVTX domain should be used.
*/
#ifndef NVTX_DISABLE
#define NVTX3_V1_FUNC_RANGE_IN(D) \
static ::nvtx3::v1::registered_string<D> const nvtx3_func_name__{__func__}; \
static ::nvtx3::v1::event_attributes const nvtx3_func_attr__{nvtx3_func_name__}; \
::nvtx3::v1::domain_thread_range<D> const nvtx3_range__{nvtx3_func_attr__};
#else
#define NVTX3_V1_FUNC_RANGE_IN(D)
#endif
/**
* @brief Convenience macro for generating a range in the global domain from the
* lifetime of a function.
*
* This macro is useful for generating an NVTX range in the global domain from
* the entry point of a function to its exit. It is intended to be the first
* line of the function.
*
* Constructs a static `registered_string` using the name of the immediately
* enclosing function returned by `__func__` and constructs a
* `nvtx3::thread_range` using the registered function name as the range's
* message.
*
* Example:
* ```
* void foo(...){
* NVTX3_FUNC_RANGE(); // Range begins on entry to foo()
* // do stuff
* ...
* } // Range ends on return from foo()
* ```
*/
#define NVTX3_V1_FUNC_RANGE() NVTX3_V1_FUNC_RANGE_IN(::nvtx3::v1::domain::global)
/* When inlining this version, versioned macros must have unversioned aliases.
* For each NVTX3_Vx_ #define, make an NVTX3_ alias of it here.*/
#if defined(NVTX3_INLINE_THIS_VERSION)
/* clang format off */
#define NVTX3_FUNC_RANGE_IN NVTX3_V1_FUNC_RANGE_IN
#define NVTX3_FUNC_RANGE NVTX3_V1_FUNC_RANGE
/* clang format on */
#endif
#endif // NVTX3_CPP_DEFINITIONS_V1_0
/* Add functionality for new minor versions here, by copying the above section enclosed
* in #ifndef NVTX3_CPP_DEFINITIONS_Vx_y, and incrementing the minor version. This code
* is an example of how additions for version 1.2 would look, indented for clarity. Note
* that the versioned symbols and macros are always provided, and the unversioned symbols
* are only provided if NVTX3_INLINE_THIS_VERSION was defined at the top of this header.
*
* \code{.cpp}
* #ifndef NVTX3_CPP_DEFINITIONS_V1_2
* #define NVTX3_CPP_DEFINITIONS_V1_2
* namespace nvtx3 {
* NVTX3_INLINE_IF_REQUESTED namespace NVTX3_VERSION_NAMESPACE {
* class new_class {};
* inline void new_function() {}
* }
* }
*
* // Macros must have the major version in their names:
* #define NVTX3_V1_NEW_MACRO_A() ...
* #define NVTX3_V1_NEW_MACRO_B() ...
*
* // If inlining, make aliases for the macros with the version number omitted
* #if defined(NVTX3_INLINE_THIS_VERSION)
* #define NVTX3_NEW_MACRO_A NVTX3_V1_NEW_MACRO_A
* #define NVTX3_NEW_MACRO_B NVTX3_V1_NEW_MACRO_B
* #endif
* #endif // NVTX3_CPP_DEFINITIONS_V1_2
* \endcode
*/
/* Undefine all temporarily-defined unversioned macros, which would conflict with
* subsequent includes of different versions of this header. */
#undef NVTX3_CPP_VERSION_MAJOR
#undef NVTX3_CPP_VERSION_MINOR
#undef NVTX3_CONCAT
#undef NVTX3_NAMESPACE_FOR
#undef NVTX3_VERSION_NAMESPACE
#undef NVTX3_INLINE_IF_REQUESTED
#undef NVTX3_RELAXED_CONSTEXPR
#if defined(NVTX3_INLINE_THIS_VERSION)
#undef NVTX3_INLINE_THIS_VERSION
#endif