Skip to content

File tsc_timer.c

File List > base > tsc_timer.c

Go to the documentation of this file

/*
 * Copyright 2008 Search Solution Corporation
 * Copyright 2016 CUBRID Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

/*
 * tsc_timer.c - Time Stamp Counter(TSC) timer implementations
 */

#ident "$Id$"

#include "config.h"

#include <fcntl.h>
#include "tsc_timer.h"
#if !defined(WINDOWS)
#include <sys/time.h>
#endif
// XXX: SHOULD BE THE LAST INCLUDE HEADER
#include "memory_wrapper.hpp"

#define CHECK_CPU_FREQ(v) \
do { \
  if ((v) == 0) \
    { \
      (v) = get_clock_freq (); \
    } \
} while (0)

#define CALCULATE_ELAPSED_TIME_USEC(time, diff, freq) \
do { \
  (time)->tv_sec = (long)((diff) / (freq)); \
  (time)->tv_usec = (long)((((diff) % (freq)) * (TSC_UINT64) (1000000)) / (freq)); \
} while (0)

#define CALCULATE_ELAPSED_TIMEVAL(time, end, start) \
do { \
  (time)->tv_sec = (end).tv_sec - (start).tv_sec; \
  (time)->tv_usec = (end).tv_usec - (start).tv_usec; \
  if ((time)->tv_usec < 0) \
    { \
      (time)->tv_sec--; \
      (time)->tv_usec += 1000000; \
    } \
} while (0)

static int power_Savings = -1;
static TSC_UINT64 cpu_Clock_rate = 0;

static void check_power_savings (void);

/*
 * tsc_init() - initialize the tsc_timer
 */
void
tsc_init (void)
{
  check_power_savings ();
  cpu_Clock_rate = get_clock_freq ();
  return;
}

/*
 * tsc_getticks() - get the current Time Stamp Counter
 *   tck(out): current CPU ticks or timeval
 *
 * Note: Ticks does not mean the current TIME.
 */
void
tsc_getticks (TSC_TICKS * tck)
{
  if (power_Savings == 0)
    {
      tck->tc = getticks ();
    }
  else
    {
#if defined (WINDOWS)
      gettimeofday (&(tck->tv), NULL);
#else
      struct timespec ts;
      /* replace gettimeofday with clock_gettime for performance */
      clock_gettime (CLOCK_REALTIME_COARSE, &ts);
      tck->tv.tv_sec = ts.tv_sec;
      tck->tv.tv_usec = ts.tv_nsec / 1000;
#endif
    }
  return;
}

/*
 * tsc_elapsed_time_usec() - measure the elapsed time in microseconds
 *   tv(out)       : elapsed time (sec, usec)
 *   end_tick(in)  : end time
 *   start_tick(in): start time
 */
void
tsc_elapsed_time_usec (TSCTIMEVAL * tv, TSC_TICKS end_tick, TSC_TICKS start_tick)
{
  if (power_Savings == 0)
    {
      TSC_UINT64 diff_tsc;

      /* Sometimes the time goes backwards in the MULTI-CORE processor world. But it is a negligible level. */
      if (end_tick.tc < start_tick.tc)
    {
      tv->tv_sec = 0;
      tv->tv_usec = 0;
      return;
    }

      CHECK_CPU_FREQ (cpu_Clock_rate);
      diff_tsc = (TSC_UINT64) elapsed ((ticks) end_tick.tc, (ticks) start_tick.tc);
      CALCULATE_ELAPSED_TIME_USEC (tv, diff_tsc, cpu_Clock_rate);
    }
  else
    {
      CALCULATE_ELAPSED_TIMEVAL (tv, end_tick.tv, start_tick.tv);
    }
  return;
}

/*
 * tsc_elapsed_utime () - measure the elapsed time in microseconds (not
 *            seconds, microseconds like tsc_elapsed_time_usec).
 *
 * return      : Elapsed time (microseconds).
 * end_tick (in)   : End time.
 * start_tick (in) : Start time.
 */
UINT64
tsc_elapsed_utime (TSC_TICKS end_tick, TSC_TICKS start_tick)
{
  TSCTIMEVAL tv;
  tsc_elapsed_time_usec (&tv, end_tick, start_tick);
  return tv.tv_sec * 1000000LL + tv.tv_usec;
}

/*
 * tsc_start_time_usec() - get the current Time Stamp Counter
 *   tck(out): current CPU ticks or timeval
 *
 * Note: This function does exactly same thing with tsc_getticks().
 *       It pairs up with tsc_end_time_usec().
 */
void
tsc_start_time_usec (TSC_TICKS * tck)
{
  tsc_getticks (tck);
  return;
}

/*
 * tsc_end_time_usec() - measure the elapsed time in microseconds
 *   tv(out)       : elapsed time (sec, usec)
 *   start_tick(in): start time
 */
void
tsc_end_time_usec (TSCTIMEVAL * tv, TSC_TICKS start_tick)
{
  TSC_TICKS end_tick;
  tsc_getticks (&end_tick);

  if (power_Savings == 0)
    {
      TSC_UINT64 diff_tsc;
      if (end_tick.tc < start_tick.tc)
    {
      tv->tv_sec = 0;
      tv->tv_usec = 0;
      return;
    }

      CHECK_CPU_FREQ (cpu_Clock_rate);
      diff_tsc = (TSC_UINT64) elapsed ((ticks) end_tick.tc, (ticks) start_tick.tc);
      CALCULATE_ELAPSED_TIME_USEC (tv, diff_tsc, cpu_Clock_rate);
    }
  else
    {
      CALCULATE_ELAPSED_TIMEVAL (tv, end_tick.tv, start_tick.tv);
    }
  return;
}

/*
 * check_power_savings() - check power saving options
 */
static void
check_power_savings (void)
{
#if defined (WINDOWS)
  /*
   * Note: Windows's QueryPerformanceFrequency always returns
   *       the stable CPU or mainboard clock rate.
   */
  power_Savings = 0;

#elif defined (LINUX)
  /*
   * Note: 'power_saving value == zero' means that the CPU clock rate is fixed.
   */
  int fd_mc, fd_smt;
  char mc = 0, smt = 0;

  fd_mc = open ("/sys/devices/system/cpu/sched_mc_power_savings", O_RDONLY);
  if (fd_mc != -1)
    {
      char tmp = 0;
      ssize_t n;

      n = read (fd_mc, &tmp, 1);
      if (n > 0)
    {
      mc = tmp;
    }
      close (fd_mc);
    }

  fd_smt = open ("/sys/devices/system/cpu/sched_smt_power_savings", O_RDONLY);
  if (fd_smt != -1)
    {
      char tmp = 0;
      ssize_t n;

      n = read (fd_smt, &tmp, 1);
      if (n > 0)
    {
      smt = tmp;
    }
      close (fd_smt);
    }

  if (mc == '0' && smt == '0')
    {
      power_Savings = 0;
      return;
    }

  power_Savings = 1;

#else
  /*
   * Note: We assume that the unknown OS performs the power-saving policy.
   */
  power_Savings = 1;

#endif
  return;
}