From 0a9b527c8c17e78fca09561324e9fd853210fd36 Mon Sep 17 00:00:00 2001
From: Dominic Kempf <dominic.kempf@iwr.uni-heidelberg.de>
Date: Thu, 27 Apr 2017 14:55:28 +0200
Subject: [PATCH] Add TSC timers

---
 CMakeLists.txt                       |   7 +
 dune/perftool/CMakeLists.txt         |   2 +
 dune/perftool/common/CMakeLists.txt  |   7 +
 dune/perftool/common/timer.hh        | 113 ++-------------
 dune/perftool/common/timer_chrono.hh | 114 ++++++++++++++++
 dune/perftool/common/timer_tsc.hh    | 112 +++++++++++++++
 dune/perftool/common/tsc.cc          | 197 +++++++++++++++++++++++++++
 dune/perftool/common/tsc.hh          | 108 +++++++++++++++
 8 files changed, 555 insertions(+), 105 deletions(-)
 create mode 100644 dune/perftool/common/CMakeLists.txt
 create mode 100644 dune/perftool/common/timer_chrono.hh
 create mode 100644 dune/perftool/common/timer_tsc.hh
 create mode 100644 dune/perftool/common/tsc.cc
 create mode 100644 dune/perftool/common/tsc.hh

diff --git a/CMakeLists.txt b/CMakeLists.txt
index e7ab965f..912355b9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -17,6 +17,13 @@ include(DuneMacros)
 
 # start a dune project with information from dune.module
 dune_project()
+
+dune_add_library(duneperftool dune/perftool/common/tsc.cc)
+
+dune_target_enable_all_packages(duneperftool)
+
+dune_register_package_flags(LIBRARIES duneperftool)
+
 dune_enable_all_packages()
 
 add_subdirectory(dune/perftool)
diff --git a/dune/perftool/CMakeLists.txt b/dune/perftool/CMakeLists.txt
index 9c5a06fc..7533fd57 100644
--- a/dune/perftool/CMakeLists.txt
+++ b/dune/perftool/CMakeLists.txt
@@ -1,3 +1,5 @@
+add_subdirectory(common)
+
 install(FILES vectorclass/dispatch_example.cpp
               vectorclass/instrset_detect.cpp
               vectorclass/instrset.h
diff --git a/dune/perftool/common/CMakeLists.txt b/dune/perftool/common/CMakeLists.txt
new file mode 100644
index 00000000..31520e34
--- /dev/null
+++ b/dune/perftool/common/CMakeLists.txt
@@ -0,0 +1,7 @@
+install(FILES muladd_workarounds.hh
+              opcounter.hh
+              timer.hh
+              tsc.hh
+              vectorclass.hh
+        DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dune/perftool/common
+        )
diff --git a/dune/perftool/common/timer.hh b/dune/perftool/common/timer.hh
index d7d208ad..cd1bc474 100644
--- a/dune/perftool/common/timer.hh
+++ b/dune/perftool/common/timer.hh
@@ -1,111 +1,14 @@
-#ifndef HP_TIMER_HH
-#define HP_TIMER_HH
+#ifndef DUNE_PERFTOOL_COMMON_TIMER_HH
+#define DUNE_PERFTOOL_COMMON_TIMER_HH
 
-#include <chrono>
-
-#include <dune/perftool/common/opcounter.hh>
-
-#define HP_TIMER_OPCOUNTER oc::OpCounter<double>
-
-#define HP_TIMER_DURATION(name) __hp_timer_##name##_duration
-#define HP_TIMER_STARTTIME(name) __hp_timer_##name##_start
-#define HP_TIMER_OPCOUNTERS_START(name) __hp_timer_##name##_counters_start
-#define HP_TIMER_OPCOUNTERS(name) __hp_timer_##name##_counters
-#define HP_TIMER_ELAPSED(name) std::chrono::duration_cast<std::chrono::duration<double> >( HP_TIMER_DURATION(name) ).count()
-
-
-
-#ifdef ENABLE_HP_TIMERS
-
-#ifdef ENABLE_COUNTER
-
-#define HP_DECLARE_TIMER(name)                               \
-  std::chrono::high_resolution_clock::duration HP_TIMER_DURATION(name);	\
-  std::chrono::high_resolution_clock::time_point HP_TIMER_STARTTIME(name); \
-  HP_TIMER_OPCOUNTER::Counters HP_TIMER_OPCOUNTERS_START(name); \
-  HP_TIMER_OPCOUNTER::Counters HP_TIMER_OPCOUNTERS(name);
-
-#define HP_TIMER_START(name) \
-  do { \
-  HP_TIMER_OPCOUNTERS_START(name) = HP_TIMER_OPCOUNTER::counters; \
-  HP_TIMER_STARTTIME(name) = std::chrono::high_resolution_clock::now(); \
-  } while(false)
-
-#define HP_TIMER_STOP(name) \
-  do { \
-  std::chrono::high_resolution_clock::time_point __hp_end_time = std::chrono::high_resolution_clock::now(); \
-  HP_TIMER_OPCOUNTERS(name) += HP_TIMER_OPCOUNTER::counters - HP_TIMER_OPCOUNTERS_START(name); \
-  HP_TIMER_DURATION(name) += __hp_end_time - HP_TIMER_STARTTIME(name); \
-  } while(false)
-
-#define HP_TIMER_RESET(name) \
-  do { \
-  HP_TIMER_DURATION(name) = std::chrono::high_resolution_clock::duration::zero(); \
-  HP_TIMER_OPCOUNTERS(name).reset(); \
-  } while (false)
-
-#else
-
-#define HP_DECLARE_TIMER(name)                               \
-  std::chrono::high_resolution_clock::duration HP_TIMER_DURATION(name);	\
-  std::chrono::high_resolution_clock::time_point HP_TIMER_STARTTIME(name);
-
-#define HP_TIMER_START(name) HP_TIMER_STARTTIME(name) = std::chrono::high_resolution_clock::now();
-#define HP_TIMER_STOP(name) \
-  do { \
-  std::chrono::high_resolution_clock::time_point __hp_end_time = std::chrono::high_resolution_clock::now(); \
-  HP_TIMER_DURATION(name) += __hp_end_time - HP_TIMER_STARTTIME(name); \
-  } while(false)
-
-#define HP_TIMER_RESET(name) HP_TIMER_DURATION(name) = std::chrono::high_resolution_clock::duration::zero();
-
-#endif // ENABLE_COUNTER
-
-#else // ENABLE_HP_TIMERS
-
-#define HP_DECLARE_TIMER(name)
-#define HP_TIMER_START(name)
-#define HP_TIMER_STOP(name)
-#define HP_TIMER_RESET(name)
-
-#endif // ENABLE_HP_TIMERS
-
-
-
-
-#ifdef ENABLE_COUNTER
-
-#define DUMP_TIMER(name,os,reset)\
-  if (HP_TIMER_ELAPSED(name) > 1e-12) \
-    os << ident << " " << #name << " time " << HP_TIMER_ELAPSED(name) << std::endl; \
-  HP_TIMER_OPCOUNTERS(name).reportOperations(os,ident,#name,reset);
-
-#define DUMP_AND_ACCUMULATE_TIMER(name,os,reset,time,ops)  \
-  if (HP_TIMER_ELAPSED(name) > 1e-12) \
-    os << ident << " " << #name << " time " << HP_TIMER_ELAPSED(name) << std::endl; \
-  time += HP_TIMER_ELAPSED(name); \
-  ops += HP_TIMER_OPCOUNTERS(name); \
-  HP_TIMER_OPCOUNTERS(name).reportOperations(os,ident,#name,reset);
-
-#elif defined ENABLE_HP_TIMERS
-
-#define DUMP_TIMER(name,os,reset)                       \
-  if (HP_TIMER_ELAPSED(name) > 1e-12) \
-    os << ident << " " << #name << " time " << HP_TIMER_ELAPSED(name) << std::endl; \
-  if (reset) HP_TIMER_RESET(name);
-
-#define DUMP_AND_ACCUMULATE_TIMER(name,os,reset,time,ops)  \
-  if (HP_TIMER_ELAPSED(name) > 1e-12) \
-    os << ident << " " << #name << " time " << HP_TIMER_ELAPSED(name) << std::endl; \
-  time += HP_TIMER_ELAPSED(name); \
-  if (reset) HP_TIMER_RESET(name);
+#define _GONE_THROUGH_TIMER_HH
 
+#if ENABLE_CHRONO_TIMER
+#include<dune/perftool/common/timer_chrono.hh>
 #else
-
-#define DUMP_TIMER(name,os,reset)
-#define DUMP_AND_ACCUMULATE_TIMER(name,os,reset,time,ops)
-
+#include<dune/perftool/common/timer_tsc.hh>
 #endif
 
+#undef _GONE_THROUGH_TIMER_HH
 
-#endif // HP_TIMER_HH
+#endif
diff --git a/dune/perftool/common/timer_chrono.hh b/dune/perftool/common/timer_chrono.hh
new file mode 100644
index 00000000..6e2098a1
--- /dev/null
+++ b/dune/perftool/common/timer_chrono.hh
@@ -0,0 +1,114 @@
+#ifndef DUNE_PERFTOOL_COMMON_TIMER_CHRONO_HH
+#define DUNE_PERFTOOL_COMMON_TIMER_CHRONO_HH
+
+#ifndef _GONE_THROUGH_TIMER_HH
+#error "Do not include timer_chrono.hh directly, instead use timer.hh"
+#endif
+
+#include <chrono>
+
+#include <dune/perftool/common/opcounter.hh>
+
+#define HP_TIMER_OPCOUNTER oc::OpCounter<double>
+
+#define HP_TIMER_DURATION(name) __hp_timer_##name##_duration
+#define HP_TIMER_STARTTIME(name) __hp_timer_##name##_start
+#define HP_TIMER_OPCOUNTERS_START(name) __hp_timer_##name##_counters_start
+#define HP_TIMER_OPCOUNTERS(name) __hp_timer_##name##_counters
+#define HP_TIMER_ELAPSED(name) std::chrono::duration_cast<std::chrono::duration<double> >( HP_TIMER_DURATION(name) ).count()
+
+
+
+#ifdef ENABLE_HP_TIMERS
+
+#ifdef ENABLE_COUNTER
+
+#define HP_DECLARE_TIMER(name)                               \
+  std::chrono::high_resolution_clock::duration HP_TIMER_DURATION(name);	\
+  std::chrono::high_resolution_clock::time_point HP_TIMER_STARTTIME(name); \
+  HP_TIMER_OPCOUNTER::Counters HP_TIMER_OPCOUNTERS_START(name); \
+  HP_TIMER_OPCOUNTER::Counters HP_TIMER_OPCOUNTERS(name);
+
+#define HP_TIMER_START(name) \
+  do { \
+  HP_TIMER_OPCOUNTERS_START(name) = HP_TIMER_OPCOUNTER::counters; \
+  HP_TIMER_STARTTIME(name) = std::chrono::high_resolution_clock::now(); \
+  } while(false)
+
+#define HP_TIMER_STOP(name) \
+  do { \
+  std::chrono::high_resolution_clock::time_point __hp_end_time = std::chrono::high_resolution_clock::now(); \
+  HP_TIMER_OPCOUNTERS(name) += HP_TIMER_OPCOUNTER::counters - HP_TIMER_OPCOUNTERS_START(name); \
+  HP_TIMER_DURATION(name) += __hp_end_time - HP_TIMER_STARTTIME(name); \
+  } while(false)
+
+#define HP_TIMER_RESET(name) \
+  do { \
+  HP_TIMER_DURATION(name) = std::chrono::high_resolution_clock::duration::zero(); \
+  HP_TIMER_OPCOUNTERS(name).reset(); \
+  } while (false)
+
+#else
+
+#define HP_DECLARE_TIMER(name)                               \
+  std::chrono::high_resolution_clock::duration HP_TIMER_DURATION(name);	\
+  std::chrono::high_resolution_clock::time_point HP_TIMER_STARTTIME(name);
+
+#define HP_TIMER_START(name) HP_TIMER_STARTTIME(name) = std::chrono::high_resolution_clock::now();
+#define HP_TIMER_STOP(name) \
+  do { \
+  std::chrono::high_resolution_clock::time_point __hp_end_time = std::chrono::high_resolution_clock::now(); \
+  HP_TIMER_DURATION(name) += __hp_end_time - HP_TIMER_STARTTIME(name); \
+  } while(false)
+
+#define HP_TIMER_RESET(name) HP_TIMER_DURATION(name) = std::chrono::high_resolution_clock::duration::zero();
+
+#endif // ENABLE_COUNTER
+
+#else // ENABLE_HP_TIMERS
+
+#define HP_DECLARE_TIMER(name)
+#define HP_TIMER_START(name)
+#define HP_TIMER_STOP(name)
+#define HP_TIMER_RESET(name)
+
+#endif // ENABLE_HP_TIMERS
+
+
+
+
+#ifdef ENABLE_COUNTER
+
+#define DUMP_TIMER(name,os,reset)\
+  if (HP_TIMER_ELAPSED(name) > 1e-12) \
+    os << ident << " " << #name << " time " << HP_TIMER_ELAPSED(name) << std::endl; \
+  HP_TIMER_OPCOUNTERS(name).reportOperations(os,ident,#name,reset);
+
+#define DUMP_AND_ACCUMULATE_TIMER(name,os,reset,time,ops)  \
+  if (HP_TIMER_ELAPSED(name) > 1e-12) \
+    os << ident << " " << #name << " time " << HP_TIMER_ELAPSED(name) << std::endl; \
+  time += HP_TIMER_ELAPSED(name); \
+  ops += HP_TIMER_OPCOUNTERS(name); \
+  HP_TIMER_OPCOUNTERS(name).reportOperations(os,ident,#name,reset);
+
+#elif defined ENABLE_HP_TIMERS
+
+#define DUMP_TIMER(name,os,reset)                       \
+  if (HP_TIMER_ELAPSED(name) > 1e-12) \
+    os << ident << " " << #name << " time " << HP_TIMER_ELAPSED(name) << std::endl; \
+  if (reset) HP_TIMER_RESET(name);
+
+#define DUMP_AND_ACCUMULATE_TIMER(name,os,reset,time,ops)  \
+  if (HP_TIMER_ELAPSED(name) > 1e-12) \
+    os << ident << " " << #name << " time " << HP_TIMER_ELAPSED(name) << std::endl; \
+  time += HP_TIMER_ELAPSED(name); \
+  if (reset) HP_TIMER_RESET(name);
+
+#else
+
+#define DUMP_TIMER(name,os,reset)
+#define DUMP_AND_ACCUMULATE_TIMER(name,os,reset,time,ops)
+
+#endif
+
+#endif // DUNE_PERFTOOL_COMMON_TIMER_CHRONO_HH
diff --git a/dune/perftool/common/timer_tsc.hh b/dune/perftool/common/timer_tsc.hh
new file mode 100644
index 00000000..65ee08e4
--- /dev/null
+++ b/dune/perftool/common/timer_tsc.hh
@@ -0,0 +1,112 @@
+#ifndef DUNE_PERFTOOL_COMMON_TIMER_TSC_HH
+#define DUNE_PERFTOOL_COMMON_TIMER_TSC_HH
+
+#ifndef _GONE_THROUGH_TIMER_HH
+#error "Do not include timer_tsc.hh directly, instead use timer.hh"
+#endif
+
+#include <dune/perftool/common/tsc.hh>
+#include <dune/perftool/common/opcounter.hh>
+
+#define HP_TIMER_OPCOUNTER oc::OpCounter<double>
+
+#define HP_TIMER_DURATION(name) __hp_timer_##name##_duration
+#define HP_TIMER_STARTTIME(name) __hp_timer_##name##_start
+#define HP_TIMER_OPCOUNTERS_START(name) __hp_timer_##name##_counters_start
+#define HP_TIMER_OPCOUNTERS(name) __hp_timer_##name##_counters
+
+
+
+#ifdef ENABLE_HP_TIMERS
+
+#ifdef ENABLE_COUNTER
+
+#define HP_DECLARE_TIMER(name)                               \
+  Dune::PDELab::TSC::Counter HP_TIMER_DURATION(name);	\
+  long long HP_TIMER_STARTTIME(name); \
+  HP_TIMER_OPCOUNTER::Counters HP_TIMER_OPCOUNTERS_START(name); \
+  HP_TIMER_OPCOUNTER::Counters HP_TIMER_OPCOUNTERS(name);
+
+#define HP_TIMER_START(name) \
+  do { \
+  HP_TIMER_OPCOUNTERS_START(name) = HP_TIMER_OPCOUNTER::counters; \
+  HP_TIMER_STARTTIME(name) = Dune::PDELab::TSC::start(); \
+  } while(false)
+
+#define HP_TIMER_STOP(name) \
+  do { \
+  long long __hp_end_time = Dune::PDELab::TSC::stop(); \
+  HP_TIMER_OPCOUNTERS(name) += HP_TIMER_OPCOUNTER::counters - HP_TIMER_OPCOUNTERS_START(name); \
+  HP_TIMER_DURATION(name) += Dune::PDELab::TSC::elapsed(HP_TIMER_STARTTIME(name),__hp_end_time); \
+  } while(false)
+
+#define HP_TIMER_RESET(name) \
+  do { \
+  HP_TIMER_DURATION(name) = Dune::PDELab::TSC::zero(); \
+  HP_TIMER_OPCOUNTERS(name).reset(); \
+  } while (false)
+
+#else
+
+#define HP_DECLARE_TIMER(name)                               \
+  Dune::PDELab::TSC::Counter HP_TIMER_DURATION(name);	\
+  long long HP_TIMER_STARTTIME(name);
+
+#define HP_TIMER_START(name) HP_TIMER_STARTTIME(name) = Dune::PDELab::TSC::start();
+#define HP_TIMER_STOP(name) \
+  do { \
+  long long __hp_end_time = Dune::PDELab::TSC::stop(); \
+  HP_TIMER_DURATION(name) += Dune::PDELab::TSC::elapsed(HP_TIMER_STARTTIME(name), __hp_end_time); \
+  } while(false)
+
+#define HP_TIMER_RESET(name) HP_TIMER_DURATION(name) = Dune::PDELab::TSC::zero();
+
+#endif // ENABLE_COUNTER
+
+#else // ENABLE_HP_TIMERS
+
+#define HP_DECLARE_TIMER(name)
+#define HP_TIMER_START(name)
+#define HP_TIMER_STOP(name)
+#define HP_TIMER_RESET(name)
+
+#endif // ENABLE_HP_TIMERS
+
+
+
+
+#ifdef ENABLE_COUNTER
+
+#define DUMP_TIMER(name,os,reset)\
+  if (HP_TIMER_DURATION(name) > 1e-12) \
+    os << ident << " " << #name << " time " << HP_TIMER_DURATION(name) << std::endl; \
+  HP_TIMER_OPCOUNTERS(name).reportOperations(os,ident,#name,reset);
+
+#define DUMP_AND_ACCUMULATE_TIMER(name,os,reset,time,ops)  \
+  if (HP_TIMER_DURATION(name) > 1e-12) \
+    os << ident << " " << #name << " time " << HP_TIMER_DURATION(name) << std::endl; \
+  time += HP_TIMER_DURATION(name); \
+  ops += HP_TIMER_OPCOUNTERS(name); \
+  HP_TIMER_OPCOUNTERS(name).reportOperations(os,ident,#name,reset);
+
+#elif defined ENABLE_HP_TIMERS
+
+#define DUMP_TIMER(name,os,reset)                       \
+  if (HP_TIMER_DURATION(name) > 1e-12) \
+    os << ident << " " << #name << " time " << HP_TIMER_DURATION(name) << std::endl; \
+  if (reset) HP_TIMER_RESET(name);
+
+#define DUMP_AND_ACCUMULATE_TIMER(name,os,reset,time,ops)  \
+  if (HP_TIMER_DURATION(name) > 1e-12) \
+    os << ident << " " << #name << " time " << HP_TIMER_DURATION(name) << std::endl; \
+  time += HP_TIMER_DURATION(name); \
+  if (reset) HP_TIMER_RESET(name);
+
+#else
+
+#define DUMP_TIMER(name,os,reset)
+#define DUMP_AND_ACCUMULATE_TIMER(name,os,reset,time,ops)
+
+#endif
+
+#endif // DUNE_PERFTOOL_COMMON_TIMER_TSC_HH
diff --git a/dune/perftool/common/tsc.cc b/dune/perftool/common/tsc.cc
new file mode 100644
index 00000000..320a14af
--- /dev/null
+++ b/dune/perftool/common/tsc.cc
@@ -0,0 +1,197 @@
+#include "config.h"
+
+#include <regex>
+#include <iostream>
+#include <algorithm>
+#include <vector>
+
+#ifdef __linux__
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#elif __APPLE__
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#endif
+
+#include <dune/perftool/common/tsc.hh>
+
+namespace Dune {
+  namespace PDELab {
+
+    namespace impl {
+
+#if __linux__
+
+      TSC::Counter get_tsc_frequency()
+      {
+        int pipe_fds[2];
+        if (pipe(pipe_fds) < 0)
+          DUNE_THROW(TSCError,"Failed to create pipe for communicating with dmesg");
+
+        pid_t pid = fork();
+
+        if (pid < 0)
+          DUNE_THROW(TSCError,"Failed to fork process for running dmesg");
+
+        double result = -1.0;
+
+        if (pid == 0)
+          {
+            if (close(0) < 0)
+              _exit(1);
+
+            if (close(2) < 0)
+              _exit(2);
+
+            if (dup2(pipe_fds[1],1) < 0)
+              _exit(3);
+
+            if (close(pipe_fds[0]) < 0)
+              _exit(4);
+
+            if (close(pipe_fds[1]) < 0)
+              _exit(5);
+
+            const char * args[] = {
+              "/bin/dmesg",
+              "-t",
+              nullptr
+            };
+            execvp(args[0],const_cast<char*const*>(args));
+
+            _exit(6);
+          }
+
+        if (pid > 0)
+          {
+
+            if (close(pipe_fds[1]) < 0)
+              DUNE_THROW(TSCError,"Failed to close write end of pipe");
+
+            std::regex regex("tsc:.*TSC.*?(\\d+\\.\\d+)\\s+MHz\n");
+
+            FILE* input = fdopen(pipe_fds[0],"r");
+
+            if (not input)
+              DUNE_THROW(TSCError,"Failed to create file object for reading dmesg output");
+
+            char buf[1024];
+            std::cmatch match;
+
+            while(fgets(buf,1024,input))
+              {
+                if(std::regex_match(buf,match,regex))
+                  {
+                    result = atof(match[1].first);
+                    break;
+                  }
+              }
+
+            if (fclose(input) != 0)
+              DUNE_THROW(TSCError,"Failed to close file object for reading dmesg output");
+
+            int status;
+            if (waitpid(pid,&status,0) < 0)
+              DUNE_THROW(TSCError,"Failed to clean up dmesg child process");
+
+            if (WEXITSTATUS(status) != 0)
+              DUNE_THROW(TSCError,"Child process failed with return status " << WEXITSTATUS(status));
+
+            if (result < 0)
+              DUNE_THROW(TSCError,"Could not find TSC frequency information in kernel log");
+
+
+          }
+
+        // The kernel logs the frequency in MHz
+        return static_cast<TSC::Counter>(result*1e6);
+
+      }
+
+
+#elif __APPLE__
+
+      TSC::Counter get_tsc_frequency()
+      {
+        std::int64_t frequency = 0;
+        std::size_t size = sizeof(frequency);
+        if (sysctlbyname("machdep.tsc.frequency",&frequency,&size,nullptr,0) < 0)
+          DUNE_THROW(TSCError,"Failed to read TSC frequency from sysctl machdep.tsc.frequency");
+        return frequency;
+      }
+
+#endif
+
+      TSC::Counter calibrate_tsc_overhead_min(std::size_t iterations)
+      {
+        TSC::Counter start, end, overhead = std::numeric_limits<TSC::Counter>::max();
+
+        for (std::size_t i = 0 ; i < iterations ; ++i)
+          {
+            start = TSC::start();
+            asm volatile("");
+            end = TSC::stop();
+            overhead = std::min(overhead,end - start);
+          }
+
+        return overhead;
+      }
+
+      TSC::Counter calibrate_tsc_overhead_median(std::size_t iterations)
+      {
+        TSC::Counter start, end;
+
+        std::vector<TSC::Counter> measurements(iterations);
+
+        for (auto& m : measurements)
+          {
+            start = TSC::start();
+            asm volatile("");
+            end = TSC::stop();
+            m = end - start;
+          }
+
+        std::sort(measurements.begin(),measurements.end());
+        return measurements[measurements.size()/2];
+      }
+
+    } // namespace impl
+
+
+    TSC::TSC(const Dune::ParameterTree* params)
+    {
+      if (params)
+        {
+          if (params->hasKey("frequency"))
+            _frequency = params->get<TSC::Counter>("frequency");
+          else
+            _frequency = impl::get_tsc_frequency();
+          _scale_factor = 1.0 / _frequency;
+
+          if (params->get<bool>("correct_overhead",true))
+            {
+              std::string calibration_method = params->get<std::string>("calibration_method","min");
+              if (calibration_method == "min")
+                _overhead = impl::calibrate_tsc_overhead_min(params->get<std::size_t>("calibration_iterations",TSC::calibrationIterations));
+              else if (calibration_method == "median")
+                _overhead = impl::calibrate_tsc_overhead_median(params->get<std::size_t>("calibration_iterations",TSC::calibrationIterations));
+              else
+                DUNE_THROW(TSCError,"Unknown TSC calibration method " << calibration_method);
+            }
+          else
+            _overhead = 0;
+        }
+      else
+        {
+          _frequency = impl::get_tsc_frequency();
+          _overhead = impl::calibrate_tsc_overhead_min(TSC::calibrationIterations);
+        }
+      _scale_factor = 1.0 / _frequency;
+    }
+
+    // initialize with something stupid
+    TSC::Counter TSC::_overhead = std::numeric_limits<TSC::Counter>::max();
+
+  } // namespace PDELab
+} // namespace Dune
diff --git a/dune/perftool/common/tsc.hh b/dune/perftool/common/tsc.hh
new file mode 100644
index 00000000..57183d90
--- /dev/null
+++ b/dune/perftool/common/tsc.hh
@@ -0,0 +1,108 @@
+#ifndef DUNE_PERFTOOL_COMMON_TSC_TIMER_HH
+#define DUNE_PERFTOOL_COMMON_TSC_TIMER_HH
+
+#include <dune/pdelab/common/exceptions.hh>
+#include <dune/common/parametertree.hh>
+
+namespace Dune {
+  namespace PDELab {
+
+    class TSCError
+      : public Exception
+    {};
+
+    class TSC
+    {
+
+    public:
+
+      using Counter = std::uint64_t;
+
+      static constexpr std::size_t calibrationIterations = 100000;
+
+      static Counter start()
+      {
+        unsigned a, d;
+        asm volatile(
+                     "lfence\n"
+                     "rdtsc"
+                     :"=a" (a), "=d" (d)
+                     ::
+                     );
+        return (static_cast<std::uint64_t>(d) << 32) | a;
+      }
+
+      static Counter stop()
+      {
+        unsigned a, d;
+        asm volatile(
+                     "lfence\n"
+                     "rdtsc"
+                     :"=a" (a), "=d" (d)
+                     ::
+                     );
+        return (static_cast<std::uint64_t>(d) << 32) | a;
+      }
+
+      static Counter zero()
+      {
+        return 0;
+      }
+
+      static void init()
+      {
+        instance(nullptr);
+      }
+
+      static void init(const Dune::ParameterTree& params)
+      {
+        instance(&params);
+      }
+
+      static Counter overhead()
+      {
+        return instance()._overhead;
+      }
+
+      static Counter frequency()
+      {
+        return instance()._frequency;
+      }
+
+      static Counter elapsed(Counter begin, Counter end)
+      {
+        return end - begin - _overhead;
+      }
+
+      static double seconds(Counter elapsed)
+      {
+        return elapsed * instance()._scale_factor;
+      }
+
+      TSC(const TSC&) = delete;
+      TSC(TSC&&) = delete;
+      TSC& operator=(const TSC&) = delete;
+      TSC& operator=(TSC&&) = delete;
+
+    private:
+
+      static const TSC& instance(const Dune::ParameterTree* params = nullptr)
+      {
+        static TSC tsc(params);
+        return tsc;
+      }
+
+      TSC(const Dune::ParameterTree* params);
+
+      Counter _frequency;
+      double _scale_factor;
+
+      // make this static to avoid the overhead of calling instance everytime we evaluate a timer
+      static Counter _overhead;
+
+    };
+
+  } // namespace PDELab
+} // namespace Dune
+
+#endif // DUNE_PDELAB_COMMON_TSC_HH
-- 
GitLab