Back to home page

EIC code displayed by LXR

 
 

    


Warning, file /include/Gaudi/Timers/RdtscClock.h was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /***********************************************************************************\
0002 * (c) Copyright 1998-2020 CERN for the benefit of the LHCb and ATLAS collaborations *
0003 *                                                                                   *
0004 * This software is distributed under the terms of the Apache version 2 licence,     *
0005 * copied verbatim in the file "LICENSE".                                            *
0006 *                                                                                   *
0007 * In applying this licence, CERN does not waive the privileges and immunities       *
0008 * granted to it by virtue of its status as an Intergovernmental Organization        *
0009 * or submit itself to any jurisdiction.                                             *
0010 \***********************************************************************************/
0011 #pragma once
0012 
0013 #ifndef __x86_64__
0014 #  error "<Gaudi/Timers/RdtscClock.h> is only supported on x86"
0015 #else // not __x86_64__
0016 
0017 #  include <chrono>
0018 #  include <functional>
0019 #  include <ratio>
0020 #  include <thread>
0021 #  include <x86intrin.h>
0022 
0023 namespace Gaudi {
0024   namespace Timers {
0025     /**
0026      * @brief A std::chrono compatible Clock using rdtsc as its timing source
0027      *
0028      * This clock is not directly related to wall time but meant as a low-overhead
0029      * time interval measurement. The Precision template parameter determines the
0030      * internal precision of the clock (e.g. `std::milli`, `std::micro`). On first
0031      * invocation of `now()` the timer is calibrated against a wall-time clock,
0032      * which will have an overhead of several milliseconds. This can be avoided by
0033      * calling `calibrate()` explicitly before the first measurement.
0034      *
0035      */
0036     template <typename Precision = std::chrono::microseconds>
0037     class RdtscClock {
0038     public:
0039       // to meet requirements of TrivialClock:
0040       typedef typename Precision::rep             rep;
0041       typedef typename Precision::period          period;
0042       typedef std::chrono::duration<rep, period>  duration;
0043       typedef std::chrono::time_point<RdtscClock> time_point;
0044 
0045       static constexpr bool is_steady{ true };
0046 
0047       // some sanity checks:
0048       static_assert( period::num == 1, "The Precision of RdtscClock must be reducible to 1/N" );
0049       static_assert( std::ratio_less_equal<period, std::milli>(),
0050                      "The Precision of RdtscClock must be at least std::chrono::milliseconds" );
0051 
0052       /// Calibrate the RDTSC clock against wall time
0053       static rep calibrate() noexcept { return ticks_per_unit(); }
0054 
0055       static time_point now() noexcept { return time_point{ duration( __rdtsc() / ticks_per_unit() ) }; }
0056 
0057     private:
0058       static rep ticks_per_unit() noexcept {
0059         static rep ticks_per_unit = do_calibrate(); // local static guarantees thread-safety
0060         return ticks_per_unit;
0061       }
0062 
0063       static rep do_calibrate() noexcept {
0064         // Calibration time and conversion factor to unit of Precision
0065         static constexpr auto calibTime = std::chrono::milliseconds( 100 );
0066         static constexpr auto toPrec    = std::ratio_divide<std::milli, period>::num;
0067 
0068         // Calibrate against wall clock
0069         auto t1_ref = std::chrono::high_resolution_clock::now();
0070         auto t1     = __rdtsc();
0071         std::this_thread::sleep_for( calibTime );
0072         auto t2     = __rdtsc();
0073         auto t2_ref = std::chrono::high_resolution_clock::now();
0074 
0075         // Calculate ticks per unit of Precision
0076         auto dt_ref         = std::chrono::duration_cast<std::chrono::milliseconds>( t2_ref - t1_ref ).count();
0077         rep  ticks_per_unit = ( t2 - t1 ) / ( dt_ref * toPrec );
0078 
0079         return ticks_per_unit;
0080       }
0081     };
0082   } // namespace Timers
0083 } // namespace Gaudi
0084 #endif