Функция таймера для обеспечения времени в нано секундах с помощью C++


Я хочу рассчитать время, которое потребовалось для API, чтобы вернуть значение. Время, необходимое для такого действия, находится в пространстве нано секунд. Поскольку API-это класс/функция C++, я использую таймер.h для того чтобы caculate то же самое:

  #include <ctime>
  #include <cstdio>

  using namespace std;

  int main(int argc, char** argv) {

      clock_t start;
      double diff;
      start = clock();
      diff = ( std::clock() - start ) / (double)CLOCKS_PER_SEC;
      cout<<"printf: "<< diff <<'n';

      return 0;
  }

приведенный выше код дает время в секундах. Как я могу получить то же самое в нано секундах и с большей точностью?

2 96

2 ответа:

то, что другие опубликовали о повторном запуске функции в цикле, является правильным.

для Linux (и BSD) вы хотите использовать при успешном выполнении функции clock_gettime().

#include <sys/time.h>

int main()
{
   timespec ts;
   // clock_gettime(CLOCK_MONOTONIC, &ts); // Works on FreeBSD
   clock_gettime(CLOCK_REALTIME, &ts); // Works on Linux
}

для windows вы хотите использовать QueryPerformanceCounter. И вот еще на QPC

по-видимому, существует известная вопрос С QPC на некоторых чипсетах, поэтому вы можете убедиться, что у вас нет этих чипсетов. Кроме того, некоторые двойные ядро AMDs также может вызвать

этот новый ответ использует C++11-х <chrono> объект. Хотя есть и другие ответы, которые показывают, как использовать <chrono>, ни один из них не показывает, как использовать <chrono> С RDTSC объект, упомянутый в нескольких других ответах здесь. Поэтому я подумал, что покажу, как использовать RDTSC С <chrono>. Кроме того, я продемонстрирую, как вы можете шаблонизировать тестовый код на часах, чтобы вы могли быстро переключаться между RDTSC и встроенные часы вашей системы (которые, вероятно ,будут на основе clock(),clock_gettime() и/или QueryPerformanceCounter.

отметим, что RDTSC инструкция платформы x86. QueryPerformanceCounter только для Windows. И clock_gettime() только POSIX. Ниже я представляю два новых часов:std::chrono::high_resolution_clock и std::chrono::system_clock, которые, если вы можете предположить C++11, теперь являются кросс-платформенными.

во-первых, вот как вы создаете C++11-совместимые часы из Intel rdtsc инструкция по монтажу. Я назову это x::clock:

#include <chrono>

namespace x
{

struct clock
{
    typedef unsigned long long                 rep;
    typedef std::ratio<1, 2'800'000'000>       period; // My machine is 2.8 GHz
    typedef std::chrono::duration<rep, period> duration;
    typedef std::chrono::time_point<clock>     time_point;
    static const bool is_steady =              true;

    static time_point now() noexcept
    {
        unsigned lo, hi;
        asm volatile("rdtsc" : "=a" (lo), "=d" (hi));
        return time_point(duration(static_cast<rep>(hi) << 32 | lo));
    }
};

}  // x

все, что делают эти часы-это подсчет Циклы процессора и хранить его в 64-разрядное целое число без знака. Возможно, Вам потребуется настроить синтаксис языка ассемблера для вашего компилятора. Или ваш компилятор может предложить встроенный вы можете использовать вместо этого (например now() {return __rdtsc();}).

построить часы, вы должны дать ему представление (тип хранения). Вы также должны указать тактовый период, который должен быть постоянной времени компиляции, даже если ваша машина может изменять тактовую частоту в разных режимах питания. И из них вы можете легко определить свой "родная" продолжительность времени и точка времени часов с точки зрения этих основ.

если все, что вы хотите сделать, это вывести количество тиков часов, на самом деле не имеет значения, какое число вы даете за период часов. Эта константа вступает в игру только в том случае, если вы хотите преобразовать количество тиков часов в некоторую единицу реального времени, такую как наносекунды. И в этом случае, чем точнее вы сможете поставить тактовую частоту, тем точнее будет преобразование в наносекунды, (миллисекунды, неважно).

ниже пример кода, который показывает, как использовать x::clock. На самом деле я шаблонный код на часах, как я хочу показать, как можно использовать различные часы с точно такой же синтаксис. Этот конкретный тест показывает, что накладные расходы цикла при запуске то, что вы хотите время в цикле:

#include <iostream>

template <class clock>
void
test_empty_loop()
{
    // Define real time units
    typedef std::chrono::duration<unsigned long long, std::pico> picoseconds;
    // or:
    // typedef std::chrono::nanoseconds nanoseconds;
    // Define double-based unit of clock tick
    typedef std::chrono::duration<double, typename clock::period> Cycle;
    using std::chrono::duration_cast;
    const int N = 100000000;
    // Do it
    auto t0 = clock::now();
    for (int j = 0; j < N; ++j)
        asm volatile("");
    auto t1 = clock::now();
    // Get the clock ticks per iteration
    auto ticks_per_iter = Cycle(t1-t0)/N;
    std::cout << ticks_per_iter.count() << " clock ticks per iteration\n";
    // Convert to real time units
    std::cout << duration_cast<picoseconds>(ticks_per_iter).count()
              << "ps per iteration\n";
}

первое, что делает этот код, это создает блок "реального времени" для отображения результатов. Я выбрал пикосекунды, но вы можете выберите любые единицы измерения, которые вам нравятся, либо интегральные, либо с плавающей запятой. В качестве примера можно привести уже готовый std::chrono::nanoseconds блок, который я мог бы использовать.

в качестве другого примера я хочу распечатать среднее количество тактов за итерацию в виде плавающей точки, поэтому я создаю другую длительность, основанную на double, которая имеет те же единицы, что и ТИК часов (называется Cycle в коде).

цикл синхронизирован с вызовами clock::now() С обеих сторон. Если вы хотите назвать тип возвращаемый из этой функции это:

typename clock::time_point t0 = clock::now();

(как ясно показано в x::clock пример, а также верно для системных часов).

чтобы получить длительность в терминах часов с плавающей запятой, нужно просто вычесть две точки времени и получить значение за итерацию, разделите эту длительность на количество итераций.

вы можете получить счет в любой период с помощью count() функции-члена. Это возвращает внутренний представление. Наконец-то я использую std::chrono::duration_cast для преобразования длительности Cycle на срок picoseconds и распечатать.

использовать этот код просто:

int main()
{
    std::cout << "\nUsing rdtsc:\n";
    test_empty_loop<x::clock>();

    std::cout << "\nUsing std::chrono::high_resolution_clock:\n";
    test_empty_loop<std::chrono::high_resolution_clock>();

    std::cout << "\nUsing std::chrono::system_clock:\n";
    test_empty_loop<std::chrono::system_clock>();
}

выше я выполняю тест, используя наш самодельный x::clock, и сравните эти результаты с использованием двух системных часов:std::chrono::high_resolution_clock и std::chrono::system_clock. Для меня это распечатывает:

Using rdtsc:
1.72632 clock ticks per iteration
616ps per iteration

Using std::chrono::high_resolution_clock:
0.620105 clock ticks per iteration
620ps per iteration

Using std::chrono::system_clock:
0.00062457 clock ticks per iteration
624ps per iteration

это показывает, что каждый из этих часов имеет разный период тика, так как тики за итерацию значительно отличается для каждого часы. Однако при преобразовании в известную единицу времени (например, пикосекунды) я получаю примерно тот же результат для каждого такта (ваш пробег может отличаться).

обратите внимание, что мой код полностью свободен от "магических констант преобразования". Действительно, во всем примере есть только два магических числа:

  1. тактовая частота моей машины для того, чтобы определить x::clock.
  2. количество итераций для тестирования. Если изменить это число делает ваши результаты сильно различаются, то вы, вероятно, должны сделать количество итераций выше, или очистить компьютер от конкурирующих процессов во время тестирования.