第十讲 C的性能优化 BIT/TI 第十讲 C的性能优化 由于C是C6000编程的首选语言,因此如何对C代码的性能进行优化,是我们非常关心的问题。这部分内容放在最后介绍,主要是因为有些优化方法需要前面的内容做铺垫。 BIT/TI 第十讲 C的性能优化
建议的程序开发流程 C程序 C程序优化 用线性汇编改 写关键代码段 BIT/TI 第十讲 C的性能优化 首先需要明确本讲内容在整个代码开发流程中的地位,与后面两个步骤紧密相关,非常重要。 用户程序用C程序实现,这样可以实现开发效率与程序效率的较好统一。如果要采用汇编语言实现关键的代码段,从编程效率考虑,也最好是在C的框架下。因此,本章首先介绍C程序的开发过程,以及相关开发工具的使用。 BIT/TI 第十讲 C的性能优化
学习内容 一、C代码的性能优化 二、混合语言编程-编写C可调用的汇编程序 三、实验 变量声明-两种变量访问方式 C编译选项 Intrinsics 字访问 二、混合语言编程-编写C可调用的汇编程序 建立C环境 建立汇编环境 编写线性汇编函数 三、实验 这里的性能优化是指代码执行速度的优化 BIT/TI 第十讲 C的性能优化
一、C代码的性能优化 1. 变量声明 BIT/TI 第十讲 C的性能优化
对局部变量的访问 在堆栈内分配存储空间 用堆栈首地址作首基地址,用指针*+B15(disp)来访问 堆栈分配在默认段.stack BIT/TI 第十讲 C的性能优化
全局变量/静态变量-两种访问方式 1.默认的访问方式-Near变量 int n; main() { ... n +=... } 编译后的汇编输出和访问方式 .bss _n, 4, 4 ldw.d1 *+DP(_n), A0 2.Far变量 far int n; n += ... _n .usect .far,2,2 mvk _n, A1 mvkh _n, A1 ldw.d1 *+A1, A0 了解C编译器对变量的访问方式,尤其是全局变量和静态变量(局部变量用对方指针访问,稍后介绍),是非常重要的。这是因为不同的方法效率不同,直接音响到程序的执行速度。 前面介绍了变量的MVK/MVKH/LD访问方式。这里介绍另一种方式。 在.bss内分配地址 一条指令访问 在.far内分配地址 三条指令访问 BIT/TI
Near变量的生成和使用 C语言 汇编语言 相对偏移地址 LDW .D2 *+B15(12), Reg Near 变量的生成和使用 如果不用far特别说明,C编译器会默认地将全局和静态变量分配在.bss段,并使用页指针B14(DP)-基地址+offset的方法来访问。 所有全局变量和静态变量都分配在.bss段内 .bss的开始地址被称为基地址或页指针,用DP来表示,在C6000 C编译器即B14 所有全局变量和静态变量都定义为near,其标号表示在.bss段内的偏移地址(在其它地方,标号一般表示一个绝对地址)-对堆栈的访问也是用这种方法实现的(只不过基地址SP用B15表示)。 LDW .D2 *+B15(12), Reg BIT/TI 第十讲 C的性能优化
为什么要使用Far变量? 程序中使用的全局变量和静态变量超过了32K字节 需要把变量存放在.bss以外的数据段 *+DP(offset) 限制在15位 如A/D变换后的数据存在FIFO,就必须与片内的.bss分开存放。 BIT/TI 第十讲 C的性能优化
Far变量的生成 使用关键字Far定义,在.far段内分配地址 用#pragma DATA_SECTION ( )定义新的数据段 far short m; short far n; #pragma DATA_SECTION (y, “myVariables”); int y[32]; 关键字far 新的数据段 Pragma是在C语言里表示汇编指令的方法(directive) 作用相当于伪指令.usect BIT/TI 第十讲 C的性能优化
Near/Far变量-例子 声明 C变量名加下划线 编译输出 BIT/TI 第十讲 C的性能优化 对用户定义的数据段,必须在连接器命令文件内说明其连接地址 BIT/TI 第十讲 C的性能优化
变量声明总结 局部变量在堆栈段.stack分配地址,用一条指令访问。 采用near形式声明全局变量,变量分配在数据段.bss,用一条指令访问。 采用far形式声明全局变量,变量分配在数据段.far或用户自定义数据段,用三条指令访问,应尽量避免采用。 BIT/TI 第十讲 C的性能优化
一、C代码的性能优化 2. C编译选项 BIT/TI 第十讲 C的性能优化
C优化器选项 优化器选项 有软件流水功能 BIT/TI 第十讲 C的性能优化 C6x的C编译器能够达到很高的编译效率,关键在于它的C优化器。
与优化有关的其它编译选项 建议使用 不要使用 Aliasing -pm 与-o3合用,进行程序级优化 两个指针指向同一个变量, -mt 程序中没有数据aliasing -x2 函数内联 不要使用 -ml 大模式(使得.bss段内的变量都按far方式访问) -g 符号调试 -s, -ss, -os C编译器生成的汇编文件内,C语句作为注释出现 Aliasing 两个指针指向同一个变量, 或一个指针修改后指向 另外一个变量 -o3 文件级优化选项。与-pm合用,可以进行程序级优化。 -pm 选项-pm使得语法分析器在启动优化器和代码产生器之前,把所有的C文件组合成一个文件来处理。这样做可以使得优化能对整个程序进行,因而优化效率通常会更高。 -mt 使用选项–mt向编译器说明在程序中没有出现aliasing(两个指针指向同一个变量,或一个指针修改后指向另外一个变量)。有了这个信息优化器在进行优化时就可能效率更高。而选项–ma与-mt正相反,它通知编译器在程序中使用了aliasing,因而编译器在进行优化时会更谨慎。例如:向量加法有两个输入数组和一个输出数组的首地址作输入参数,如果输入数组的地址与输出数组不同,而可以使用选项-mt向编译器说明;以便优化器在优化时,不考虑输入数组指针与输出数组指针之间的数据相关性。 -x2 内联选项。使用–x2之后,编译器会把小于指定长度的函数,以及使用了inline关键字的函数进行内联处理。在进行第四级优化时(-o3),内联选项-x2自动启用。 -ml 大模式选项(big endian)。大模式选项用于说明程序中使用了超过32K字节的static和global变量。如果不特别指明是near,编译器会把每个变量都作为far变量处理。即编译器对变量的访问用mvk/mvkh来代替更有效的页指针模式*+DP(offset))方式。这样做会增加代码长度,降低程序执行速度。 -g 符号调试选项。符号调试选项-g会使得许多与符号调试不兼容的优化手段无法使用,三级优化(-o2)是与符号调试兼容的最高优化级别。 -s,-ss,-os 交叉列表功能选项。在最初的功能调试阶段,把C语句、汇编语句以及编译器注释放在一起,对调试会有帮助。但是如同选项-g一样,交叉列表功能可能会影响到代码的优化,这是因为它会使得优化不能跨越C语句进行,影响优化效果。所以如果希望最大程度的优化,就不要使用交叉列表选项–s,-ss或-os。 BIT/TI 第十讲 C的性能优化
3. Intrinsics Intrinsics:直接与C62xx汇编指令相对应的特殊内联函数,没有函数调用开支。 Intrinsics是代表一些C62xx指令的C函数。经过编译后,这些函数被相应的汇编指令代替。Intrinsics 是C6000所特有的,它能大大提高C编译器的编译效率,因而是对标准ANSI C的很好补充。 使用Intrinsics时,只要用Intrinsics代替相应的C代码即可。而且在Intrinsics内可以直接使用C的变量名代替寄存器。 BIT/TI 第十讲 C的性能优化
常用Intrinsics列表 加法、减法、乘法 位域操作、long转换 为int 对应汇编指令 .trip BIT/TI
几种编程方式的比较 代码效率低 C代码 y = a * b; 容易破坏C环境 使用Intrinsics的C代码 y = _mpy (a, b); 嵌入汇编 asm (“MPY A0, A1, A2”); C代码:编程周期最短,但是代码效率不高 使用Intrinsics的C代码:兼顾编程难易程度和代码效率 嵌入汇编:因为是面向寄存器的操作,所以可能会破坏C环境(破坏寄存器内容等) 汇编代码:效率高,但是编程周期长 汇编代码 MPY A0, A1, A2, ; a, b, y 编程工作量大 BIT/TI 第十讲 C的性能优化
Intrinsics的特点 函数参数使用C变量名(不是寄存器),与C环境兼容 不增加C的编程工作量 代码效率与汇编相同 BIT/TI
一、C代码的性能优化 4. 字访问 BIT/TI 第十讲 C的性能优化
字访问优化方法 1. 利用32位字访问16位数据(三种方法) 1) 联合Union 2) 强制类型转换 3) 把数据直接定义为32位字 2. 利用Intrinsics完成数值运算(_mpy, _mpyh, _add2, _sub2〕 C6000的内部数据总线和寄存器都是32位的,因此对于16位的short类型数据,我们可以一个LD指令取两个16位数据,并可进一步用C6000的两个16位乘法器在一个周期内完成两个16位乘法,提高算法效率。以上功能在汇编语言内只要用指令LDW和MPY/MPYH就可以实现。但是用C语言实现上述功能,涉及到如何对16位数据进行32位访问?下面介绍三种方法: BIT/TI 第十讲 C的性能优化
1) 字访问-联合Union 必须按照word 数据定界 BIT/TI 第十讲 C的性能优化 使用C语言里的Union类型,把要访问的16位short类型定义为union类型。定义为联合的变量占用同一块存储空间,但是可以用多种方式进行访问。 BIT/TI 第十讲 C的性能优化
2) 字访问-强制类型转换 16位访问 32位访问 BIT/TI 第十讲 C的性能优化 使用C语言的强制类型转换。使用方法如图所示 2) 字访问-强制类型转换 16位访问 使用C语言的强制类型转换。使用方法如图所示 short DP(short *m, short *n, short count) { short i ; int pro_h, pro_l ; int sum_h = 0 ; int sum_l = 0 ; int sum = 0 ; int *data_a = ( int *) m ; int *data_x = ( int *) n ; count = count >> 1 ; for(i=0;i<count;i++) { pro_l = _mpy( data_a[i], data_x[i]) ; pro_h = _mpyh( data_a[i], data_x[i]) ; sum_l += pro_l ; sum_h += pro_h ; } sum = sum_l + sum_h ; return( sum) ; } 32位访问 BIT/TI 第十讲 C的性能优化
3) 字访问-把数据直接定义为32位字 BIT/TI 第十讲 C的性能优化
字访问小结 用union方式需要对调用函数和被调用函数进行修改 用强制类型转换,只需要修改被调用函数 直接定义为32字,影响程序可读性 实验1的字访问是采用强制类型转换法实现的。 BIT/TI 第十讲 C的性能优化
二、混合语言编程 -C与线性汇编的混合编程 BIT/TI 第十讲 C的性能优化
编写可被C调用的线性汇编函数 1. 建立C环境 2. 建立汇编环境 3. 编写线性汇编函数 入口代码 算法 出口代码 BIT/TI
1. 建立C环境 1) 在C程序前声明线性汇编函数 2) 调用汇编函数 1)声明 2)调用 BIT/TI 第十讲 C的性能优化
2. 建立汇编环境 1) 汇编函数的入口地址声明为全局变量 汇编函数的入口地址-C函数名前加下划线 2) 定义函数入口地址 1)声明 2)定义 BIT/TI 第十讲 C的性能优化
3. 编写线性汇编函数 入口 代码 出口 代码 在第九章介绍的线性汇编编程方法 BIT/TI 第十讲 C的性能优化
! 线性汇编的寄存器保护问题 汇编优化器自动完成 BIT/TI 第十讲 C的性能优化 函数参数传递、返回值传递 堆栈指针(堆栈栈顶地址:程序开始时是第一个可用的堆栈地址)、数据页指针(.bss段首地址) BIT/TI 第十讲 C的性能优化
实验1 实验目的: 实验内容 学习和掌握利用intrinsics进行字长优化 改写C语言点积函数,用强制类型转换法实现字优化。 实验9 BIT/TI 第十讲 C的性能优化
实验2 实验目的: 实验内容 掌握线性汇编语言和C语言混合编程的方法 用线性汇编改写C的点积函数。 BIT/TI 第十讲 C的性能优化 实验9 BIT/TI 第十讲 C的性能优化