text: // Copyright (C) 2011  Davis E. King (davis@dlib.net)
  // License: Boost Software License   See LICENSE.txt for the full license.
  #ifndef DLIB_TImING_Hh_
  #define DLIB_TImING_Hh_
  
  #include <chrono>
  #include <atomic>
  #include <cstring>
  #include "string.h"
  
  #include <iostream>
  
  // ----------------------------------------------------------------------------------------
  
  /*!A timing
  
      This set of functions is useful for determining how much time is spent
      executing blocks of code.  Consider the following example:
  
      int main()
      {
          using namespace dlib::timing;
          for (int i = 0; i < 10; ++i)
          {
              // timing block #1
              start(1,"block #1");
              dlib::sleep(500);
              stop(1);
  
              // timing block #2
              start(2,"block #2");
              dlib::sleep(1000);
              stop(2);
          }
  
          print();
      }
  
      This program would output:
          Timing report: 
              block #1: 5.0 seconds
              block #2: 10.0 seconds
  
      So we spent 5 seconds in block #1 and 10 seconds in block #2
  
  
  
      Additionally, note that you can use an RAII style timing block object.  For
      example, if we wanted to find out how much time we spent in a loop a convenient
      way to do this would be as follows:
  
      int main()
      {
          using namespace dlib::timing;
          for (int i = 0; i < 10; ++i)
          {
              block tb(1, "main loop");
  
              dlib::sleep(1500);
          } 
  
          print();
      }
  
      This program would output:
          Timing report: 
              block main loop: 15.0 seconds
  
  !*/
  
  // ----------------------------------------------------------------------------------------
  
  namespace dlib {
  namespace timing {
  const int TIME_SLOTS = 500;
  const int NAME_LENGTH = 40;
  
  inline std::atomic <uint64_t>* time_buf() {
    static std::atomic <uint64_t> buf[TIME_SLOTS];
    return buf;
  }
  
  inline char* name_buf(int i, const char* name) {
    static char buf[TIME_SLOTS][NAME_LENGTH] = {{0}};
    // if this name buffer is empty then copy name into it
    if (buf[i][0] == '\0') {
      using namespace std;
      strncpy(buf[i], name, NAME_LENGTH - 1);
      buf[i][NAME_LENGTH - 1] = '\0';
    }
    // return the name buffer
    return buf[i];
  }
  
  inline uint64_t ts() {
    using namespace std::chrono;
    return duration_cast < duration < double, std::nano >> (high_resolution_clock::now().time_since_epoch()).count();
  }
  
  inline void start(int i) {
    time_buf()[i] -= ts();
  }
  
  inline void start(int i, const char* name) {
    time_buf()[i] -= ts();
    name_buf(i, name);
  }
  
  inline void stop(int i) {
    time_buf()[i] += ts();
  }
  
  inline void print() {
    using namespace std;
    cout << "Timing report: " << endl;
  
    // figure out how long the longest name is going to be.
    unsigned long max_name_length = 0;
    for (int i = 0; i < TIME_SLOTS; ++i) {
      string name;
      // Check if the name buffer is empty.  Use the name it contains if it isn't.
      if (name_buf(i, "")[0] != '\0')
        name = cast_to_string(i) + ": " + name_buf(i, "");
      else
        name = cast_to_string(i);
      max_name_length = std::max<unsigned long>(max_name_length, name.size());
    }
  
    for (int i = 0; i < TIME_SLOTS; ++i) {
      if (time_buf()[i] != 0) {
        double time = time_buf()[i] / 1000.0 / 1000.0;
        string name;
        // Check if the name buffer is empty.  Use the name it contains if it isn't.
        if (name_buf(i, "")[0] != '\0')
          name = cast_to_string(i) + ": " + name_buf(i, "");
        else
          name = cast_to_string(i);
  
        // make sure the name is always the same length.  Do so by padding with spaces
        if (name.size() < max_name_length)
          name += string(max_name_length - name.size(), ' ');
  
        if (time < 1000)
          cout << "  " << name << ": " << time << " milliseconds" << endl;
        else if (time < 1000 * 60)
          cout << "  " << name << ": " << time / 1000.0 << " seconds" << endl;
        else if (time < 1000 * 60 * 60)
          cout << "  " << name << ": " << time / 1000.0 / 60.0 << " minutes" << endl;
        else
          cout << "  " << name << ": " << time / 1000.0 / 60.0 / 60.0 << " hours" << endl;
      }
    }
  }
  
  inline void clear() {
    for (int i = 0; i < TIME_SLOTS; ++i) {
      // clear timing buffer
      time_buf()[i] = 0;
      // clear name buffer
      name_buf(i, "")[0] = '\0';
    }
  }
  
  struct block {
    /*!
        WHAT THIS OBJECT REPRESENTS
            This is an RAII tool for calling start() and stop()
    !*/
  
    block(int i) : idx(i) { start(idx); }
  
    block(int i, const char* str) : idx(i) { start(idx, str); }
  
    ~block() { stop(idx); }
  
    const int idx;
  };
  }
  }
  
  
  #endif // DLIB_TImING_Hh_
  
  