2009-11-06 66 views

回答

2

在您输入功能之前,关闭系统时间。 从功能返回后,请关闭系统时间。 减去差异并比较两个实现。

+0

谢谢杰夫,这似乎是一个很好的方法。 – Fred 2009-11-06 15:02:25

+2

当然,循环它足够多次,你不会得到零的差异。 – Cascabel 2009-11-06 15:02:51

0
  • 商店时间戳才进入功能

  • 商店时间戳后退出功能

  • 比较时间戳

确保使用SI因为时间分辨率可能会改变你的结果。对于短期功能尤其如此。使用高分辨率定时器(大多数平台都可以使用微秒分辨率)。

3

运行(他们)数百万次(每个)并测量所花费的时间。
完成速度更快的是更好的性能。

gprof可以帮助:)

这里的gprof的结果,当我跑我的10秒的程序(函数名称变更)

 
Each sample counts as 0.01 seconds. 
    % cumulative self    self  total 
time seconds seconds calls ms/call ms/call name 
60.29  8.68  8.68 115471546  0.00  0.00 workalot 
39.22  14.32  5.64  46 122.70 311.32 work_b 
    0.49  14.39  0.07        inlined 
    0.07  14.40  0.01  46  0.22  0.22 work_c 
    0.00  14.40  0.00  460  0.00  0.00 find_minimum 
    0.00  14.40  0.00  460  0.00  0.00 feedback 
    0.00  14.40  0.00  46  0.00  0.00 work_a 
+1

我同意这*一般*。但是,由于缓存问题,第一次迭代可能比其他迭代慢得多。如果例行程序通常只进行一次,而不是紧密循环,这会给你一张歪斜的图片。 OTOH,如果例行程序只进行一次,则不应该浪费宝贵的时间尝试分析或优化它。 – 2009-11-06 15:21:16

+0

感谢pmg,我会检查gprof。我注意到我甚至默认安装了它。 – Fred 2009-11-06 15:25:46

+0

T.E.D.有几个优点。操作系统的CPU缓存和缓存将大大提高除第一次迭代之外的所有功能的性能,如果函数单独运行或在其他功能足够丰富以替代CPU缓存的内容。但这可能是目前最好的简单分析技术,并且仍会给你一个良好/可接受/糟糕的性能数据。 – Dogmang 2009-11-06 15:48:02

9

需要高分辨率定时器。

在Linux上,gettimeofday()是一个不错的选择,它给你微秒的分辨率。在Windows上,QueryPerformanceCounter()是典型的。确保你多次运行你的功能,以获得稳定的读数。

快速样品,对于Linux:

struct timeval t0, t1; 
unsigned int i; 

gettimeofday(&t0, NULL); 
for(i = 0; i < 100000; i++) 
    function_to_measure(); 
gettimeofday(&t1, NULL); 
printf("Did %u calls in %.2g seconds\n", i, t1.tv_sec - t0.tv_sec + 1E-6 * (t1.tv_usec - t0.tv_usec); 

你当然会调整数(100,000)来匹配功能的性能。如果函数真的需要一段时间才能运行,那么最好是循环和/或函数调用开销可能占主导地位。

+0

感谢您的提示和示例。我在这里运行mac os,所以gettimeofday()在这里也可用。 – Fred 2009-11-06 15:21:47

+0

如果函数仅依赖于内存和cpu,并且不会改变状态(即每次运行相同),则此方法正常工作。如果你的函数有文件访问权限,你可能会被文件系统缓存愚弄。 – Adriaan 2009-11-06 15:46:13

1

结帐HighResTimer高性能计时器。

您可能会发现存储前/后的时间不够准确,并且可能会导致0,除非您有更长的运行功能。

0

退房RDTSC但最好是像下面这样做。

0 - 呼叫系统的睡眠或产量的功能,这样,当它返回时,你有一个新的时间片

1 - RDTSC

2 - 呼叫你的函数

3 - RDTSC

如果你的功能是长期运行的,你必须使用像gprof这样的分析工具(这非常容易使用)&英特尔的VTune应用程序(我很久没用过了)。在看到Art的回答后,我将自己的思想从gprof改变为Callgrind。过去我只使用Valgrind的Memcheck工具,这是一个宏伟的工具。我之前没有使用过Callgrind,但我确信它比gprof更好...

+0

有趣的是,我不知道这里有详细的说明。也许不得不尝试这个以及看它是如何工作的。 – Fred 2009-11-06 15:57:44

4

开放源代码Callgrind profiler(用于Linux)是衡量性能的非常棒的方法。再加上KCacheGrind,你可以很好地看到你的时间花在哪里。

Callgrind是Valgrind的一部分。

  • 艺术
0

正如你可以使用标准的函数时(),它返回自纪元秒当前数量的简单和便携的方式。


#include <time.h> 

time_t starttime, endtime; 

starttime = time(NULL); 
for (i = 0; i < 1000000; i++) 
{ 
    testfunc(); 
} 
endtime = time(NULL); 

printf("Time in seconds is %d\n", (int)(endtime-starttime)); 

根据您的需要调整迭代次数。如果一个函数调用需要5秒钟,那么你需要一杯laaarge咖啡来进行1000000次迭代......当差异小于1秒时,即使是大量的,你也应该问问你自己是否重要,如果是的话,2 )检查你最喜欢的编译器是否已经具有内置分析功能。

3

Fred,我注意到你在评论中说你在OS X上。在OS X上获得非常精确的小规模函数时间的最好方法是使用mach_absoute_time()函数。

#include <mach/mach_time.h> 
#include <stdint.h> 

int loopCount; 

uint64_t startTime = mach_absolute_time(); 
for (loopCount = 0; loopCount < iterations; ++loopCount) { 
    functionBeingTimed(); 
} 
uint64_t endTime = mach_absolute_time(); 
double averageTime = (double)(endTime-startTime)/iterations; 

这让您在iterations调用该函数的平均时间:您可以按如下方式使用它。这可能会受到系统外部进程以外的影响的影响。因此,你可能反而想利用最快的时间:

#include <mach/mach_time.h> 
#include <stdint.h> 

int loopCount; 

double bestTime = __builtin_inf(); 
for (loopCount = 0; loopCount < iterations; ++loopCount) { 
    uint64_t startTime = mach_absolute_time(); 
    functionBeingTimed(); 
    uint64_t endTime = mach_absolute_time(); 
    double bestTime = __builtin_fmin(bestTime, (double)(endTime-startTime)); 
} 

这可能有其自身的问题,特别是如果被计时的功能是非常非常快的。你需要考虑你真正想要测量的是什么,并选择一种科学合理的方法(好的实验设计是)。我经常使用这两种方法之间的混合来作为衡量一项新任务的第一次尝试(对于许多呼叫来说最小的平均值)。

还要注意,在上面的代码样本中,时间以“时间单位”表示。如果你只是想比较算法,这通常很好。出于其他一些目的,您可能希望将它们转换为纳秒或周期。要做到这一点,你可以使用以下功能:

#include <mach/mach_time.h> 
#include <sys/sysctl.h> 
#include <stdint.h> 

double ticksToNanoseconds(double ticks) { 
    static double nanosecondsPerTick = 0.0; 
    // The first time the function is called 
    // ask the system how to convert mach 
    // time units to nanoseconds 
    if (0.0 == nanosecondsPerTick) { 
     mach_timebase_info_data_t timebase; 
     // to be completely pedantic, check the return code of this call: 
     mach_timebase_info(&timebase); 
     nanosecondsPerTick = (double)timebase.numer/timebase.denom; 
    } 
    return ticks * nanosecondsPerTick; 
} 

double nanosecondsToCycles(double nanoseconds) { 
    static double cyclesPerNanosecond = 0.0; 
    // The first time the function is called 
    // ask the system what the CPU frequency is 
    if (0.0 == cyclesPerNanosecond) { 
     uint64_t freq; 
     size_t freqSize = sizeof(freq); 
     // Again, check the return code for correctness =) 
     sysctlbyname("hw.cpufrequency", &freq, &freqSize, NULL, 0L); 
     cyclesPerNanosecond = (double)freq * 1e-9; 
    } 
    return nanoseconds * cyclesPerNanosecond; 
} 

注意,转换为纳秒永远是声音,但转换到周期可能出差错以不同的方式,因为现代的CPU不会在一个运行固定速度。尽管如此,它一般工作得很好。

+0

谢谢Stephen,非常棒!我会试试这个。 – Fred 2009-11-06 17:34:34

+0

如果您遇到任何问题,请告诉我;我从内存中输入了所有这些,所以我可能在某处出错了) – 2009-11-06 17:40:03

1

所有这些其他答案都使用gettimeofday()的某些变体进行计时。这很简单,因为通常需要多次运行内核才能获得可重复的结果。将它放在一个紧密的循环中会改变代码和数据高速缓存的状态,所以这些结果可能不会指示真实的性能。

更好的选择是实际使用CPU周期计数器。在x86上,你可以用rdtsc指令来做到这一点。这是x264

static inline uint32_t read_time(void) 
{ 
    uint32_t a = 0; 
#if defined(__GNUC__) && (defined(ARCH_X86) || defined(ARCH_X86_64)) 
    asm volatile("rdtsc" :"=a"(a) ::"edx"); 
#elif defined(ARCH_PPC) 
    asm volatile("mftb %0" : "=r" (a)); 
#elif defined(ARCH_ARM)  // ARMv7 only 
    asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r"(a)); 
#endif 
    return a; 
} 

更多关于使用各种硬件计数器分析,看PAPI。出于某些目的,模拟器(如Callgrind和基于中断廓线仪(Oprofile)是有用的

2

你好,我会给你一个例子,解释它:使用程序处理器时间:

#include <stdio.h> 
#include <time.h> 

int main(void) 
{ 

    clock_t start_clk = clock(); 

    /* 
     put any code here 
    */ 

    printf("Processor time used by program: %lg sec.\n", \ 
    (clock() - start_clk)/(long double) CLOCKS_PER_SEC); 

    return 0; 
} 

输出4.94066 E-324秒

time.h中:。

声明clock_t表示这是一个算术(你可以做在这个值的数学像我在示例做的)时间值 基本上把任何代码评论的地方。

CLOCKS_PER_SEC是time.h中声明的宏,用它作为分母将值转换为秒。

它转换为long double的原因有两个很重要的:

  1. 我们不知道是什么类型clock_t表示实际上是,但我们想打印(你穿什么转换printf中?) 。
  2. long double是一个非常精确的类型,它可以表示非常小的值。