From 3971d0153896cd530f95c092fb8e41aeb58f1431 Mon Sep 17 00:00:00 2001 From: Anuj Shukla Date: Thu, 8 Jan 2026 13:51:12 -0500 Subject: [PATCH 1/4] Convert thread storage from std::array to std::vector with dynamic growth --- .../rocprofiler-systems/external/timemory | 2 +- .../library/rocprofiler-sdk/counters.hpp | 48 +++++++++++++++++-- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/projects/rocprofiler-systems/external/timemory b/projects/rocprofiler-systems/external/timemory index 24407d37ab8..56363a11d49 160000 --- a/projects/rocprofiler-systems/external/timemory +++ b/projects/rocprofiler-systems/external/timemory @@ -1 +1 @@ -Subproject commit 24407d37ab85c46ba6c18fba9498320f825ee4e4 +Subproject commit 56363a11d49b1f78ea5a335f2e344622e325a3fe diff --git a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/rocprofiler-sdk/counters.hpp b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/rocprofiler-sdk/counters.hpp index 05b65df4424..591d47d849a 100644 --- a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/rocprofiler-sdk/counters.hpp +++ b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/rocprofiler-sdk/counters.hpp @@ -124,19 +124,56 @@ struct set_storage<::rocprofsys::rocprofiler_sdk::counter_data_tracker> { static constexpr size_t max_threads = 4096; using type = ::rocprofsys::rocprofiler_sdk::counter_data_tracker; - using storage_array_t = std::array*, max_threads>; + using storage_array_t = std::vector*>; friend struct get_storage; ROCPROFSYS_DEFAULT_OBJECT(set_storage) - auto operator()(storage* _v, size_t _idx) const { get().at(_idx) = _v; } + auto operator()(storage* _v, size_t _idx) const + { + ensure_capacity(_idx); + get().at(_idx) = _v; + } auto operator()(type&, size_t) const {} - auto operator()(storage* _v) const { get().fill(_v); } + auto operator()(storage* _v) const { std::fill(get().begin(), get().end(), _v); } private: + static std::atomic& get_capacity() + { + static std::atomic _cap{max_threads}; + return _cap; + } + + static std::mutex& get_mutex() + { + static std::mutex _mtx; + return _mtx; + } + + static void ensure_capacity(size_t _idx) + { + // Fast path: check atomic capacity (no lock) + if(_idx < get_capacity().load(std::memory_order_acquire)) + return; + + // Slow path: need to resize with lock + std::lock_guard _lock(get_mutex()); + auto& _v = get(); + + // Double-check after acquiring lock + if(_idx >= _v.size()) + { + size_t new_size = std::max(_v.size(), size_t(1)); + while(new_size <= _idx) + new_size *= 2; // Geometric growth (doubling) + _v.resize(new_size, nullptr); + get_capacity().store(_v.size(), std::memory_order_release); + } + } + static storage_array_t& get() { - static storage_array_t _v = { nullptr }; + static storage_array_t _v(max_threads, nullptr); return _v; } }; @@ -161,6 +198,9 @@ struct get_storage<::rocprofsys::rocprofiler_sdk::counter_data_tracker> auto operator()(size_t _idx) const { + // Thread-safe read using atomic capacity + if(_idx >= operation::set_storage::get_capacity().load(std::memory_order_acquire)) + return static_cast*>(nullptr); return operation::set_storage::get().at(_idx); } From b68a11b266a6a04711adfe276e5e8a58dd0ece43 Mon Sep 17 00:00:00 2001 From: Anuj Shukla Date: Thu, 8 Jan 2026 14:18:22 -0500 Subject: [PATCH 2/4] Convert thread storage from std::array to std::vector with dynamic growth - Replace fixed std::array with std::vector in counters.hpp - Implement thread-safe dynamic resizing with geometric growth (2x) - Add ensure_capacity() with double-checked locking pattern - Use std::atomic for lock-free capacity reads - Add bounds checking in get_storage operation - Initial capacity set to 4096, grows as needed - Update timemory submodule to users/anujshuk/dynamic-thread-storage_ds (696a160d) --- .../rocprofiler-systems/external/timemory | 2 +- .../library/rocprofiler-sdk/counters.hpp | 60 ++++++------------- 2 files changed, 19 insertions(+), 43 deletions(-) diff --git a/projects/rocprofiler-systems/external/timemory b/projects/rocprofiler-systems/external/timemory index 56363a11d49..2624144f553 160000 --- a/projects/rocprofiler-systems/external/timemory +++ b/projects/rocprofiler-systems/external/timemory @@ -1 +1 @@ -Subproject commit 56363a11d49b1f78ea5a335f2e344622e325a3fe +Subproject commit 2624144f553d4c3e15d5b222e54edf6bd75f0e56 diff --git a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/rocprofiler-sdk/counters.hpp b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/rocprofiler-sdk/counters.hpp index 591d47d849a..89b6c628477 100644 --- a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/rocprofiler-sdk/counters.hpp +++ b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/rocprofiler-sdk/counters.hpp @@ -22,12 +22,15 @@ #pragma once +#include "common/defines.h" + #include "common/synchronized.hpp" #include "core/debug.hpp" #include "core/perfetto.hpp" #include "core/timemory.hpp" #include "library/rocprofiler-sdk/fwd.hpp" +#include #include #include @@ -119,57 +122,31 @@ namespace tim { namespace operation { +using type = ::rocprofsys::rocprofiler_sdk::counter_data_tracker; + template <> -struct set_storage<::rocprofsys::rocprofiler_sdk::counter_data_tracker> +struct set_storage +: protected dynamic_storage_base*, ROCPROFSYS_MAX_THREADS> { - static constexpr size_t max_threads = 4096; - using type = ::rocprofsys::rocprofiler_sdk::counter_data_tracker; - using storage_array_t = std::vector*>; - friend struct get_storage; + static constexpr size_t max_threads = ROCPROFSYS_MAX_THREADS; + using base_type = dynamic_storage_base*, max_threads>; + using storage_array_t = typename base_type::storage_array_t; ROCPROFSYS_DEFAULT_OBJECT(set_storage) auto operator()(storage* _v, size_t _idx) const { - ensure_capacity(_idx); + base_type::ensure_capacity(get(), _idx); get().at(_idx) = _v; } auto operator()(type&, size_t) const {} - auto operator()(storage* _v) const { std::fill(get().begin(), get().end(), _v); } - -private: - static std::atomic& get_capacity() + auto operator()(storage* _v) const { - static std::atomic _cap{max_threads}; - return _cap; + std::fill(get().begin(), get().end(), _v); } - static std::mutex& get_mutex() - { - static std::mutex _mtx; - return _mtx; - } - - static void ensure_capacity(size_t _idx) - { - // Fast path: check atomic capacity (no lock) - if(_idx < get_capacity().load(std::memory_order_acquire)) - return; - - // Slow path: need to resize with lock - std::lock_guard _lock(get_mutex()); - auto& _v = get(); - - // Double-check after acquiring lock - if(_idx >= _v.size()) - { - size_t new_size = std::max(_v.size(), size_t(1)); - while(new_size <= _idx) - new_size *= 2; // Geometric growth (doubling) - _v.resize(new_size, nullptr); - get_capacity().store(_v.size(), std::memory_order_release); - } - } + // Expose get_capacity for get_storage access + using base_type::get_capacity; static storage_array_t& get() { @@ -179,10 +156,8 @@ struct set_storage<::rocprofsys::rocprofiler_sdk::counter_data_tracker> }; template <> -struct get_storage<::rocprofsys::rocprofiler_sdk::counter_data_tracker> +struct get_storage { - using type = ::rocprofsys::rocprofiler_sdk::counter_data_tracker; - ROCPROFSYS_DEFAULT_OBJECT(get_storage) auto operator()(const type&) const @@ -199,7 +174,8 @@ struct get_storage<::rocprofsys::rocprofiler_sdk::counter_data_tracker> auto operator()(size_t _idx) const { // Thread-safe read using atomic capacity - if(_idx >= operation::set_storage::get_capacity().load(std::memory_order_acquire)) + if(_idx >= + operation::set_storage::get_capacity().load(std::memory_order_acquire)) return static_cast*>(nullptr); return operation::set_storage::get().at(_idx); } From ea379a7d8c0258ea30d8e25c92cb8b00a73ccade Mon Sep 17 00:00:00 2001 From: Anuj Shukla Date: Tue, 20 Jan 2026 16:21:15 -0500 Subject: [PATCH 3/4] refactor: address code review comments for dynamic thread storage --- .../rocprofiler-systems/external/timemory | 2 +- .../source/lib/core/config.cpp | 19 +++++++++++++++++- .../library/rocprofiler-sdk/counters.hpp | 20 +++++++++---------- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/projects/rocprofiler-systems/external/timemory b/projects/rocprofiler-systems/external/timemory index 2624144f553..079309f9690 160000 --- a/projects/rocprofiler-systems/external/timemory +++ b/projects/rocprofiler-systems/external/timemory @@ -1 +1 @@ -Subproject commit 2624144f553d4c3e15d5b222e54edf6bd75f0e56 +Subproject commit 079309f96909282dca4122ccc8f9b7788eb7df61 diff --git a/projects/rocprofiler-systems/source/lib/core/config.cpp b/projects/rocprofiler-systems/source/lib/core/config.cpp index ffbb4dd46ed..203fb882d91 100644 --- a/projects/rocprofiler-systems/source/lib/core/config.cpp +++ b/projects/rocprofiler-systems/source/lib/core/config.cpp @@ -2758,7 +2758,24 @@ get_tmp_file(std::string _basename, std::string _ext) _cfg.use_suffix = true; _cfg.suffix = "%pid%"; _cfg.explicit_path = get_tmpdir(); - _cfg.subdirectory = JOIN('/', settings::output_path(), "%ppid%", ""); + + auto _output_path = settings::output_path(); + if(!_output_path.empty() && _output_path.front() == '/') + { + // Extract the last directory component to use as subdirectory + auto _last_slash = _output_path.find_last_of('/'); + if(_last_slash != std::string::npos && _last_slash < _output_path.size() - 1) + _output_path = _output_path.substr(_last_slash + 1); + else if(_last_slash == _output_path.size() - 1 && _last_slash > 0) + { + // Path ends with slash, find the second-to-last slash + auto _prev_slash = _output_path.find_last_of('/', _last_slash - 1); + if(_prev_slash != std::string::npos) + _output_path = + _output_path.substr(_prev_slash + 1, _last_slash - _prev_slash - 1); + } + } + _cfg.subdirectory = JOIN('/', _output_path, "%ppid%", ""); auto _fname = settings::compose_output_filename(std::move(_basename), std::move(_ext), _cfg); diff --git a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/rocprofiler-sdk/counters.hpp b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/rocprofiler-sdk/counters.hpp index 89b6c628477..ab81b1e5335 100644 --- a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/rocprofiler-sdk/counters.hpp +++ b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/rocprofiler-sdk/counters.hpp @@ -122,13 +122,15 @@ namespace tim { namespace operation { -using type = ::rocprofsys::rocprofiler_sdk::counter_data_tracker; template <> -struct set_storage -: protected dynamic_storage_base*, ROCPROFSYS_MAX_THREADS> +struct set_storage<::rocprofsys::rocprofiler_sdk::counter_data_tracker> +: public dynamic_storage_base< + storage<::rocprofsys::rocprofiler_sdk::counter_data_tracker>*, + ROCPROFSYS_MAX_THREADS> { static constexpr size_t max_threads = ROCPROFSYS_MAX_THREADS; + using type = ::rocprofsys::rocprofiler_sdk::counter_data_tracker; using base_type = dynamic_storage_base*, max_threads>; using storage_array_t = typename base_type::storage_array_t; @@ -145,9 +147,6 @@ struct set_storage std::fill(get().begin(), get().end(), _v); } - // Expose get_capacity for get_storage access - using base_type::get_capacity; - static storage_array_t& get() { static storage_array_t _v(max_threads, nullptr); @@ -156,8 +155,10 @@ struct set_storage }; template <> -struct get_storage +struct get_storage<::rocprofsys::rocprofiler_sdk::counter_data_tracker> { + using type = ::rocprofsys::rocprofiler_sdk::counter_data_tracker; + ROCPROFSYS_DEFAULT_OBJECT(get_storage) auto operator()(const type&) const @@ -174,9 +175,8 @@ struct get_storage auto operator()(size_t _idx) const { // Thread-safe read using atomic capacity - if(_idx >= - operation::set_storage::get_capacity().load(std::memory_order_acquire)) - return static_cast*>(nullptr); + const size_t current_capacity = operation::set_storage::get_capacity(); + if(_idx >= current_capacity) return static_cast*>(nullptr); return operation::set_storage::get().at(_idx); } From e9a38d431c083b9d3f14826ffd281810e565204d Mon Sep 17 00:00:00 2001 From: Anuj Shukla Date: Wed, 28 Jan 2026 16:53:05 -0500 Subject: [PATCH 4/4] Apply clang-format-18 to config.cpp and counters.hpp --- .../rocprofiler-systems/external/timemory | 2 +- .../source/lib/core/config.cpp | 19 +-------- .../library/rocprofiler-sdk/counters.hpp | 41 +++++++++++-------- 3 files changed, 25 insertions(+), 37 deletions(-) diff --git a/projects/rocprofiler-systems/external/timemory b/projects/rocprofiler-systems/external/timemory index 079309f9690..f3f2b94eada 160000 --- a/projects/rocprofiler-systems/external/timemory +++ b/projects/rocprofiler-systems/external/timemory @@ -1 +1 @@ -Subproject commit 079309f96909282dca4122ccc8f9b7788eb7df61 +Subproject commit f3f2b94eada2657d9f8de1bc8c6ee4cd9b10a90a diff --git a/projects/rocprofiler-systems/source/lib/core/config.cpp b/projects/rocprofiler-systems/source/lib/core/config.cpp index 203fb882d91..ffbb4dd46ed 100644 --- a/projects/rocprofiler-systems/source/lib/core/config.cpp +++ b/projects/rocprofiler-systems/source/lib/core/config.cpp @@ -2758,24 +2758,7 @@ get_tmp_file(std::string _basename, std::string _ext) _cfg.use_suffix = true; _cfg.suffix = "%pid%"; _cfg.explicit_path = get_tmpdir(); - - auto _output_path = settings::output_path(); - if(!_output_path.empty() && _output_path.front() == '/') - { - // Extract the last directory component to use as subdirectory - auto _last_slash = _output_path.find_last_of('/'); - if(_last_slash != std::string::npos && _last_slash < _output_path.size() - 1) - _output_path = _output_path.substr(_last_slash + 1); - else if(_last_slash == _output_path.size() - 1 && _last_slash > 0) - { - // Path ends with slash, find the second-to-last slash - auto _prev_slash = _output_path.find_last_of('/', _last_slash - 1); - if(_prev_slash != std::string::npos) - _output_path = - _output_path.substr(_prev_slash + 1, _last_slash - _prev_slash - 1); - } - } - _cfg.subdirectory = JOIN('/', _output_path, "%ppid%", ""); + _cfg.subdirectory = JOIN('/', settings::output_path(), "%ppid%", ""); auto _fname = settings::compose_output_filename(std::move(_basename), std::move(_ext), _cfg); diff --git a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/rocprofiler-sdk/counters.hpp b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/rocprofiler-sdk/counters.hpp index ab81b1e5335..bff6b14dd00 100644 --- a/projects/rocprofiler-systems/source/lib/rocprof-sys/library/rocprofiler-sdk/counters.hpp +++ b/projects/rocprofiler-systems/source/lib/rocprof-sys/library/rocprofiler-sdk/counters.hpp @@ -22,15 +22,12 @@ #pragma once -#include "common/defines.h" - #include "common/synchronized.hpp" -#include "core/debug.hpp" +#include "core/containers/stable_vector.hpp" #include "core/perfetto.hpp" #include "core/timemory.hpp" #include "library/rocprofiler-sdk/fwd.hpp" -#include #include #include @@ -122,36 +119,46 @@ namespace tim { namespace operation { - template <> struct set_storage<::rocprofsys::rocprofiler_sdk::counter_data_tracker> -: public dynamic_storage_base< - storage<::rocprofsys::rocprofiler_sdk::counter_data_tracker>*, - ROCPROFSYS_MAX_THREADS> { - static constexpr size_t max_threads = ROCPROFSYS_MAX_THREADS; - using type = ::rocprofsys::rocprofiler_sdk::counter_data_tracker; - using base_type = dynamic_storage_base*, max_threads>; - using storage_array_t = typename base_type::storage_array_t; + static constexpr size_t initial_capacity = ROCPROFSYS_MAX_THREADS; + using type = ::rocprofsys::rocprofiler_sdk::counter_data_tracker; + using storage_array_t = + ::rocprofsys::container::stable_vector*, initial_capacity>; + friend struct get_storage; ROCPROFSYS_DEFAULT_OBJECT(set_storage) auto operator()(storage* _v, size_t _idx) const { - base_type::ensure_capacity(get(), _idx); + ensure_capacity(_idx); get().at(_idx) = _v; } + auto operator()(type&, size_t) const {} + auto operator()(storage* _v) const { - std::fill(get().begin(), get().end(), _v); + // Fill all existing elements with the same storage pointer + auto& vec = get(); + for(size_t i = 0; i < vec.size(); ++i) + vec[i] = _v; } +private: static storage_array_t& get() { - static storage_array_t _v(max_threads, nullptr); + static storage_array_t _v(initial_capacity, nullptr); return _v; } + + static void ensure_capacity(size_t _idx) + { + auto& vec = get(); + while(vec.size() <= _idx) + vec.emplace_back(nullptr); + } }; template <> @@ -174,9 +181,7 @@ struct get_storage<::rocprofsys::rocprofiler_sdk::counter_data_tracker> auto operator()(size_t _idx) const { - // Thread-safe read using atomic capacity - const size_t current_capacity = operation::set_storage::get_capacity(); - if(_idx >= current_capacity) return static_cast*>(nullptr); + operation::set_storage::ensure_capacity(_idx); return operation::set_storage::get().at(_idx); }