龙芯2E平台程序性能分析
来源:互联网 发布:命若琴弦知乎 编辑:程序博客网 时间:2024/04/30 01:19
当前版本: 0.1
完成日期: 2007-3-27
作者: Dajie Tan <jiankemeng@gmail.com>
1. 分析一段代码的性能,最常用的方法是测量这段代码的运行时间。假如我要分析下面这两段代码的性能差异,可以在代码前后插入2个变量,分别记录运行前后的时间,相减即可:
代码一:
int matrix[2047][7];
int main()
{
int i, j, sum = 0;
unsigned int beg, end;
A1:
for(i = 0; i < 7; i++)
for(j = 0; j < 2047; j++)
sum += matrix[j][i] * 1024;
B1:
}
代码二:
int matrix[2047][7];
int main()
{
int i, j, sum = 0;
unsigned int beg, end;
A2:
for(i = 0; i < 2047; i++)
for(j = 0; j < 7; j++)
sum += matrix[i][j] * 1024;
B2:
}
则可以在A1,A2处插入 beg = times(NULL); // 返回以过去某点为基准点的时钟滴答数
B1, B2处插入 end = times(NULL); // 需要包含头文件 sys/times.h
相减即可得到该段代码的运行时间。
当 前linux系统的每秒经过的时钟滴答数可以使用sysconf(_SC_CLK_TCK)获得,我在linux-2.6.18 for loongson2e的环境下获取的值为 100,即最小测量的精度是10ms。这个精度要远远高于time() 1s的精度,但是这个精度还是无法满足上面测量要求,因为分别编译运行后,两段代码的运行时间是一致的,这个结果是不能被接收的,从定性的分析开看,代码 二的性能肯定是优于代码一的。
2. 使用龙芯2E系统协处理器(CP0)中的两个性能计数器(performance counter)
龙 芯2E之系统协处理器中引入了两个性能计数器,对应CP0寄存器的25号,长度为64位,低32位作为Counter 0,高32位作为Counter 1。其中CP0的24号寄存器作为2个计数器的控制寄存器,亦是64位,可以设置它,分别控制两个计数器所记录的事件。
CP0的24号寄存器简单描述:3~0 位作为使能位目前置1;第5位作为中断使能位目前置0;8~5为Counter 0的计数事件,12~9 作为Counter 1的计数事件,其余位置0。
比 如我要控制 Counter 0对时钟周期进行计数,Counter 1对一级数据缓存不命中进行计数,可以将CP0D的24号寄存器置值为 0x80f(01000 0000 1111),即Counter 0的计数事件为0000,Counter 1的计数事件为0100,这个值可以查看龙芯2E用户手册。
3. 设置性能计数器的计数事件
Counter 0常用计数事件(4位):
0000: 时钟周期计数
0001: 分支指令计数
0100: 一级I-cache不命中计数
1001: 从主存中读计数
1110: TLB重填例外计数
Counter 1常用计数事件(4位):
0001: 分支预测失败计数
0100: 一级D-cache不命中计数
0111: 访问未缓存计数
1001: 写到主存计数
1100: ITLB不命中计数
目前的内核起起来后,CP0的24号寄存器为 0x1ef,故而我要重设24号寄存器的值 0x80f(01000 0000 1111,Counter 0的计数事件为0000,Counter 1的计数事件为0100).
对龙芯2E系统协处理器的寄存器执行写操作在用户空间是没有写权限的。我们可以写一个简单的模块,把设置的操作放在模块初试化函数中来完成:
[set_event.c]
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
static int set_init(void)
{
unsigned long op = 0x80f;
asm volatile("mtc0 %0, $24"::"r"(op));
printk(KERN_ALERT "cp0_24 set complete/n");
return 0;
}
static void set_exit(void)
{
unsigned int val = 0;
asm volatile("mfc0 %0, $24":"=r"(val));
printk(KERN_EMERG "current cp0_24 value is 0x%x/n", val);
}
module_init(set_init);
module_exit(set_exit);
相应的Makefile为:
ifeq ($(KERNELRELEASE),)
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
.PHONY: modules modules_install clean
else
# called from kernel build system: just declare what our modules are
obj-m := set_event.o
endif
4. 解决问题
在使用上述模块编译、安装(insmod),设置好两个计数器的计数事件后,我们依然在A1,A2插入代码,读取两个计数器的值,并记录:
unsigned int cache_miss_beg, cache_miss_end;
asm volatile
(
".set mips3/n/t"
"dmfc0 %0, $25/n/t"
"dsra %0, 32/n/t"
:"=r"(cache_miss_beg) //记录运行前Counter 1的值,L1 D-cache的事件
);
asm volatile("mfc0 %0, $25":"=r"(beg)); //记录运行前Counter 0的值,CPU时钟周期事件
在B1,B2插入:
asm volatile("mfc0 %0, $25":"=r"(end)); //记录运行后Counter 0的值,CPU时钟周期事件
asm volatile
(
".set mips3/n/t"
"dmfc0 %0, $25/n/t"
"dsra %0, 32/n/t"
:"=r"(cache_miss_end) //记录运行后Counter 1的值,L1 D-cache的事件
);
printf("Total CPU Cycles is: %d/n", end - beg);
printf("The number of L1 D-Cache miss is: %d/n", cache_miss_end - cache_miss_beg);
基于计数器的测量方法,精度很高,可以得到程序执行期间所经过的时钟周期数。但是影响其准确性的因素也很多,主要有因进程上下文切换,致使其他进程占用处理器,影响计数器的计数,这个可以通过在低负载的机器上多次运行求平均值的方法来弱化。
如上面的例子我们执行 init 1 进入单用户模式下,此时系统仅有一个bash进程,分别执行5次求得平均值:
代码一所需时钟周期为: 1035200
代码一一级数据cache缺失次数为: 2431
代码二所需时钟周期为: 735310
代码二一级数据cache缺失次数为: 2092
代码一所需时钟周期是代码二的 1.4 倍
代码一一级数据cache缺失次数是代码二的 1.16 倍
可以看到,代码一访问数组按行访问(C语言中,数组存放于内存是按列存放),这样CPU对数据的访问就会呈“跳跃”的形态,违背了空间局部性,致使cache命中率下降,影响了整个代码的性能。
测试代码: http://people.openrays.org/~comcat/misc/pmc.tar.gz
- 龙芯2E平台程序性能分析
- APUE.2e程序清单4-7举例分析
- APUE.2e程序清单4-7举例分析
- linux 平台性能分析工具
- linux 平台性能分析工具
- linux 平台 性能分析 工具
- 程序数据库性能分析。
- 程序性能分析
- 程序性能分析工具
- [Python]程序性能分析
- 程序性能分析
- 程序性能分析profiling
- 程序性能分析
- 程序性能分析
- 性能分析框架和性能监控平台
- LPC-10e综合器程序分析
- 程序性能分析及性能测试
- 程序性能分析及性能测试
- javaIO 中JDK 1.1以后的IO流组合例子
- sql分页存储过程大比拼
- uint int Number
- JSP页面的初始化是使用JSP标签还是使用JavaScript
- sql 语句 复制另一个表的同样的表结构
- 龙芯2E平台程序性能分析
- vi使用手册
- 使用一条SQL语句删除表中重复记录
- xml文件解析-DOM
- C# 删除子目录
- Hibernate open session in view
- 用delphi实现MD5加密算法
- xml文件解析-JDOM
- xml文件解析-DOM4J