我希望计算API返回值所花费的时间。 这种行动所花费的时间是纳秒秒。由于API是C ++类/函数,我使用timer.h来计算相同的内容:
#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;
}
以上代码以秒为单位给出时间。如何在纳秒内以更高的精度获得相同的效果?
答案 0 :(得分:81)
其他人发布的关于在循环中重复运行该函数的内容是正确的。
对于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
的更多信息显然在某些芯片组上有一个已知issue QPC,因此您可能需要确保没有这些芯片组。此外,一些双核AMD也可能导致problem。请参阅sebbbi的第二篇文章,他说:
QueryPerformanceCounter()和 QueryPerformanceFrequency()提供了一个 更好的分辨率,但有 不同的问题。例如在 Windows XP,所有AMD Athlon X2双 核心CPU返回任意一台的PC 核心“随机”(PC有时候 除非你,否则有点向后跳 特别安装AMD双核驱动程序 包来解决问题。我们没有 注意到任何其他双核心CPU 有类似的问题(p4双,p4 ht, core2 dual,core2 quad,phenom quad)。
编辑2013/07/16:
在http://msdn.microsoft.com/en-us/library/windows/desktop/ee417693(v=vs.85).aspx
中所述的某些情况下,QPC的效果似乎存在争议... QueryPerformanceCounter和QueryPerformanceFrequency通常会调整 多个处理器,BIOS或驱动程序中的错误可能导致这些例程返回 线程从一个处理器移动到另一个处理器时的不同值......
然而,这个StackOverflow回答https://stackoverflow.com/a/4588605/34329指出QPC在Win XP Service Pack 2之后应该可以在任何MS OS上正常工作。
本文显示Windows 7可以确定处理器是否具有不变的TSC,如果不存在,则可以退回到外部计时器。 http://performancebydesign.blogspot.com/2012/03/high-resolution-clocks-and-timers-for.html跨处理器同步仍是一个问题。
与计时器相关的其他精细阅读:
有关详细信息,请参阅评论。
答案 1 :(得分:68)
这个新答案使用C ++ 11的<chrono>
工具。虽然还有其他答案显示如何使用<chrono>
,但其中没有一个显示如何将<chrono>
与其他几个答案中提到的RDTSC
工具一起使用。所以我想我会展示如何将RDTSC
与<chrono>
一起使用。此外,我将演示如何在时钟上模板化测试代码,以便您可以在RDTSC
和系统的内置时钟设施之间快速切换(这可能基于clock()
,{{ 1}}和/或clock_gettime()
。
请注意,QueryPerformanceCounter
指令是特定于x86的。 RDTSC
仅限Windows。 QueryPerformanceCounter
仅限POSIX。下面我介绍两个新时钟:clock_gettime()
和std::chrono::high_resolution_clock
,如果你可以假设C ++ 11,现在是跨平台的。
首先,以下是如何使用英特尔std::chrono::system_clock
汇编指令创建与C ++ 11兼容的时钟。我称之为rdtsc
:
x::clock
所有这个时钟都计算CPU周期并将其存储在无符号的64位整数中。您可能需要调整编译器的汇编语言语法。或者您的编译器可能会提供您可以使用的内在函数(例如#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
)。
要构建时钟,您必须为其提供表示(存储类型)。您还必须提供时钟周期,该周期必须是编译时间常数,即使您的机器可能会在不同的功耗模式下更改时钟速度。从那些你可以根据这些基本原理轻松定义你的时钟的“原生”持续时间和时间点。
如果您只想输出时钟滴答数,那么您在时钟周期内给出的数字并不重要。如果您想将时钟周期数转换为某些实时单位(如纳秒),则此常量才会起作用。在这种情况下,您能够提供更准确的时钟速度,转换为纳秒(毫秒,无论如何)的准确度就越高。
以下是显示如何使用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";
}
单位。
作为另一个例子,我想打印出每次迭代的平均时钟周期数作为一个浮点,所以我创建了另一个基于double的持续时间,它具有与时钟的tick相同的单位(称为{{1}在代码中。)
循环定时调用任何一方的std::chrono::nanoseconds
。如果要命名从此函数返回的类型,则为:
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
。答案 2 :(得分:27)
具有这种准确度,最好在CPU时钟而不是系统调用like clock()中进行推理。并且不要忘记,如果执行指令需要超过一纳秒......具有纳秒精度几乎是不可能的。
仍然,something like that是一个开始:
这是检索自上次启动CPU以来传递的80x86 CPU时钟周期数的实际代码。它适用于奔腾及以上版本(不支持386/486)。这段代码实际上是MS Visual C ++特有的,但只要它支持内联汇编,就可以非常方便地移植到其他任何东西。
inline __int64 GetCpuClocks()
{
// Counter
struct { int32 low, high; } counter;
// Use RDTSC instruction to get clocks count
__asm push EAX
__asm push EDX
__asm __emit 0fh __asm __emit 031h // RDTSC
__asm mov counter.low, EAX
__asm mov counter.high, EDX
__asm pop EDX
__asm pop EAX
// Return result
return *(__int64 *)(&counter);
}
此功能还具有极快的优势 - 通常执行时不超过50个cpu周期。
Using the Timing Figures:
如果需要将时钟计数转换为真实的经过时间,请将结果除以芯片的时钟速度。请记住,“额定”GHz可能与芯片的实际速度略有不同。要检查芯片的真实速度,可以使用几个非常好的实用程序或Win32调用QueryPerformanceFrequency()。
答案 3 :(得分:23)
要正确执行此操作,您可以使用以下两种方法之一:RDTSC
或clock_gettime()
。
第二个是快2倍,并具有给予正确的绝对时间的优势。请注意,要使RDTSC
正常工作,您需要按指示使用它(此页面上的其他注释有错误,并且可能会在某些处理器上产生错误的计时值)
inline uint64_t rdtsc()
{
uint32_t lo, hi;
__asm__ __volatile__ (
"xorl %%eax, %%eax\n"
"cpuid\n"
"rdtsc\n"
: "=a" (lo), "=d" (hi)
:
: "%ebx", "%ecx" );
return (uint64_t)hi << 32 | lo;
}
和clock_gettime :(我任意选择微秒分辨率)
#include <time.h>
#include <sys/timeb.h>
// needs -lrt (real-time lib)
// 1970-01-01 epoch UTC time, 1 mcs resolution (divide by 1M to get time_t)
uint64_t ClockGetTime()
{
timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
return (uint64_t)ts.tv_sec * 1000000LL + (uint64_t)ts.tv_nsec / 1000LL;
}
产生的时间和价值:
Absolute values:
rdtsc = 4571567254267600
clock_gettime = 1278605535506855
Processing time: (10000000 runs)
rdtsc = 2292547353
clock_gettime = 1031119636
答案 4 :(得分:21)
我使用以下内容来获得所需的结果:
#include <time.h>
#include <iostream>
using namespace std;
int main (int argc, char** argv)
{
// reset the clock
timespec tS;
tS.tv_sec = 0;
tS.tv_nsec = 0;
clock_settime(CLOCK_PROCESS_CPUTIME_ID, &tS);
...
... <code to check for the time to be put here>
...
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tS);
cout << "Time taken is: " << tS.tv_sec << " " << tS.tv_nsec << endl;
return 0;
}
答案 5 :(得分:8)
对于C++11,这是一个简单的包装器:
#include <iostream>
#include <chrono>
class Timer
{
public:
Timer() : beg_(clock_::now()) {}
void reset() { beg_ = clock_::now(); }
double elapsed() const {
return std::chrono::duration_cast<second_>
(clock_::now() - beg_).count(); }
private:
typedef std::chrono::high_resolution_clock clock_;
typedef std::chrono::duration<double, std::ratio<1> > second_;
std::chrono::time_point<clock_> beg_;
};
或者对于* nix上的C ++ 03,
class Timer
{
public:
Timer() { clock_gettime(CLOCK_REALTIME, &beg_); }
double elapsed() {
clock_gettime(CLOCK_REALTIME, &end_);
return end_.tv_sec - beg_.tv_sec +
(end_.tv_nsec - beg_.tv_nsec) / 1000000000.;
}
void reset() { clock_gettime(CLOCK_REALTIME, &beg_); }
private:
timespec beg_, end_;
};
使用示例:
int main()
{
Timer tmr;
double t = tmr.elapsed();
std::cout << t << std::endl;
tmr.reset();
t = tmr.elapsed();
std::cout << t << std::endl;
return 0;
}
答案 6 :(得分:5)
您可以在x86处理器下运行gcc时使用以下功能:
unsigned long long rdtsc()
{
#define rdtsc(low, high) \
__asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))
unsigned int low, high;
rdtsc(low, high);
return ((ulonglong)high << 32) | low;
}
使用Digital Mars C ++:
unsigned long long rdtsc()
{
_asm
{
rdtsc
}
}
读取芯片上的高性能定时器。我在进行性能分析时会使用它。
答案 7 :(得分:5)
通常,为了计算调用函数所需的时间,您希望执行的次数比不止一次多。如果你只调用一次函数并且运行时间很短,你仍然需要实际调用计时器函数的开销,而你不知道需要多长时间。
例如,如果您估计您的函数可能需要800 ns才能运行,请循环调用它一千万次(这将需要大约8秒)。将总时间除以一千万来获得每次通话的时间。
答案 8 :(得分:3)
我在这里使用Borland代码是代码ti_hund给了我一些否定数,但时间相当不错。
#include <dos.h>
void main()
{
struct time t;
int Hour,Min,Sec,Hun;
gettime(&t);
Hour=t.ti_hour;
Min=t.ti_min;
Sec=t.ti_sec;
Hun=t.ti_hund;
printf("Start time is: %2d:%02d:%02d.%02d\n",
t.ti_hour, t.ti_min, t.ti_sec, t.ti_hund);
....
your code to time
...
// read the time here remove Hours and min if the time is in sec
gettime(&t);
printf("\nTid Hour:%d Min:%d Sec:%d Hundreds:%d\n",t.ti_hour-Hour,
t.ti_min-Min,t.ti_sec-Sec,t.ti_hund-Hun);
printf("\n\nAlt Ferdig Press a Key\n\n");
getch();
} // end main
答案 9 :(得分:3)
您可以使用 Embedded Profiler (适用于Windows和Linux),它具有多平台定时器的接口(处理器周期数),并且每秒可以为您提供多个周期:
EProfilerTimer timer;
timer.Start();
... // Your code here
const uint64_t number_of_elapsed_cycles = timer.Stop();
const uint64_t nano_seconds_elapsed =
mumber_of_elapsed_cycles / (double) timer.GetCyclesPerSecond() * 1000000000;
对于可以动态更改CPU频率的现代处理器,重新计算循环计数到时间可能是一项危险的操作。因此,为了确保转换时间正确,有必要在分析之前修复处理器频率。
答案 10 :(得分:3)
使用Brock Adams的方法,使用简单的类:
int get_cpu_ticks()
{
LARGE_INTEGER ticks;
QueryPerformanceFrequency(&ticks);
return ticks.LowPart;
}
__int64 get_cpu_clocks()
{
struct { int32 low, high; } counter;
__asm cpuid
__asm push EDX
__asm rdtsc
__asm mov counter.low, EAX
__asm mov counter.high, EDX
__asm pop EDX
__asm pop EAX
return *(__int64 *)(&counter);
}
class cbench
{
public:
cbench(const char *desc_in)
: desc(strdup(desc_in)), start(get_cpu_clocks()) { }
~cbench()
{
printf("%s took: %.4f ms\n", desc, (float)(get_cpu_clocks()-start)/get_cpu_ticks());
if(desc) free(desc);
}
private:
char *desc;
__int64 start;
};
用法示例:
int main()
{
{
cbench c("test");
... code ...
}
return 0;
}
结果:
测试时间:0.0002 ms
有一些函数调用开销,但应该还是足够快:)
答案 11 :(得分:3)
如果您需要亚秒精度,则需要使用特定于系统的扩展,并且必须检查操作系统的文档。 POSIX支持gettimeofday最多微秒,但没有更精确,因为计算机的频率不超过1GHz。
如果您使用的是Boost,则可以查看boost::posix_time。
答案 12 :(得分:2)
您如何看待这个:
int iceu_system_GetTimeNow(long long int *res)
{
static struct timespec buffer;
//
#ifdef __CYGWIN__
if (clock_gettime(CLOCK_REALTIME, &buffer))
return 1;
#else
if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &buffer))
return 1;
#endif
*res=(long long int)buffer.tv_sec * 1000000000LL + (long long int)buffer.tv_nsec;
return 0;
}
答案 13 :(得分:2)
如果这是针对Linux的,我一直在使用函数“gettimeofday”,它返回一个结构,给出自Epoch以来的秒和微秒。然后,您可以使用timersub减去两者以获得时间差异,并将其转换为您想要的任何精度。但是,您指定纳秒,看起来函数clock_gettime()就是您要查找的内容。它将时间以秒和纳秒的形式投入到传递给它的结构中。
答案 14 :(得分:2)
这是一个很好的Boost计时器,效果很好:
//Stopwatch.hpp
#ifndef STOPWATCH_HPP
#define STOPWATCH_HPP
//Boost
#include <boost/chrono.hpp>
//Std
#include <cstdint>
class Stopwatch
{
public:
Stopwatch();
virtual ~Stopwatch();
void Restart();
std::uint64_t Get_elapsed_ns();
std::uint64_t Get_elapsed_us();
std::uint64_t Get_elapsed_ms();
std::uint64_t Get_elapsed_s();
private:
boost::chrono::high_resolution_clock::time_point _start_time;
};
#endif // STOPWATCH_HPP
//Stopwatch.cpp
#include "Stopwatch.hpp"
Stopwatch::Stopwatch():
_start_time(boost::chrono::high_resolution_clock::now()) {}
Stopwatch::~Stopwatch() {}
void Stopwatch::Restart()
{
_start_time = boost::chrono::high_resolution_clock::now();
}
std::uint64_t Stopwatch::Get_elapsed_ns()
{
boost::chrono::nanoseconds nano_s = boost::chrono::duration_cast<boost::chrono::nanoseconds>(boost::chrono::high_resolution_clock::now() - _start_time);
return static_cast<std::uint64_t>(nano_s.count());
}
std::uint64_t Stopwatch::Get_elapsed_us()
{
boost::chrono::microseconds micro_s = boost::chrono::duration_cast<boost::chrono::microseconds>(boost::chrono::high_resolution_clock::now() - _start_time);
return static_cast<std::uint64_t>(micro_s.count());
}
std::uint64_t Stopwatch::Get_elapsed_ms()
{
boost::chrono::milliseconds milli_s = boost::chrono::duration_cast<boost::chrono::milliseconds>(boost::chrono::high_resolution_clock::now() - _start_time);
return static_cast<std::uint64_t>(milli_s.count());
}
std::uint64_t Stopwatch::Get_elapsed_s()
{
boost::chrono::seconds sec = boost::chrono::duration_cast<boost::chrono::seconds>(boost::chrono::high_resolution_clock::now() - _start_time);
return static_cast<std::uint64_t>(sec.count());
}
答案 15 :(得分:2)
如果想要使用可用于快速测试的简约结构,那么我建议您在#include
之后立即复制并粘贴在C ++文件中的任何位置。这是我牺牲Allman风格格式的唯一例子。
您可以轻松调整结构第一行的精度。可能的值包括:nanoseconds
,microseconds
,milliseconds
,seconds
,minutes
或hours
。
#include <chrono>
struct MeasureTime
{
using precision = std::chrono::microseconds;
std::vector<std::chrono::steady_clock::time_point> times;
std::chrono::steady_clock::time_point oneLast;
void p() {
std::cout << "Mark "
<< times.size()/2
<< ": "
<< std::chrono::duration_cast<precision>(times.back() - oneLast).count()
<< std::endl;
}
void m() {
oneLast = times.back();
times.push_back(std::chrono::steady_clock::now());
}
void t() {
m();
p();
m();
}
MeasureTime() {
times.push_back(std::chrono::steady_clock::now());
}
};
MeasureTime m; // first time is already in memory
doFnc1();
m.t(); // Mark 1: next time, and print difference with previous mark
doFnc2();
m.t(); // Mark 2: next time, and print difference with previous mark
doStuff = doMoreStuff();
andDoItAgain = doStuff.aoeuaoeu();
m.t(); // prints 'Mark 3: 123123' etc...
Mark 1: 123
Mark 2: 32
Mark 3: 433234
如果您之后想要报告,例如,您之间的代码也会写入标准输出。然后将以下函数添加到struct(就在MeasureTime()之前):
void s() { // summary
int i = 0;
std::chrono::steady_clock::time_point tprev;
for(auto tcur : times)
{
if(i > 0)
{
std::cout << "Mark " << i << ": "
<< std::chrono::duration_cast<precision>(tprev - tcur).count()
<< std::endl;
}
tprev = tcur;
++i;
}
}
那么你可以使用:
MeasureTime m;
doFnc1();
m.m();
doFnc2();
m.m();
doStuff = doMoreStuff();
andDoItAgain = doStuff.aoeuaoeu();
m.m();
m.s();
其中将列出所有标记,但之后会执行其他代码。请注意,您不应同时使用m.s()
和m.t()
。
答案 16 :(得分:0)
plf::nanotimer是为此提供的轻量级选项,可在Windows,Linux,Mac和BSD等环境下使用。根据操作系统的不同,精度约为〜微秒:
#include "plf_nanotimer.h"
#include <iostream>
int main(int argc, char** argv)
{
plf::nanotimer timer;
timer.start()
// Do something here
double results = timer.get_elapsed_ns();
std::cout << "Timing: " << results << " nanoseconds." << std::endl;
return 0;
}