Skip to content
Snippets Groups Projects
Commit b3caecf0 authored by Marco Clemencic's avatar Marco Clemencic
Browse files

Implement helper class for arrays of counters

See merge request !1484
parents f2781c31 7e3e87d9
No related branches found
No related tags found
1 merge request!1484Implement helper class for arrays of counters
Pipeline #6185766 failed
......@@ -55,6 +55,7 @@ gaudi_add_library(GaudiKernel
src/Lib/ContainedObject.cpp
src/Lib/ConversionSvc.cpp
src/Lib/Converter.cpp
src/Lib/CounterArray.cpp
src/Lib/DataHandle.cpp
src/Lib/DataHandleFinder.cpp
src/Lib/DataHandleHolderVisitor.cpp
......@@ -304,6 +305,9 @@ if(BUILD_TESTING)
gaudi_add_executable(test_Counters SOURCES tests/src/CountersUnitTest.cpp
LINK GaudiKernel Boost::unit_test_framework TEST)
gaudi_add_executable(test_CounterArray SOURCES tests/src/CounterArrayUnitTest.cpp
LINK GaudiKernel Boost::unit_test_framework TEST)
gaudi_add_executable(test_CounterHistos SOURCES tests/src/CounterHistosUnitTest.cpp
LINK GaudiKernel Boost::unit_test_framework TEST)
......
/***********************************************************************************\
* (c) Copyright 1998-2022 CERN for the benefit of the LHCb and ATLAS collaborations *
* *
* This software is distributed under the terms of the Apache version 2 licence, *
* copied verbatim in the file "LICENSE". *
* *
* In applying this licence, CERN does not waive the privileges and immunities *
* granted to it by virtue of its status as an Intergovernmental Organization *
* or submit itself to any jurisdiction. *
\***********************************************************************************/
#pragma once
#include <Gaudi/Accumulators.h>
#include <utility>
namespace Gaudi::Accumulators {
namespace details {
/**
* Default formating for counter names, only calling fmt::format
* on the text given at construction and passing the histo index as argument
*/
struct FormatCounterDefault {
std::string_view text;
FormatCounterDefault( std::string_view t ) : text{ t } {}
std::string operator()( size_t n );
};
/**
* internal class implementing an array of counters
* @see CounterArray
*/
template <typename Counter, std::size_t N>
struct CounterArrayInternal : std::array<Counter, N> {
/// constructor with callables for FormatName
template <typename OWNER, typename FormatName, std::size_t... Ns,
typename = typename std::enable_if_t<std::is_invocable_v<FormatName, int>>>
CounterArrayInternal( OWNER* owner, FormatName&& fname, std::integer_sequence<std::size_t, Ns...> )
: std::array<Counter, N>{ Counter{ owner, fname( Ns ) }... } {
static_assert( sizeof...( Ns ) < 1000, "Using CounterArray with 1000 arrays or more is prohibited. This "
"would lead to very long compilation times" );
}
/// constructor for strings, FormatCounterDefault is used as the default callable
template <typename OWNER, std::size_t... Ns>
CounterArrayInternal( OWNER* owner, std::string_view name, std::integer_sequence<std::size_t, Ns...> )
: std::array<Counter, N>{ Counter{ owner, FormatCounterDefault{ name }( Ns ) }... } {
static_assert( sizeof...( Ns ) < 1000, "Using CounterArray with 1000 arrays or more is prohibited. This "
"would lead to very long compilation times" );
}
};
} // namespace details
/**
* generic class implementing an array of counters
* The only addition to a raw array is the constructor that allows
* to build names of the counters automatically from the index of the
* counter in the array
* There are 2 possibilities :
* - if a string_view is given, it is used in a call to std::format(name, n);
* - if a callable is given, it is called on the index
* it should take a size_t and return some type convertible to string_view
* actual implementation is in CounterArrayInternal
*
* Typical usage :
* // Array of 5 simple counters with simple names. Names will be MyCounter-0, MyCounter-1, ...
* CounterArray<Counter<>, 5> counters{ &algo, "MyCounter-{}" };
* ++counters[1];
* // Array of 5 averaging counters with same simple names
* CounterArray<AveragingCounter<>, 5> avgCounters{ &algo, "MyCounter-{}" };
* avgCounters[2] += 3.14;
* // Array of 5 cimple counters with custom names. Names will be "0^2=0", "1^2=1", "2^2=4", ...
* CounterArray<Counter<>, 5> customCounters{
* &algo,
* []( int n ) { return fmt::format( "{}^2={}", n, n*n ); }
* }
* ++customCounters[3];
*/
template <typename Counter, std::size_t N>
struct CounterArray : details::CounterArrayInternal<Counter, N> {
template <typename OWNER, typename FormatName>
CounterArray( OWNER* owner, FormatName&& fname )
: details::CounterArrayInternal<Counter, N>( owner, fname, std::make_integer_sequence<std::size_t, N>{} ) {}
};
} // namespace Gaudi::Accumulators
......@@ -66,6 +66,29 @@ namespace Gaudi::Accumulators {
* - if 2 callables are given, they are called on the index
* they should take a size_t and return some type convertible to string_view
* actual implementation is in HistogramArrayInternal
*
* Typical usage :
* // Array of 5 1D histograms with simple names and titles
* // Names will be GaudiH1D-0, GaudiH1D-1, ... and similarly for titles
* Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<1>, 5> histo1d{
* &algo, "GaudiH1D-{}", "A Gaudi 1D histogram - number {}", { 21, -10.5, 10.5, "X" } };
* ++histo1d[3][-10.0];
* // Array of 5 2D weighted histograms with simple names and titles
* // Names will be Name0, Name1, ... and similarly for titles
* Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::WeightedHistogram<2>, 7> histo2dw{
* &algo, "Name{}", "Title {}", { 21, -10.5, 10.5, "X" }, { 21, -10.5, 10.5, "Y" } };
* histo2dw[1][{ -10.0, -10.0 }] += 0.25;
* // Array of histograms with custom name and titles
* // Names will be GaudiH1D-0-0, GaudiH1D-1-1, GaudiH1D-2-4, ...
* // Titles will be "Title 1 of 5", "Title 2 of 5", "Title 3 of 5", ...
* Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<1>, 5> histoCustom{
* &algo,
* []( int n ) { return fmt::format( "GaudiH1D-{}-{}", n, n*n ); },
* [nb = 5]( int n ) {
* return fmt::format( "Title {} of {}", n+1, nb );
* },
* { 21, -10.5, 10.5, "X" } };
* ++histoCustom[2][-10.0];
*/
template <typename Histo, std::size_t N, typename Seq>
struct HistogramArrayBase;
......
/***********************************************************************************\
* (c) Copyright 1998-2019 CERN for the benefit of the LHCb and ATLAS collaborations *
* *
* This software is distributed under the terms of the Apache version 2 licence, *
* copied verbatim in the file "LICENSE". *
* *
* In applying this licence, CERN does not waive the privileges and immunities *
* granted to it by virtue of its status as an Intergovernmental Organization *
* or submit itself to any jurisdiction. *
\***********************************************************************************/
#include <Gaudi/Accumulators/CounterArray.h>
#include <fmt/format.h>
#if FMT_VERSION < 80000
namespace fmt {
template <typename T>
const T& runtime( const T& v ) {
return v;
}
} // namespace fmt
#endif
std::string Gaudi::Accumulators::details::FormatCounterDefault::operator()( size_t n ) {
return fmt::format( fmt::runtime( text ), n );
}
/***********************************************************************************\
* (c) Copyright 1998-2021 CERN for the benefit of the LHCb and ATLAS collaborations *
* *
* This software is distributed under the terms of the Apache version 2 licence, *
* copied verbatim in the file "LICENSE". *
* *
* In applying this licence, CERN does not waive the privileges and immunities *
* granted to it by virtue of its status as an Intergovernmental Organization *
* or submit itself to any jurisdiction. *
\***********************************************************************************/
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE test_CounterArray
#include <Gaudi/Accumulators/CounterArray.h>
#include <boost/test/unit_test.hpp>
#include <fmt/format.h>
#include <iostream>
#include <vector>
namespace {
// Mock code for the test
struct MonitoringHub : Gaudi::Monitoring::Hub {};
struct ServiceLocator {
MonitoringHub& monitoringHub() { return m_monitHub; }
MonitoringHub m_monitHub{};
};
struct Algo {
ServiceLocator* serviceLocator() { return &m_serviceLocator; }
std::string name() { return ""; }
ServiceLocator m_serviceLocator{};
};
struct CounterSink : public Gaudi::Monitoring::Hub::Sink {
virtual void registerEntity( Gaudi::Monitoring::Hub::Entity ent ) override { m_entities.push_back( ent ); }
virtual void removeEntity( Gaudi::Monitoring::Hub::Entity const& ent ) override {
auto it = std::find( begin( m_entities ), end( m_entities ), ent );
if ( it != m_entities.end() ) m_entities.erase( it );
}
std::vector<Gaudi::Monitoring::Hub::Entity> m_entities;
};
// Little helper for using automatic nlohmann conversion mechanism
template <typename T>
nlohmann::json toJSON( T const& t ) {
nlohmann::json j = t;
return t;
}
} // namespace
BOOST_AUTO_TEST_CASE( test_counter_array ) {
Algo algo;
CounterSink sink;
algo.serviceLocator()->monitoringHub().addSink( &sink );
{
// testing an array of 5 simple counters, with "standard" names
Gaudi::Accumulators::CounterArray<Gaudi::Accumulators::Counter<>, 5> counters{ &algo, "Counter-{}" };
// check increment
for ( unsigned int i = 0; i < 5; i++ ) ++counters[i]; // increment counters
for ( unsigned int i = 0; i < 5; i++ ) BOOST_TEST( counters[i].nEntries() == 1 );
// check names
unsigned int i = 0;
for ( auto& entity : sink.m_entities ) {
BOOST_TEST( entity.name == fmt::format( "Counter-{}", i ) );
i++;
}
// check json output
for ( unsigned int i = 0; i < 5; i++ ) { BOOST_TEST( toJSON( counters[i] ).at( "nEntries" ) == 1 ); }
}
{
// testing an array of averaging counters with "standard" names
Gaudi::Accumulators::CounterArray<Gaudi::Accumulators::AveragingCounter<>, 5> counters{ &algo, "AvgCounter-{}" };
// check increment
for ( unsigned int i = 0; i < 5; i++ ) counters[i] += i; // increment counters
for ( unsigned int i = 0; i < 5; i++ ) {
BOOST_TEST( counters[i].nEntries() == 1 );
BOOST_TEST( counters[i].sum() == i );
}
// check names
unsigned int i = 0;
for ( auto& entity : sink.m_entities ) {
BOOST_TEST( entity.name == fmt::format( "AvgCounter-{}", i ) );
i++;
}
// check json output
for ( unsigned int i = 0; i < 5; i++ ) {
BOOST_TEST( toJSON( counters[i] ).at( "nEntries" ) == 1 );
BOOST_TEST( toJSON( counters[i] ).at( "sum" ) == i );
}
}
{
// testing an array of simple counters, with non standard names
Gaudi::Accumulators::CounterArray<Gaudi::Accumulators::Counter<>, 5> counters{
&algo, []( int n ) { return fmt::format( "Counter-{}-{}", n, n ^ 2 ); } };
// check increment
for ( unsigned int i = 0; i < 5; i++ ) ++counters[i]; // increment counters
for ( unsigned int i = 0; i < 5; i++ ) { BOOST_TEST( counters[i].nEntries() == 1 ); }
// check names
unsigned int i = 0;
for ( auto& entity : sink.m_entities ) {
BOOST_TEST( entity.name == fmt::format( "Counter-{}-{}", i, i ^ 2 ) );
i++;
}
// check json output
for ( unsigned int i = 0; i < 5; i++ ) { BOOST_TEST( toJSON( counters[i] ).at( "nEntries" ) == 1 ); }
}
}
......@@ -14,7 +14,6 @@
#include <boost/test/unit_test.hpp>
#include <deque>
#include <iostream>
namespace {
......@@ -30,14 +29,6 @@ namespace {
std::string name() { return ""; }
ServiceLocator m_serviceLocator{};
};
struct HistSink : public Gaudi::Monitoring::Hub::Sink {
virtual void registerEntity( Gaudi::Monitoring::Hub::Entity ent ) override { m_entities.push_back( ent ); }
virtual void removeEntity( Gaudi::Monitoring::Hub::Entity const& ent ) override {
auto it = std::find( begin( m_entities ), end( m_entities ), ent );
if ( it != m_entities.end() ) m_entities.erase( it );
}
std::deque<Gaudi::Monitoring::Hub::Entity> m_entities;
};
// Little helper for using automatic nlohmann conversion mechanism
template <typename T>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment