Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-30 10:22:37

0001 /// \file ROOT/RNTupleMetrics.hxx
0002 /// \ingroup NTuple ROOT7
0003 /// \author Jakob Blomer <jblomer@cern.ch>
0004 /// \date 2019-08-27
0005 /// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
0006 /// is welcome!
0007 
0008 /*************************************************************************
0009  * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers.               *
0010  * All rights reserved.                                                  *
0011  *                                                                       *
0012  * For the licensing terms see $ROOTSYS/LICENSE.                         *
0013  * For the list of contributors see $ROOTSYS/README/CREDITS.             *
0014  *************************************************************************/
0015 
0016 #ifndef ROOT7_RNTupleMetrics
0017 #define ROOT7_RNTupleMetrics
0018 
0019 #include <ROOT/RConfig.hxx>
0020 #include <string_view>
0021 
0022 #include <TError.h>
0023 
0024 #include <atomic>
0025 #include <chrono>
0026 #include <cstdint>
0027 #include <ctime> // for CPU time measurement with clock()
0028 #include <functional>
0029 #include <limits>
0030 #include <memory>
0031 #include <ostream>
0032 #include <string>
0033 #include <vector>
0034 #include <utility>
0035 
0036 namespace ROOT {
0037 namespace Experimental {
0038 namespace Detail {
0039 
0040 class RNTupleMetrics;
0041 
0042 // clang-format off
0043 /**
0044 \class ROOT::Experimental::Detail::RNTuplePerfCounter
0045 \ingroup NTuple
0046 \brief A performance counter with a name and a unit, which can be activated on demand
0047 
0048 Derived classes decide on the counter type and implement printing of the value.
0049 */
0050 // clang-format on
0051 class RNTuplePerfCounter {
0052 private:
0053    /// Symbol to split name, unit, description, and value when printing
0054    static constexpr char kFieldSeperator = '|';
0055 
0056    std::string fName;
0057    std::string fUnit;
0058    std::string fDescription;
0059    bool fIsEnabled = false;
0060 
0061 public:
0062    RNTuplePerfCounter(const std::string &name, const std::string &unit, const std::string &desc)
0063       : fName(name), fUnit(unit), fDescription(desc) {}
0064    virtual ~RNTuplePerfCounter();
0065    void Enable() { fIsEnabled = true; }
0066    bool IsEnabled() const { return fIsEnabled; }
0067    std::string GetName() const { return fName; }
0068    std::string GetDescription() const { return fDescription; }
0069    std::string GetUnit() const { return fUnit; }
0070 
0071    virtual std::int64_t GetValueAsInt() const = 0;
0072    virtual std::string GetValueAsString() const = 0;
0073    std::string ToString() const;
0074 };
0075 
0076 
0077 // clang-format off
0078 /**
0079 \class ROOT::Experimental::Detail::RNTuplePlainCounter
0080 \ingroup NTuple
0081 \brief A non thread-safe integral performance counter
0082 */
0083 // clang-format on
0084 class RNTuplePlainCounter : public RNTuplePerfCounter {
0085 private:
0086    std::int64_t fCounter = 0;
0087 
0088 public:
0089    RNTuplePlainCounter(const std::string &name, const std::string &unit, const std::string &desc)
0090       : RNTuplePerfCounter(name, unit, desc)
0091    {
0092    }
0093 
0094    R__ALWAYS_INLINE void Inc() { ++fCounter; }
0095    R__ALWAYS_INLINE void Dec() { --fCounter; }
0096    R__ALWAYS_INLINE void Add(int64_t delta) { fCounter += delta; }
0097    R__ALWAYS_INLINE int64_t GetValue() const { return fCounter; }
0098    R__ALWAYS_INLINE void SetValue(int64_t val) { fCounter = val; }
0099    std::int64_t GetValueAsInt() const override { return fCounter; }
0100    std::string GetValueAsString() const override { return std::to_string(fCounter); }
0101 };
0102 
0103 
0104 // clang-format off
0105 /**
0106 \class ROOT::Experimental::Detail::RNTupleAtomicCounter
0107 \ingroup NTuple
0108 \brief A thread-safe integral performance counter
0109 */
0110 // clang-format on
0111 class RNTupleAtomicCounter : public RNTuplePerfCounter {
0112 private:
0113    std::atomic<std::int64_t> fCounter{0};
0114 
0115 public:
0116    RNTupleAtomicCounter(const std::string &name, const std::string &unit, const std::string &desc)
0117       : RNTuplePerfCounter(name, unit, desc) { }
0118 
0119    R__ALWAYS_INLINE
0120    void Inc() {
0121       if (R__unlikely(IsEnabled()))
0122          ++fCounter;
0123    }
0124 
0125    R__ALWAYS_INLINE
0126    void Dec() {
0127       if (R__unlikely(IsEnabled()))
0128          --fCounter;
0129    }
0130 
0131    R__ALWAYS_INLINE
0132    void Add(int64_t delta) {
0133       if (R__unlikely(IsEnabled()))
0134          fCounter += delta;
0135    }
0136 
0137    R__ALWAYS_INLINE
0138    int64_t XAdd(int64_t delta) {
0139       if (R__unlikely(IsEnabled()))
0140          return fCounter.fetch_add(delta);
0141       return 0;
0142    }
0143 
0144    R__ALWAYS_INLINE
0145    int64_t GetValue() const {
0146       if (R__unlikely(IsEnabled()))
0147          return fCounter.load();
0148       return 0;
0149    }
0150 
0151    R__ALWAYS_INLINE
0152    void SetValue(int64_t val) {
0153       if (R__unlikely(IsEnabled()))
0154          fCounter.store(val);
0155    }
0156 
0157    std::int64_t GetValueAsInt() const override { return GetValue(); }
0158    std::string GetValueAsString() const override { return std::to_string(GetValue()); }
0159 };
0160 
0161 
0162 // clang-format off
0163 /**
0164 \class ROOT::Experimental::Detail::RNTupleCalcPerf
0165 \ingroup NTuple
0166 \brief A metric element that computes its floating point value from other counters.
0167 */
0168 // clang-format on
0169 class RNTupleCalcPerf : public RNTuplePerfCounter {
0170 public:
0171    using MetricFunc_t = std::function<std::pair<bool, double>(const RNTupleMetrics &)>;
0172 
0173 private:
0174    RNTupleMetrics &fMetrics;
0175    const MetricFunc_t fFunc;
0176 
0177 public:
0178    RNTupleCalcPerf(const std::string &name, const std::string &unit, const std::string &desc,
0179                    RNTupleMetrics &metrics, MetricFunc_t &&func)
0180       : RNTuplePerfCounter(name, unit, desc), fMetrics(metrics), fFunc(std::move(func))
0181    {
0182    }
0183 
0184    double GetValue() const {
0185       auto ret = fFunc(fMetrics);
0186       if (ret.first)
0187          return ret.second;
0188       return std::numeric_limits<double>::quiet_NaN();
0189    }
0190 
0191    std::int64_t GetValueAsInt() const override {
0192       return static_cast<std::int64_t>(GetValue());
0193    }
0194 
0195    std::string GetValueAsString() const override {
0196       return std::to_string(GetValue());
0197    }
0198 };
0199 
0200 // clang-format off
0201 /**
0202 \class ROOT::Experimental::Detail::RNTupleTickCounter
0203 \ingroup NTuple
0204 \brief An either thread-safe or non thread safe counter for CPU ticks
0205 
0206 On print, the value is converted from ticks to ns.
0207 */
0208 // clang-format on
0209 template <typename BaseCounterT>
0210 class RNTupleTickCounter : public BaseCounterT {
0211 public:
0212    RNTupleTickCounter(const std::string &name, const std::string &unit, const std::string &desc)
0213       : BaseCounterT(name, unit, desc)
0214    {
0215       R__ASSERT(unit == "ns");
0216    }
0217 
0218    std::int64_t GetValueAsInt() const final {
0219       auto ticks = BaseCounterT::GetValue();
0220       return std::uint64_t((double(ticks) / double(CLOCKS_PER_SEC)) * (1000. * 1000. * 1000.));
0221    }
0222 
0223    std::string GetValueAsString() const final {
0224       return std::to_string(GetValueAsInt());
0225    }
0226 };
0227 
0228 
0229 // clang-format off
0230 /**
0231 \class ROOT::Experimental::Detail::RNTupleTimer
0232 \ingroup NTuple
0233 \brief Record wall time and CPU time between construction and destruction
0234 
0235 Uses RAII as a stop watch. Only the wall time counter is used to determine whether the timer is active.
0236 */
0237 // clang-format on
0238 template <typename WallTimeT, typename CpuTimeT>
0239 class RNTupleTimer {
0240 private:
0241    using Clock_t = std::chrono::steady_clock;
0242 
0243    WallTimeT &fCtrWallTime;
0244    CpuTimeT &fCtrCpuTicks;
0245    /// Wall clock time
0246    Clock_t::time_point fStartTime;
0247    /// CPU time
0248    clock_t fStartTicks = 0;
0249 
0250 public:
0251    RNTupleTimer(WallTimeT &ctrWallTime, CpuTimeT &ctrCpuTicks)
0252       : fCtrWallTime(ctrWallTime), fCtrCpuTicks(ctrCpuTicks)
0253    {
0254       if (!fCtrWallTime.IsEnabled())
0255          return;
0256       fStartTime = Clock_t::now();
0257       fStartTicks = clock();
0258    }
0259 
0260    ~RNTupleTimer() {
0261       if (!fCtrWallTime.IsEnabled())
0262          return;
0263       auto wallTimeNs = std::chrono::duration_cast<std::chrono::nanoseconds>(Clock_t::now() - fStartTime);
0264       fCtrWallTime.Add(wallTimeNs.count());
0265       fCtrCpuTicks.Add(clock() - fStartTicks);
0266    }
0267 
0268    RNTupleTimer(const RNTupleTimer &other) = delete;
0269    RNTupleTimer &operator =(const RNTupleTimer &other) = delete;
0270 };
0271 
0272 using RNTuplePlainTimer = RNTupleTimer<RNTuplePlainCounter, RNTupleTickCounter<RNTuplePlainCounter>>;
0273 using RNTupleAtomicTimer = RNTupleTimer<RNTupleAtomicCounter, RNTupleTickCounter<RNTupleAtomicCounter>>;
0274 
0275 
0276 // clang-format off
0277 /**
0278 \class ROOT::Experimental::Detail::RNTupleMetrics
0279 \ingroup NTuple
0280 \brief A collection of Counter objects with a name, a unit, and a description.
0281 
0282 The class owns the counters; on registration of a new
0283 */
0284 // clang-format on
0285 class RNTupleMetrics {
0286 private:
0287    /// Symbol to split metrics name from counter / sub metrics name
0288    static constexpr char kNamespaceSeperator = '.';
0289 
0290    std::vector<std::unique_ptr<RNTuplePerfCounter>> fCounters;
0291    std::vector<RNTupleMetrics *> fObservedMetrics;
0292    std::string fName;
0293    bool fIsEnabled = false;
0294 
0295    bool Contains(const std::string &name) const;
0296 
0297 public:
0298    explicit RNTupleMetrics(const std::string &name) : fName(name) {}
0299    RNTupleMetrics(const RNTupleMetrics &other) = delete;
0300    RNTupleMetrics & operator=(const RNTupleMetrics &other) = delete;
0301    RNTupleMetrics(RNTupleMetrics &&other) = default;
0302    RNTupleMetrics & operator=(RNTupleMetrics &&other) = default;
0303    ~RNTupleMetrics() = default;
0304 
0305    // TODO(jblomer): return a reference
0306    template <typename CounterPtrT, class... Args>
0307    CounterPtrT MakeCounter(const std::string &name, Args&&... args)
0308    {
0309       R__ASSERT(!Contains(name));
0310       auto counter = std::make_unique<std::remove_pointer_t<CounterPtrT>>(name, std::forward<Args>(args)...);
0311       auto ptrCounter = counter.get();
0312       fCounters.emplace_back(std::move(counter));
0313       return ptrCounter;
0314    }
0315 
0316    /// Searches counters registered in this object only. Returns nullptr if `name` is not found.
0317    const RNTuplePerfCounter *GetLocalCounter(std::string_view name) const;
0318    /// Searches this object and all the observed sub metrics. `name` must start with the prefix used
0319    /// by this RNTupleMetrics instance. Returns nullptr if `name` is not found.
0320    const RNTuplePerfCounter *GetCounter(std::string_view name) const;
0321 
0322    void ObserveMetrics(RNTupleMetrics &observee);
0323 
0324    void Print(std::ostream &output, const std::string &prefix = "") const;
0325    void Enable();
0326    bool IsEnabled() const { return fIsEnabled; }
0327 };
0328 
0329 } // namespace Detail
0330 } // namespace Experimental
0331 } // namespace ROOT
0332 
0333 #endif