CUDA程序优化心得之测时函数的设计

近一年来,学习CUDA也算是有苦有甜,苦的是遇到问题时的无助、苦恼和迷茫,甜的是问题解决时的兴奋,今天就利用这五一的假期把自己的经验结合CUDA优化的内容写下来,希望对于那些依旧在其中挣扎的同学有所帮助。

一、 测时
判断程序优劣的最简单方式就是计算程序的运行时间,在同一台机器上,运行时间短的程序一般来说是更优的,当然不能一概而论,毕竟决定程序运行速度的因素很多,比如算法,机器的指令集,使用的语言等等。
我们的目的是要设计一个既能够在windows上运行,又能够在linux上运行,既可以用于C,又可以用于CUDA的计时器,同时最好便于扩展。计时的方法很多,如标准C库的time、clock系列函数,CUDA的事件,linux下的gettimeofday,windows下的GetTickCount。
本计时器系列采用C++的面向对象设计方法,基本的思路是设计一个父类,然后再从父类创造子类。这样要建造一个新计时器,只要继承父类就行了。
父类代码如下:

	class TimeCounter{
		protected :
			clock_t startp,endp;
		public :
			TimeCounter():startp(-1),endp(-1){}
			void start(){//设置计时起点
			#ifdef __CUDACC__
				cudaThreadSynchronize();
			#endif
				startp=clock();
			}
			void stop(){//设置计时终点
				if(-1==startp){
					perror("you must set start point at first");
				}else{
				#ifdef __CUDACC__
					cudaThreadSynchronize();
				#endif
					endp=clock();
				}
			}
			virtual long getTimeDiff()=0;//返回时间差滴答数
			virtual void printTimeDiff()=0;//打印出时间差
	};

子类秒计时器代码如下:

	class SecondCounter:public TimeCounter{
		public :
		    long getTimeDiff(){
				 if(-1==endp){
					perror("you must set stop point before invoke this function");
					exit(1);
				}else{
					return (endp-startp)/CLOCKS_PER_SEC;
				}
			}
			void printTimeDiff(){
				long temp=getTimeDiff();
				printf("use time :%lds\n",temp);
			}
	};

子类毫秒计时器代码如下:
	class MillisecondCounter:public TimeCounter{
		public :
		    long getTimeDiff(){

				 if(-1==endp){
					perror("you must set stop point before invoke this function");
					exit(1);
				}else{
					return 1.0f*(endp-startp)/CLOCKS_PER_SEC*1000;
				}
			}
			void printTimeDiff(){
				long temp=getTimeDiff();
				printf("use time :%ldms\n",temp);
			}
	};

子类微秒计时器代码如下:
#ifdef __CUDACC__
class MicrosecondCounter:public TimeCounter{
		public:
			long getTimeDiff(){
				if(-1==endp){
					printf("please set start point or end point\n");
					exit(1);
				}else{
				return 1.0f*(endp-startp)/CLOCKS_PER_SEC*1000000;
				}
			}
			void printTimeDiff(){
				long temp=getTimeDiff();
				printf("use time:%ld us\n",temp);
			}
	};
#endif

这种设计使得我们可以声明一个计时器父类引用,而让其实际指向子类,这样,如果精度小了的话,就可以只更改一处,而不用更改其它的内容了。在我的实践中,这种基于对象的多态设计的计时器工作得很好。在开始计时处调用start方法,在计时终点调用stop方法,调用getTimeDiff方法返回经过的时间数,但是这个方法有个问题就是如果两个不同的子类的计时器,其用此函数获得的值不能直接相加减,由于这个问题我本来想将其设置private,但是最终还是让其保持public,因为这符合C/C++的设计思想:程序员是最聪明的,他们应当知道会发生什么,一切由他们来决定。函数printTimeDiff打印计时时间。
如果有一天C语言的clock函数精度达到了微秒,我们只用将包围MicrosecondCounter类的条件编译语句去掉就行了。如果有一天其精度达到了纳秒,我们只用再写一个类继承TimeCounter类就行了,不用改写任何代码。

[ 本帖最后由 yyfn风辰 于 2010-5-4 21:32 编辑 ]

呵呵,楼主啊。。我想在我的程序中直接用你的??

怎么用啊??
我没有学过C++啊??
稍微相信一点就可以了,呵呵呵。以后一定去看看C++,现在时间有点紧

下面有个例子

int main(int argc,char *argv){

MicrosecondCounter mc;
TimeCounter& tc = mc;

tc.start();

int a = 0;
for(int i = 0; i < 10000000; i++){
a += 5;
}
tc.stop();

tc.printTimeDiff();

return 0;

}