diff --git a/CHANGELOG.md b/CHANGELOG.md index 27e19259a..dbeb76177 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to ### For users #### Added +* A timer is added to reflect the time consumed by plugins. #### Changed diff --git a/CMakeLists.txt b/CMakeLists.txt index 0b077743a..1a641ad21 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -76,10 +76,9 @@ option(BUILD_SHARED_LIBS "Build shared libraries rather than static ones" option(BUILD_TRACE_PLUGIN "Build Trace plugin" ON) option(BUILD_USER_CODE_PLUGIN "Build User-code plugin" ON) option(BUILD_JSON_PLUGIN "Build JSON plugin" OFF) +option(BUILD_TIMER_PLUGIN "Build Timer plugin" ON) option(ENABLE_BENCHMARKING "Activate benchmarks in the test suite" OFF) - - ### Default build type if(NOT DEFINED CMAKE_BUILD_TYPE) @@ -295,9 +294,9 @@ endif() ## HDF5 if("${BUILD_DECL_HDF5_PLUGIN}" OR "${BUILD_DECL_NETCDF_PLUGIN}") - + set(HDF5_CMAKE_CACHE_ARGS) - + if("${BUILD_HDF5_PARALLEL}") set(HDF5_PREFER_PARALLEL ON) list(APPEND HDF5_CMAKE_CACHE_ARGS "-DHDF5_ENABLE_PARALLEL:BOOL=ON") @@ -536,3 +535,10 @@ sbuild_add_module(PDI_API_TESTS INSTALL_COMMAND "" SUBSTEPS test ) + +sbuild_add_module(TIMER_PLUGIN + ENABLE_BUILD_FLAG BUILD_TIMER_PLUGIN + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/plugins/timer" + DEPENDS PDI + # SUBSTEPS indent test +) diff --git a/example/timer.yml b/example/timer.yml new file mode 100644 index 000000000..49ad3d80d --- /dev/null +++ b/example/timer.yml @@ -0,0 +1,57 @@ +# duration in seconds +duration: 0.75 +# global [height, width] (excluding boundary conditions or ghosts) +datasize: [60, 12] +# degree of parallelism +parallelism: { height: 3, width: 1 } + +# only the following config is passed to PDI +pdi: + metadata: # type of small values for which PDI keeps a copy + iter: int # current iteration id + dsize: { size: 2, type: array, subtype: int } # local data size including ghosts/boundary + psize: { size: 2, type: array, subtype: int } # number of processes in each dimension + pcoord: { size: 2, type: array, subtype: int } # coordinate of the process + data: # type of values for which PDI does not keep a copy + main_field: { size: [ '$dsize[0]', '$dsize[1]' ], type: array, subtype: double } + timer: true + plugins: + mpi: + decl_hdf5: + file: data.h5 + communicator: $MPI_COMM_WORLD # the MPI communicator used for HDF5 parallel synchronized write + datasets: # type of the datasets to create in file + data: + type: array + subtype: double + size: [10, '$psize[0]*($dsize[0]-2)', '$psize[1]*($dsize[1]-2)'] + write: + main_field: # the name of the data to write + dataset: data + mpio: COLLECTIVE # or INDEPENDENT + when: '$iter<10' # do only write the first 10 iterations (0...9) + memory_selection: # exclude ghosts from the data in memory + size: ['$dsize[0]-2', '$dsize[1]-2'] + start: [1, 1] + dataset_selection: # only write into a single slice in time + size: [1, '$dsize[0]-2', '$dsize[1]-2'] + start: [$iter, '($dsize[0]-2)*$pcoord[0]', '($dsize[1]-2)*$pcoord[1]'] + decl_netcdf: + file: data.nc + communicator: $MPI_COMM_WORLD # the MPI communicator used for HDF5 parallel synchronized write + variables: # type of the datasets to create in file + v_data: + type: array + subtype: double + size: [10, '$psize[0]*$dsize[0]', '$psize[1]*$dsize[1]'] + write: + main_field: # the name of the data to write + variable: v_data + when: '$iter<10' # do only write the first 10 iterations (0...9) + variable_selection: # only write into a single slice in time + subsize: [1, '$dsize[0]', '$dsize[1]'] + start: [$iter, '$dsize[0]*$pcoord[0]', '$dsize[1]*$pcoord[1]'] + json: + file: data_${pcoord[0]}_${pcoord[1]}.json + when: '$iter<10' + write: [main_field] \ No newline at end of file diff --git a/pdi/CMakeLists.txt b/pdi/CMakeLists.txt index bc4963f1d..fdd1cf90a 100644 --- a/pdi/CMakeLists.txt +++ b/pdi/CMakeLists.txt @@ -160,6 +160,7 @@ set(PDI_C_SRC src/ref_any.cxx src/scalar_datatype.cxx src/string_tools.cxx + src/timer.cxx src/tuple_datatype.cxx ) add_library(PDI_C ${PDI_C_SRC}) diff --git a/pdi/include/pdi/context.h b/pdi/include/pdi/context.h index b24b2f6cd..7d2547b46 100644 --- a/pdi/include/pdi/context.h +++ b/pdi/include/pdi/context.h @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2015-2024 Commissariat a l'energie atomique et aux energies alternatives (CEA) + * Copyright (C) 2015-2026 Commissariat a l'energie atomique et aux energies alternatives (CEA) * Copyright (C) 2021 Institute of Bioorganic Chemistry Polish Academy of Science (PSNC) * All rights reserved. * @@ -37,6 +37,7 @@ #include #include #include +#include namespace PDI { @@ -114,6 +115,11 @@ class PDI_EXPORT Context */ virtual Logger& logger() = 0; + /** Timer getter + * \return timer + */ + virtual Timer& timer() = 0; + /** Callbacks of the context * \return context callbacks */ diff --git a/pdi/include/pdi/context_proxy.h b/pdi/include/pdi/context_proxy.h index 2cefc9ed9..f3bbbffa5 100644 --- a/pdi/include/pdi/context_proxy.h +++ b/pdi/include/pdi/context_proxy.h @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2021-2024 Commissariat a l'energie atomique et aux energies alternatives (CEA) + * Copyright (C) 2021-2026 Commissariat a l'energie atomique et aux energies alternatives (CEA) * Copyright (C) 2019-2021 Institute of Bioorganic Chemistry Polish Academy of Science (PSNC) * All rights reserved. * @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -44,6 +45,8 @@ class PDI_EXPORT Context_proxy: public Context /// Logger of the plugin Logger m_plugin_logger; + Timer* m_timer; + public: /** Creates Context proxy without plugin logger * \param[in] ctx context to make a proxy @@ -101,6 +104,12 @@ class PDI_EXPORT Context_proxy: public Context */ Logger& logger() override; + /** Returns plugin timer + * + * \return plugin timer + */ + Timer& timer() override; + /** Returns pdi core logger * * \return pdi core logger diff --git a/pdi/include/pdi/timer.h b/pdi/include/pdi/timer.h new file mode 100644 index 000000000..8fabbc611 --- /dev/null +++ b/pdi/include/pdi/timer.h @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (C) 2026 Commissariat a l'energie atomique et aux energies alternatives (CEA) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of CEA nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#ifndef PDI_TIMER_H_ +#define PDI_TIMER_H_ + +#include +#include +#include +#include +#include + +#include + +namespace PDI { + +class PDI_EXPORT Timer +{ +public: + Timer(const Timer&) = delete; + + Timer() {} + + void operator= (const Timer&) = delete; + + void startTimer(const std::string& name); + void stopTimer(const std::string& name); + void printReport() const; + void printReport(const std::string& name) const; + + void enable_timer(const bool choice) { timer_enabled = choice; } + +private: + std::map start_times; + std::map accumulated_times; + bool timer_enabled = false; +}; +} // namespace PDI +#endif // PDI_TIMER_H_ diff --git a/pdi/src/context_proxy.cxx b/pdi/src/context_proxy.cxx index c7177acd4..a49565750 100644 --- a/pdi/src/context_proxy.cxx +++ b/pdi/src/context_proxy.cxx @@ -26,6 +26,7 @@ #include "pdi/context.h" #include "pdi/context_proxy.h" #include "pdi/logger.h" +#include "pdi/timer.h" using std::string; @@ -34,10 +35,12 @@ namespace PDI { Context_proxy::Context_proxy(Context& ctx) : m_real_context{ctx} + , m_timer{&(ctx.timer())} {} Context_proxy::Context_proxy(Context& ctx, const string& logger_name, PC_tree_t logging_tree) : m_real_context{ctx} + , m_timer{&(ctx.timer())} , m_plugin_logger{m_real_context.logger(), logger_name, logging_tree} {} @@ -91,6 +94,11 @@ Logger& Context_proxy::logger() return m_plugin_logger; } +Timer& Context_proxy::timer() +{ + return *m_timer; +} + Logger& Context_proxy::pdi_core_logger() { return m_real_context.logger(); diff --git a/pdi/src/global_context.cxx b/pdi/src/global_context.cxx index 3e06b52a7..270bc41cf 100644 --- a/pdi/src/global_context.cxx +++ b/pdi/src/global_context.cxx @@ -107,6 +107,11 @@ Global_context::Global_context(PC_tree_t conf) , m_plugins{*this, conf} , m_callbacks{*this} { + PC_tree_t timer_node = PC_get(conf, ".timer"); + if (!PC_status(timer_node)) { + m_timer.enable_timer(to_bool(timer_node)); + } + // load basic datatypes Datatype_template::load_basic_datatypes(*this); // load user datatypes @@ -183,6 +188,11 @@ Logger& Global_context::logger() return m_logger; } +Timer& Global_context::timer() +{ + return m_timer; +} + Datatype_template_sptr Global_context::datatype(PC_tree_t node) { string type; diff --git a/pdi/src/global_context.h b/pdi/src/global_context.h index 42e6a560e..36d5c8253 100644 --- a/pdi/src/global_context.h +++ b/pdi/src/global_context.h @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2015-2025 Commissariat a l'energie atomique et aux energies alternatives (CEA) + * Copyright (C) 2015-2026 Commissariat a l'energie atomique et aux energies alternatives (CEA) * Copyright (C) 2021 Institute of Bioorganic Chemistry Polish Academy of Science (PSNC) * All rights reserved. * @@ -41,6 +41,7 @@ #include "pdi/logger.h" #include "pdi/plugin.h" #include "pdi/ref_any.h" +#include "pdi/timer.h" #include "plugin_store.h" @@ -57,6 +58,9 @@ class PDI_EXPORT Global_context: public Context /// Global logger of PDI, should be constructed first, destroyed last Logger m_logger; + /// Global timer of PDI + Timer m_timer; + /// Datatype_template constructors available in PDI std::unordered_map m_datatype_parsers; @@ -119,6 +123,8 @@ class PDI_EXPORT Global_context: public Context Logger& logger() override; + Timer& timer() override; + Datatype_template_sptr datatype(PC_tree_t node) override; void add_datatype(const std::string&, Datatype_template_parser) override; diff --git a/pdi/src/pdi.cxx b/pdi/src/pdi.cxx index 03ed040f7..2def0639a 100644 --- a/pdi/src/pdi.cxx +++ b/pdi/src/pdi.cxx @@ -42,6 +42,7 @@ #include "pdi/paraconf_wrapper.h" #include "pdi/plugin.h" #include "pdi/ref_any.h" +#include "pdi/timer.h" #include "global_context.h" @@ -170,6 +171,7 @@ try { g_transaction.clear(); g_transaction_data.clear(); Global_context::init(conf); + Global_context::context().timer().startTimer("PDI"); return PDI_OK; } catch (const Error& e) { return g_error_context.return_err(e); @@ -184,6 +186,8 @@ try { Paraconf_wrapper fw; g_transaction.clear(); g_transaction_data.clear(); + Global_context::context().timer().stopTimer("PDI"); + Global_context::context().timer().printReport(); Global_context::finalize(); return PDI_OK; } catch (const Error& e) { diff --git a/pdi/src/timer.cxx b/pdi/src/timer.cxx new file mode 100644 index 000000000..44d7e3de6 --- /dev/null +++ b/pdi/src/timer.cxx @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (C) 2026 Commissariat a l'energie atomique et aux energies alternatives (CEA) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of CEA nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include +#include + +#include "global_context.h" + +namespace PDI { + +// Start a timer by name +void Timer::startTimer(const std::string& name) +{ + if (!timer_enabled) return; + if (start_times.find(name) != start_times.end()) { + Global_context::context().logger().error("Timer for {} is already running. Ignoring the start", name); + return; + } + start_times[name] = std::chrono::high_resolution_clock::now(); +} + +// Stop a timer and accumulate the duration +void Timer::stopTimer(const std::string& name) +{ + if (!timer_enabled) return; + auto it = start_times.find(name); + if (it == start_times.end()) { + Global_context::context().logger().error("Error: Cannot end timer for {} because it was never started.", name); + return; + } + + auto end_time = std::chrono::high_resolution_clock::now(); + std::chrono::duration elapsed = end_time - start_times[name]; + + accumulated_times[name] += elapsed.count(); + start_times.erase(it); +} + +// Output the results of all timers +void Timer::printReport() const +{ + if (!timer_enabled) return; + for (const auto& [name, duration]: accumulated_times) { + Global_context::context().logger().info("Totale time spent for {} : {} seconds", name, duration); + } +} + +// Output the results of timer with name +void Timer::printReport(const std::string& name) const +{ + if (!timer_enabled) return; + auto it = accumulated_times.find(name); + if (it != accumulated_times.end()) { + Global_context::context().logger().info("Totale time spent for {} : {} seconds", name, it->second); + } else { + Global_context::context().logger().error("Cannot find timer for {}.", name); + } +} + +} // namespace PDI diff --git a/pdi/tests/mocks/context_mock.h b/pdi/tests/mocks/context_mock.h index 566c855c1..52cc92b06 100644 --- a/pdi/tests/mocks/context_mock.h +++ b/pdi/tests/mocks/context_mock.h @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2021-2024 Commissariat a l'energie atomique et aux energies alternatives (CEA) + * Copyright (C) 2021-2026 Commissariat a l'energie atomique et aux energies alternatives (CEA) * Copyright (C) 2018-2021 Institute of Bioorganic Chemistry Polish Academy of Science (PSNC) * All rights reserved. * @@ -52,6 +52,7 @@ struct MockContext: public PDI::Context { MOCK_METHOD1(event, void(const char*)); MOCK_METHOD0(logger, PDI::Logger&()); + MOCK_METHOD0(timer, PDI::Timer&()); MOCK_METHOD1(datatype, PDI::Datatype_template_sptr(PC_tree_t)); MOCK_METHOD2(add_datatype, void(const std::string&, Datatype_template_parser)); diff --git a/plugins/decl_hdf5/decl_hdf5.cxx b/plugins/decl_hdf5/decl_hdf5.cxx index 6a910d6ed..7df7bcf88 100644 --- a/plugins/decl_hdf5/decl_hdf5.cxx +++ b/plugins/decl_hdf5/decl_hdf5.cxx @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2015-2019 Commissariat a l'energie atomique et aux energies alternatives (CEA) + * Copyright (C) 2015-2026 Commissariat a l'energie atomique et aux energies alternatives (CEA) * Copyright (C) 2021 Institute of Bioorganic Chemistry Polish Academy of Science (PSNC) * All rights reserved. * @@ -114,18 +114,22 @@ class decl_hdf5_plugin: public Plugin void data(const std::string& name, Ref ref) { + context().timer().startTimer(pretty_name()); Hdf5_error_handler _; for (auto&& op: m_data[name]) { op.execute(context()); } + context().timer().stopTimer(pretty_name()); } void event(const std::string& event) { + context().timer().startTimer(pretty_name()); Hdf5_error_handler _; for (auto&& op: m_events[event]) { op.execute(context()); } + context().timer().stopTimer(pretty_name()); } /** Pretty name for the plugin that will be shown in the logger diff --git a/plugins/decl_netcdf/dnc_file_context.cxx b/plugins/decl_netcdf/dnc_file_context.cxx index fb8efb0b8..e52bc2cf7 100644 --- a/plugins/decl_netcdf/dnc_file_context.cxx +++ b/plugins/decl_netcdf/dnc_file_context.cxx @@ -212,6 +212,7 @@ Dnc_variable* Dnc_file_context::variable(const std::string& desc_name, const std void Dnc_file_context::execute(const std::string& desc_name, PDI::Ref ref) { + m_ctx.timer().startTimer(pretty_name()); if (m_when.to_long(m_ctx)) { std::list variables_holder; // memory for Variables created from descriptor @@ -276,10 +277,12 @@ void Dnc_file_context::execute(const std::string& desc_name, PDI::Ref ref) nc_file.get_sizeof_variable(size_it->first, dataset_name, ref); } } + m_ctx.timer().stopTimer(pretty_name()); } void Dnc_file_context::execute() { + m_ctx.timer().startTimer(pretty_name()); if (m_when.to_long(m_ctx)) { std::list variables_holder; std::vector variables_to_get; @@ -354,6 +357,7 @@ void Dnc_file_context::execute() i++; } } + m_ctx.timer().stopTimer(pretty_name()); } } // namespace decl_netcdf diff --git a/plugins/decl_netcdf/dnc_file_context.h b/plugins/decl_netcdf/dnc_file_context.h index 60cbb9ac1..f57aee4fd 100644 --- a/plugins/decl_netcdf/dnc_file_context.h +++ b/plugins/decl_netcdf/dnc_file_context.h @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2024 Commissariat a l'energie atomique et aux energies alternatives (CEA) + * Copyright (C) 2024-2026 Commissariat a l'energie atomique et aux energies alternatives (CEA) * Copyright (C) 2020 Institute of Bioorganic Chemistry Polish Academy of Science (PSNC) * All rights reserved. * @@ -109,6 +109,8 @@ class Dnc_file_context * \param other Dnc_file_context to move */ Dnc_file_context(Dnc_file_context&& other) noexcept; + + static std::string pretty_name() { return "Decl'NetCDF"; } }; } // namespace decl_netcdf diff --git a/plugins/json/json.cxx b/plugins/json/json.cxx index e4c9ab9f9..dcd7ee594 100644 --- a/plugins/json/json.cxx +++ b/plugins/json/json.cxx @@ -69,6 +69,7 @@ class json_plugin: public PDI::Plugin ~json_plugin() { context().logger().info("Closing plugin"); } + static std::string pretty_name() { return "JSON"; } private: /** Read the configuration file @@ -328,6 +329,7 @@ class json_plugin: public PDI::Plugin */ void write_data(const std::string& data_name, Ref_r&& reference) { + context().timer().startTimer(pretty_name()); Logger& logger = context().logger(); for (const auto& [condition, fpath]: m_data_to_path_map[data_name]) { @@ -374,6 +376,7 @@ class json_plugin: public PDI::Plugin } logger.debug("Done ! {} ", data_name); } + context().timer().stopTimer(pretty_name()); } }; diff --git a/plugins/pycall/pycall.cxx b/plugins/pycall/pycall.cxx index 84a91eb7e..7116b34c8 100644 --- a/plugins/pycall/pycall.cxx +++ b/plugins/pycall/pycall.cxx @@ -142,7 +142,7 @@ class Trigger /** Call the function that has been registered * \param ctx the PDI context for this trigger */ - void call(Context& ctx) + void call(Context& ctx, const std::string& name) { // a python context we fill with exposed variables pydict pyscope = pymod::import("__main__").attr("__dict__"); @@ -153,7 +153,9 @@ class Trigger alias.expose(ctx, pyscope); } try { + ctx.timer().startTimer(name); pybind11::exec(m_code, pyscope); + ctx.timer().stopTimer(name); } catch (const std::exception& e) { ctx.logger().error("while calling python, caught exception: {}", e.what()); } catch (...) { @@ -190,7 +192,7 @@ struct pycall_plugin: Plugin { ctx.callbacks().add_event_callback( [&ctx, triggers](const std::string&) mutable { for (auto&& trigger: triggers) { - trigger.call(ctx); + trigger.call(ctx, pretty_name()); } }, to_string(PC_get(on_event, "{%d}", map_id)) @@ -198,7 +200,7 @@ struct pycall_plugin: Plugin { } else { Trigger event_trigger{to_string(PC_get(event, ".exec")), PC_get(event, ".with")}; ctx.callbacks().add_event_callback( - [&ctx, event_trigger](const std::string&) mutable { event_trigger.call(ctx); }, + [&ctx, event_trigger](const std::string&) mutable { event_trigger.call(ctx, pretty_name()); }, to_string(PC_get(on_event, "{%d}", map_id)) ); } @@ -210,7 +212,10 @@ struct pycall_plugin: Plugin { for (int map_id = 0; map_id < nb_data; map_id++) { string data_name = to_string(PC_get(on_data, "{%d}", map_id)); Trigger data_trigger{to_string(PC_get(on_data, "<%d>", map_id)), data_name}; - ctx.callbacks().add_data_callback([&ctx, data_trigger](const std::string&, Ref) mutable { data_trigger.call(ctx); }, data_name); + ctx.callbacks().add_data_callback( + [&ctx, data_trigger](const std::string&, Ref) mutable { data_trigger.call(ctx, pretty_name()); }, + data_name + ); } } diff --git a/plugins/serialize/serialize.cxx b/plugins/serialize/serialize.cxx index 7cfd575fb..58b7d5702 100644 --- a/plugins/serialize/serialize.cxx +++ b/plugins/serialize/serialize.cxx @@ -272,6 +272,7 @@ struct serialize_plugin: PDI::Plugin { */ void share_serialized(const std::string& desc_name, PDI::Ref ref) { + context().timer().startTimer(pretty_name()); std::string serialized_name = m_desc_to_serialize[desc_name]; context().logger().debug("Serializing `{}` as `{}`", desc_name, serialized_name); PDI::Datatype_sptr serialized_type = serialize_type(ref.type()); @@ -333,6 +334,7 @@ struct serialize_plugin: PDI::Plugin { ); m_serialized_remove_callback.emplace_back(serialized_name, remove_callback, PDI_IN); } + context().timer().stopTimer(pretty_name()); } void release_serialized(const std::string& desc_name, PDI::Ref ref) diff --git a/plugins/timer/CMakeLists.txt b/plugins/timer/CMakeLists.txt new file mode 100644 index 000000000..1be07867a --- /dev/null +++ b/plugins/timer/CMakeLists.txt @@ -0,0 +1,48 @@ +#============================================================================= +# Copyright (C) 2023-2026 Commissariat a l'energie atomique et aux energies alternatives (CEA) +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of CEA nor the names of its contributors may be used to +# endorse or promote products derived from this software without specific +# prior written permission. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +#============================================================================= + +cmake_minimum_required(VERSION 3.22...4.2) +project(pdi_timer_plugin LANGUAGES C CXX) + +# Includes +include(CTest) +include(GNUInstallDirs) + +# PDI +find_package(PDI REQUIRED COMPONENTS plugins) + +# The plugin +add_library(pdi_timer_plugin MODULE timer.cxx) +target_link_libraries(pdi_timer_plugin PUBLIC PDI::PDI_plugins ) + +# Installation +set(INSTALL_PDIPLUGINDIR "${PDI_DEFAULT_PLUGINDIR}" CACHE PATH "PDI plugins (${PDI_DEFAULT_PLUGINDIR})") +install(TARGETS pdi_timer_plugin + LIBRARY DESTINATION "${INSTALL_PDIPLUGINDIR}" +) + +# Tests +# if("${BUILD_TESTING}") +# add_subdirectory(tests/) +# endif() diff --git a/plugins/timer/README.md b/plugins/timer/README.md new file mode 100644 index 000000000..1bff9ea1e --- /dev/null +++ b/plugins/timer/README.md @@ -0,0 +1,13 @@ +data: + ... + +plugins: + timer: + - recording: [decl_hdf5_start_timer, decl_hdf5_end_timer] # decl_hdf5_start/end_timer are events in PDI, not visible to users + # - recording: [json_start_timer, json_end_timer] + decl_hdf5: + - file: output.h5 + write: data + json: + - file: out.json + write: [var1, var2] \ No newline at end of file diff --git a/plugins/timer/timer.cxx b/plugins/timer/timer.cxx new file mode 100644 index 000000000..d58da2f88 --- /dev/null +++ b/plugins/timer/timer.cxx @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (C) 2026 Commissariat a l'energie atomique et aux energies alternatives (CEA) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of CEA nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +using namespace PDI; + +/** The timer plugin +*/ +class timer_plugin: public PDI::Plugin +{ + // Map between data variables and a pair between condition and the output filenames + std::map start_times; + std::map accumulated_times; + bool timer_enabled = false; + +public: + timer_plugin(Context& ctx, PC_tree_t spec_tree) + : Plugin{ctx} + { + // initialize m_data_to_path_map from config.yml + // read_config_tree(ctx.logger(), spec_tree); + + ctx.logger().info("Plugin loaded successfully"); + } + + ~timer_plugin() { context().logger().info("Closing plugin"); } + + static std::string pretty_name() { return "TIMER"; } + +private: + /** Read the configuration file + * + * \param logger PDI's logger instance + * \param spec_tree the yaml tree + */ + // void read_config_tree(Logger& logger, PC_tree_t spec_tree) + // { + // } + +}; + +} // namespace +PDI_PLUGIN(timer) diff --git a/plugins/user_code/user_code.cxx b/plugins/user_code/user_code.cxx index d438f29ad..84067c924 100644 --- a/plugins/user_code/user_code.cxx +++ b/plugins/user_code/user_code.cxx @@ -159,8 +159,9 @@ class Trigger } /// call the function that has been registered - void call(Context& ctx) + void call(Context& ctx, const std::string& name) { + ctx.timer().startTimer(name); // all exposed aliases that will be unexposed on destroy vector exposed_aliases; for (auto&& alias: m_aliases) { @@ -174,6 +175,7 @@ class Trigger } catch (...) { ctx.logger().error("While calling user code, caught exception"); } + ctx.timer().stopTimer(name); } }; // class Trigger @@ -190,7 +192,7 @@ struct user_code_plugin: Plugin { each(one_event, [&](PC_tree_t function_name, PC_tree_t parameters) { Trigger event_trigger{to_string(function_name), parameters}; ctx.callbacks().add_event_callback( - [&ctx, event_trigger](const std::string& name) mutable { event_trigger.call(ctx); }, + [&ctx, event_trigger](const std::string& name) mutable { event_trigger.call(ctx, pretty_name()); }, to_string(event_name) ); }); @@ -205,7 +207,7 @@ struct user_code_plugin: Plugin { each(one_data, [&](PC_tree_t function_name, PC_tree_t parameters) { Trigger data_trigger{to_string(function_name), parameters}; ctx.callbacks().add_data_callback( - [&ctx, data_trigger](const std::string& name, Ref ref) mutable { data_trigger.call(ctx); }, + [&ctx, data_trigger](const std::string& name, Ref ref) mutable { data_trigger.call(ctx, pretty_name()); }, to_string(data_name) ); });