Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-18 09:32:11

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