《使用龙芯2f性能计数器评测程序性能.doc》由会员分享,可在线阅读,更多相关《使用龙芯2f性能计数器评测程序性能.doc(10页珍藏版)》请在三一办公上搜索。
1、使用龙芯2f性能计数器评测程序性能评测一个程序的性能有多种方法,对目前大多数benchmark,程序执行时间是一个很重要的标准。但大多数情况下时间仅仅能给出一个结果,却给不了更多的信息,如cache miss,CPU功能部件的利用率,访存指令,分支预测等。这些信息对于知道一个程序的优化往往会起到很重要的作用。很多现代的CPU都有一些内置的计数器用来记录这些事件,X86环境下可以调用一些库对这些计数器进行设置和访问,具体可以参考这里。关于X86下使用performance counter的方法很多,而且资料也很容易找到,大家google一下就好了。龙芯从早一个版本(loongson 2e)就提供
2、了性能计数器,龙芯2f的计数器基本同2e大概上没有什么区别,定义了两个性能计数器(performance counter),分别映射到CP0(协处理器)的第24、25号寄存器。25号寄存器是64位,其高32位用作counter1,对应的event1,低32位用作counter0,对应event0,event0和event1是由24号寄存器的5-12位控制的,其中5-8位为event0,9-12位为event1,用户可以通过设置24号寄存器相应的位来控制counter记录的事件,event0和event1对应的事件如下所示:event0:0000周期0001分支指令0010指令0011指令并且域
3、rs=31 0100一级I-cache缺失0101 Alu1操作已发射0110 Mem操作已发射0111 Falu1操作已发射1000猜测指令1001从主存中读1010固定发射队列满的次数1011重排队列满的次数1100 CP0队列满的次数1101 Tlb重填例外1110例外1111内部例外event1:0000提交操作0001分支预测失败0010预测失败0011 JR且rs=31预测失败0100一级D-cache缺失0101 Alu2操作已发射0110 Falu2操作已发射0111 Uncached访问1000 BHT猜测错误1001写到主存1010浮点指针队列满的次数1011分支队列满的次
4、数1100 Itlb缺失1101例外总数1110投机缺失1111队列向前加载要使用计数器,首先要设置24号寄存器中event0和event1所对应的域。比如你要测一级I-Cache miss和一级D-Cache miss,那么就要把24号寄存器设置成0x44f(0-4位以及其他位为默认值)。但是有一个问题,龙芯2f协处理器只有在内核态下才能更改,因此我们要更改24号寄存器,需要模块的帮助。龙芯的协处理器是通过MFC0/DMFC0来读和通过MFT0/DMFT0来写的,如果在用户态下使用MFT0/DMFT0指令,会在运行时得到非法指令的错误信息。即便是在内核态下,很多寄存器也是只读的。下面我们写一
5、个简单的小模块来更改event0和event1:loongson_counter.c#include linux/init.h#include linux/module.h MODULE_LICENSE(Dual BSD/GPL);static int event_init(void)unsigned int event=0x44f;asm volatile(mtc0%0,:r(event);return 0;static void event_exit(void)module_init(set_init);module_exit(set_exit);makefileifneq($(KERNE
6、LRELEASE),)obj-m:=loongson_counter.o else KERNELDIR?=/lib/modules/$(shell uname-r)/build PWD:=$(shell pwd)clean:rm-rf*.o*core.depend.*.cmd*.ko*.mod.c.tmp_versions default:$(MAKE)-C$(KERNELDIR)M=$(PWD)modules endif make生成模块后,使用insmod loongson_counter.ko将模块加载,这时就可以使用25号寄存器记录I-cache miss和D-cache miss了。
7、在具体程序中,如果你要计算一个函数带来的cache miss,可以在这个函数开头通过MFC0/DMFC0指令读取计数器中的初始值,然后在函数结尾,再读取一次,两次相减就得到了该函数造成的cache miss。注意硬件性能计数器是一个全局的计数器,即如果你在运行你要测试的函数同时运行了其他程序,其他程序造成的cache miss也会记录在计数器里。所以在保持cpu独占的情况下可以测试几次取平均值。下面以一个sort()函数为例示例怎么在函数中应用:sort.cvoid sort()usigned int icache_init,dcache_initi,cache_miss,dcache_mis
8、s;asm volatile(.set mips3ntdmfc0%0,ntdsra%0,32ntmfc0%0,nt:=r(dcache_init),=r(icache_init);/把counter的初始值记录下来qsort(.);.;asm volatile(.set mips3ntdmfc0%0,ntdsra%0,32ntmfc0%0,nt:=r(dcache_miss),=r(icache_miss);/记录函数运行后计数器的值fprintf(stderr,I cache miss is:%dn,icache_miss-icache_init);fprintf(stderr,D cach
9、e miss is:%dn,dcache_miss-dcache_init);/红色部分为使用performance counter所加入的代码现在我们已经可以使龙芯2f的计数器起作用了,但是还存在一个问题,如果我有一个代码量相对比较多的程序,想做一个工具来多方面的评测这个程序,许要使用到各种不同的计数器事件,而又不想每次都重新编译加载一个内核来改变这个事件,这时候就需要写一个相对稍复杂一些的模块用来设置事件,下面是我写的一个模块,可以通过write系统调用来改变事件。loongson_counter.c#include linux/module.h#include linux/init.h#
10、include linux/fs.h#include linux/cdev.h MODULE_LICENSE(Dual BSD/GPL);MODULE_AUTHOR(sponge);MODULE_DESCRIPTION(change event of performance counter);MODULE_ALIAS(set_event);/function convert char to int int convert_event(char*array)int ret=0;int i;for(i=0;i=3;i+)/printk(ret is:%x,i is%x,arrayiis%cn,re
11、t,i,arrayi);if(arrayi=1)ret=ret+(1(3-i);else if(arrayi=0)continue;else return-EINVAL;return ret;/function used to modify the register int event_write(struct file*filp,const char _user*buff,size_t count,loff_t*f_pos)int e0,e1;/variables hold the event number char buff_k9;/buffer holds date transform
12、from user space char event05;char event15;/these two array hold event number in form of char int i;/loop counter int regval;/the value that will transform to if(count!=9)return-EINVAL;/copy user date if(copy_from_user(buff_k,buff,9)return-EFAULT;/convert user date to array for(i=0;i=3;i+)event0i=buf
13、f_ki;event1i=buff_ki+4;/convert char to int if(e0=convert_event(event0)=-EINVAL)return-EINVAL;if(e1=convert_event(event1)=-EINVAL)return-EINVAL;/printk(event0 is%x,event1 is%xn,e0,e1);/compute regval=(e0 5)|(e1 9)|0xf;asm volatile(mtc0%0,:r(regval);/unsigned int val=0;/asm volatile(mfc0%0,:=r(val);/
14、printk(KERN_EMERGcurrent cp0_24 value is 0x%xn,val);return 0;struct cdev event;struct file_operations event_ops;/init function static int init_event(void)/register the device event if(register_chrdev_region(MKDEV(33,0),1,pcounter)return-EFAULT;/register operations event_ops.write=event_write;cdev_in
15、it(&event,&event_ops);cdev_add(&event,MKDEV(33,0),1);return 0;/clean function static void exit_event(void)/unregister device cdev_del(&event);unregister_chrdev_region(MKDEV(33,0),1);module_init(init_event);module_exit(exit_event);Makefile跟上一个例子的基本一致。使用该模块的方法为:1、在root下使用命令mknod/dev/pcounter c33 02、在root下使用命令chmod 666/dev/pcounter 3、编译该模块,使用insmod加载模块4、在程序中可以通过write系统调用来更改性能计数器的事件,代码如下:#define writelong 9int fd;fd=open(/dev/pcounter,O_RDWR);write(fd,01000100,writelong);/写入的8个字符,前4位表示event0,后4位表示event1