会员教程翻译:性能和时间



来自会员德鲁伊: 在讨论性能之前,先讨论一个重要的话题:时间。为了理解代码中的变化如何影响性能,我们需要一个排序的指标。有许多方法用于时间例程,一些比另一些合适。在本教程


来自会员德鲁伊

在讨论性能之前,先讨论一个重要的话题:时间。为了理解代码中的变化如何影响性能,我们需要一个排序的指标。有许多方法用于时间例程,一些比另一些合适。在本教程中我们将讨论Mach Absolute Time。

 
为什么是Mach?
时间例程依赖于所需要测量的时间域。某些情况下使用诸如clock()或getrusage()函数来做些简单的数学运算就足够了。如果时间例程将用于实际的开发框架之外,可移植性就很重要了。我不使用这些。为什么?
对于我来说,调试代码的典型问题是:
1) 我只需要在即时测试时使用时间例程
2) 我不喜欢依赖于多种函数来包含不同的时间域。它们的行为可能不一致
3) 有时我需要一个高精度定时器
 
欢迎了解mach_absolute_time
mach_absolute_time是一个CPU/总线依赖函数,返回一个基于系统启动后的时钟”嘀嗒”数。它没有很好的文档定义,但这不应该成为使用它的障碍,因为在MAC OS X上可以确保它的行为,并且,它包含系统时钟包含的所有时间区域。那是否应该在产品代码中使用它呢?可能不应该。但是对于测试,它却恰到好处。
使用mach_absolute_time时需要考虑两个因素:
1) 如何获取当前的Mach绝对时间
2) 如何将其转换为有意义的数字
 
获取mach_absolute_time
这非常简单
     
     
  1. #include <stdint.h>  
  2. uint64_t start =mach_absolute_time(); 
  3. uint64_t stop =mach_absolute_time();  
 
这样就可以了。我们通常获取两个值,以得到这两个时间的时间差。
 
将mach_absolute_time时间差转换为秒数
 
这稍微有点复杂,因为我们需要获取mach_absolute_time所基于的系统时间基准。如下代码:
     
     
  1. #include <stdint.h> 
  2. #include<mach/mach_time.h>  
  3. //Raw mach_absolute_times going in,difference in seconds out 
  4. double subtractTimes( uint64_tendTime, uint64_t startTime ) 
  5. uint64_t difference = endTime - startTime; 
  6. static double conversion = 0.0; 
  7. if( conversion == 0.0 ) 
  8. mach_timebase_info_data_t info; 
  9. kern_return_t err =mach_timebase_info( &info ); 
  10. //Convert the timebase into seconds 
  11. if( err == 0  ) 
  12. conversion= 1e-9 * (double) info.numer / (double) info.denom; 
  13. return conversion * (double)difference; 
  14. }  
这里最重要的是调用mach_timebase_info。我们传递一个结构体以返回时间基准值。最后,一旦我们获取到系统的心跳,我们便能生成一个转换因子。通常,转换是通过分子(info.numer)除以分母(info.denom)。这里我乘了一个1e-9来获取秒数。最后,我们获取两个时间的差值,并乘以转换因子,便得到真实的时间差。
 
现在我们可能会想,为什么这比用clock好?看起来做了更多的事情。确实是有点,这便是为什么它在一个函数中。我们只需要传递我们的值到函数中并取得答案。
 
例子
让我们写个例子。下面是完整的代码清单(包括mach函数)。可以使用gcc mach.c –o mach来编译它:
     
     
  1. #include <stdio.h> 
  2. #include <stdlib.h> 
  3. #include <stdint.h> 
  4. #include <math.h> 
  5. #include<mach/mach_time.h>  
  6. //Raw mach_absolute_times going in,difference in seconds out 
  7. double subtractTimes( uint64_tendTime, uint64_t startTime ) 
  8. uint64_t difference = endTime -startTime; 
  9. static double conversion = 0.0; 
  10. if( conversion == 0.0 ) 
  11. {         
  12. mach_timebase_info_data_tinfo;         
  13. kern_return_terr = mach_timebase_info( &info );                       //Convert the timebaseinto seconds         
  14. if(err == 0  )                        
  15. conversion= 1e-9 * (double) info.numer / (double) info.denom;     
  16. }          
  17. return conversion * (double)difference; 
  18. }  
  19. int main() 
  20. inti, j, count; 
  21. uint64_t start,stop; 
  22. doublecurrent = 0.0; 
  23. doubleanswer = 0.0; 
  24. doubleelapsed = 0.0; 
  25. intdim1 = 256; 
  26. intdim2 = 256; 
  27. intsize = 4*dim1*dim2; 
  28. //Allocatesome memory and warm it up 
  29. double *array =(double*)malloc(size*sizeof(double));        
  30. for(i=0;i<size;i++)array = (double)i; 
  31. count= 5;      
  32. for(i=0;i<count;i++) 
  33. {               
  34. start = mach_absolute_time();                                  
  35. //dosome work 
  36. for(j=0;j<size;j++){ 
  37. answer+= sqrt(array[j]); 
  38. stop =mach_absolute_time(); 
  39. current= subtractTimes(stop,start); 
  40. printf("Timefor iteration: %1.12lf for answer: %1.12lf\n",current, answer); 
  41. elapsed+= current; 
  42. }        
  43. printf("\nTotaltime in seconds = %1.12lf for answer: %1.12lf\n",elapsed/count,answer); 
  44. free(array); 
  45. return 0; 
  46. }  
 
我们在这里做了什么?在这个例子中,我们有一个适当大小的double数组,当中存放了一些数字,然后获取这些数值的和的开方。为了测试,我们迭代了5次这个计算。每次迭代后我们打印花费的时间,并总结了计算所需的运行时间。在我的PowerMac G5(2.5)机器上,我获得如下结果:
 
     
     
  1. [bigmac:~/misc] macresearch% gcc mach.c -omach 
  2. [bigmac:~/misc] macresearch%./mach  
  3. Time for iteration: 0.006717496412for answer: 89478229.125529855490 
  4. Time for iteration: 0.007274204955for answer: 178956458.251062750816 
  5. Time for iteration: 0.006669191332for answer: 268434687.376589745283 
  6. Time for iteration: 0.006953711252for answer: 357912916.502135872841 
  7. Time for iteration: 0.007582157340for answer: 447391145.627681851387  
  8. Average time in seconds =0.007039352258 for answer: 447391145.627681851387 
 
注意,在这里我没有进行优化,因为编译器有方法避开这样的无脑循环。另外,这只是一个例子。如果是真正的代码,我们会进行优化。
 
好了,这就是这个例子的两个目的。
 
首先,我使用的数组大小比我的缓存大。我这样做的目的是因为我们需要注意到数据溢出缓存的情况(正如这个例子一样,至少在我的系统中是这样。如果是在MacPro中,不会出现这种情况)。我们将在以后讨论缓存的事宜。当然,这是一个做作的例子,但有一些东西可供思考。其次,你注意到在内存分配之前我写了一句注释,这是什么意思呢?
 
这在实际情况下是不需要关心的事情,因为内存总是在需要时已准备好使用。但当做一些小测试时来测试函数的性能时,它却可能是会影响到测试结果的实际问题。
 
当动态分配内存时,第一次访问内存管理时会将其清0(在OS X中不管使用哪种动态分配函数:malloc, calloc…所有内存在用户使用前都会清0)。内存清零是一种安全预防措施(我们不需要递交一些包含安全信息的内容,如解密密钥)
 
清零过程产生一个副作用(被系统标记为零填充页面故障)。所以为了让我们的计时更精确些,我们在使用内存之前一次性填充数据,以确保我们不会获取到零填充页面故障的处理时间。
 
让我们来测试一下,注释下面这行代码
for(i=0;i<size;i++) array =(double)i;
为:
//for(i=0;i<size;i++) array =(double)i;
 
再次运行测试
     
     
  1. [bigmac:~/misc] macresearch% ./mach 
  2. Time for iteration: 0.009478866798for answer: 0.000000000000 
  3. Time for iteration: 0.004756880234for answer: 0.000000000000 
  4. Time for iteration: 0.004927868215for answer: 0.000000000000 
  5. Time for iteration: 0.005227029674for answer: 0.000000000000 
  6. Time for iteration: 0.004891864428for answer: 0.000000000000  
  7. Average time in seconds =0.005856501870 for answer: 0.000000000000 
 
注意第一次迭代的时间比后序的时间多了将近一倍。同时还需要注意所有的answer都是0。再次说明内存被清零了。如果我们从堆上获取了内存,我们获取到的是无意义的数值。
 
最后,但很重要的一点。不要依赖于内存的清零操作。很有可能获取到的内存是从一个静态分配区而来,那么可能会导致如下这样的问题。
double array[3][3];
 
在我的系统上的打印结果是:
-1.99844 -1.29321e-231 -1.99844
-3.30953e-232 -5.31401e+303 0
1.79209e-313 3.3146e-314 0
所以需要特别注意。
 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值