TI C6000优化手册——让代码看起来像钉子
DSP芯片的出现,是为了解决大量的数字运算问题。通过集成专用的加法器、乘法器、地址产生器、复杂逻辑等硬件单元,DSP能实现比普通单片机更快速的数字运算,使处理器更适用于实时性高、复杂度强的处理场合。也正因为如此,DSP编程中非常重要的一环就是让代码尽可能高效地运行。
本文基于TI C6000硬件架构,针对C语言编程,介绍其中主要的代码优化方法。
指导方针
在做优化前,当建立以下几点信念:
循环最重要。显然,几乎所有耗时的运算都是在循环中进行,我们几乎可以说:代码的优化就是对循环的优化。
最坏原则。TI CCS编译器集成有优化器,能对C/C++代码以及汇编代码进行性能优化。但是过度的优化可能会导致程序运行错误,因此在缺少信息的情况下,编译器总是抱着最坏的打算进行优化,以优先保证程序的正确性
如果说TI提供的硬件架构和编译器优化工具是一把锤子,那编程者就应该让代码用起来像钉子。
高度优化的c/c++代码性能可以非常接近手写的汇编代码。由于汇编代码的复杂性,我们可以优先选择C/C++进行程序编写。图1为TI提供的各编程语言及其优化版本间的运算性能比较示意图。
图1 各语言优化前后的性能对比
优化并不是必须的,一般我们可以通过如图2所示的四个进阶的开发阶段,来判断对程序进行何种程度的优化。
图2 DSP 程序开发的一般过程
实用工具
如前面“锤子”与“钉子”的比喻,编程者的优化工作是尽可能地使代码能被处理器的功能单元、编译器等“锤子”充分运用,所以有必要先对它们建立基本的认识。
1. 高性能的C6000 DSP 架构
8个并行的功能单元,2组寄存器,分离的程序和数据存储;256bit取指包,能一次取指8个32bit的指令;2路64bit的数据加载/存储。这些都指向一个核心——并行处理。
图3 C6000 DSP 架构
2. C6000流水线
一个指令操作的完成实际上要经历取指、译码、执行三个阶段的多个过程才能实现,TI提供软件流水编排来让多个“工种”同时对多个操作进行流水式的处理,大大提高了运算的吞吐能力。
C64x+, C674x和 C66x系列内核还增加了软件流水循环缓存(SPLOOP buffer)单元,使得软件流水能更快速地加载数据,并可以被暂时打断。
However, the SPLOOP buffer cannot be used to handle loops that exceed 14 execute packets
For complex loops, such as nested loops, conditional branches inside loops, and function calls inside loops, the effectiveness of the compiler may be compromised.
3. SIMD(Single Instruction, Multiple Data)
C6000支持单指令多数据存取,仅一条指令,就能一次性操作64bit的数据,这64bit可以由多个双字/字/字节数据组成。
4. C6000 C Compiler
编译器是代码优化工作的最终执行者,它分析代码的相关信息,并做出优化的决策。但有时,编译器无法仅通过分析代码获得一些对于优化很重要的信息,这时编程者主动提供必要的信息给编译器就显得非常重要。
我们可以通过编译选项、关键词以及pragma编译指示来告知编译器和优化有关的信息。同时,还可以利用编译器的优化返回信息,进一步调整优化策略。
5. 其它
- 内嵌函数
TI提供一套内嵌函数供编程者调用,内嵌函数由一些特定的指令组成,配合DSP芯片的硬件函数功能单元,能高效地完成一些用C语言很难完成的复杂操作。
The intrinsic operations are not function calls (though they have the appearance of function calls), so no branching is needed.Instead, the use of intrinsic is a way to tell the compiler to issue one or more particular instructions of the C6000 instruction set.
- 优化的库函数
TI也将一些通用的运算模块封装成库函数,这些库函数都经过了深度的优化,能特别高效地完成相应的运算。
优化策略
1. 选择合适的编译选项(介绍部分)
- -o0/1/2/3:最重要的优化选项;如果选择了-o3,编译器将会尽可能尝试所有可行的优化手段,但有时也可能会使得优化后的程序出现错误。-o0和-o1将不会产生优化错误,但优化的性能则大大降低。
- -g:允许编译器插入符号调试信息,它在开发调试阶段是非常好的工具,但在最终产品代码编译中应避免使用,因为它会减少并行处理指令,并占用额外的代码空间,极大地影响代码性能。
- -mt:指示编译器,应用中所有的函数中的指针参数都不指向同一个地址,但它只作用于函数参数中的指针,对函数内部的本地指针无效。
- -k和-mw:这两个选项与最终代码性能无关,但利用他们可以获得编译器的优化反馈信息,帮助编程者调整优化策略。-k选项保留编译器的汇编输出;-mw输出软件流水的相关信息
2.“restrict”关键字
restrict的作用和-mt编译选项相似,它告诉编译器某个指针不会和函数中的其它指针指向同一个内存。
the restrict keyword can be applied to any pointers, regardless of whether it is a parameter, a local variable, or an element of a referenced dataobject. Moreover, because the restrict keyword is only effective in the function it is in, it can be used whenever needed
注意:“restrict”关键字也不能随便乱加,我们需要了解C6000的片上内存组成,只有当两个指针所指的内存在不同的block里时,restrict才是合法的。
3. 通过编译指示(pragma)提供信息
可以在代码中插入一些特定语法的编译指示指令,来告知编译器有关代码的一些信息。因为编译器在缺少信息的情况下,总是以最坏的打算来优化代码,如果编程者能提供一些关键信息,会大大帮助编译器做出好的决策。最常用的有MUST_ITERATE和UNROLL两种。
- MUST_ITERATE:提供关于循环次数的一些确切信息:最小可能循环次数、最大可能循环次数以及循环次数为某个factor的倍数。它的使用语法如下:
- UNROLL:展开指示告诉编译器可以对循环代码进行适当的展开,其使用语法如下:
在展开指示之前,也最好用MUST_ITERATE指令告诉编译器循环次数为展开系数的倍数,这样可以避免产生额外的代码来处理异常情况,如:
展开的好处有两点:一是使得编译器可以更加均衡地利用各运算单元;二是编译器有更多的机会使用SIMD指令。但有一点需要注意的是:展开将会使得循环体增大。
4. 循环体优化的注意事项
循环的优化关键在于使得循环能够被编排成软件流水。
For complex loops, such as nested loops, conditional branches inside loops, and function calls inside loops, the effectiveness of the compiler may be compromised. When the situation becomes too complex, the compiler might not be able to pipeline at all.
编译器仅对内部循环执行软件流水。
软件流水循环可包含instrinsics,但不能包含函数调用。
循环结构中不可有break和goto语句,不可有条件中止,使循环提前退出的指令。
条件代码应尽量简单,在C64XX中,条件代码需要超过6个寄存器时,循环不可进行软件流水。
避免循环体内容过于复杂,造成寄存器组不够用。
如果要求一个寄存器的生命太长,这个代码不能进行软件流水。
循环结构中不要包含改变循环计数器数值的代码。
5. 使用SIMD并行处理多个数据的运算
TI 提供了多个支持SIMD的指令,如LDDW、STDW,分别用于并行64bit的数据加载和存储。
Assuming all the data used are actually 16-bit data, it would be ideal if the LDDW and STDW instructions can operate on 4 elements every time. However, if thedata is declared as 32-bit type, LDDW and STDW can only operate on 2 elements every time. Therefore,to fully utilize SIMD instructions, it is important to choose the smallest data size that works for the data.
尽管C64x+, C674x, C66x内核支持对非对齐的数据使用SIMD指令,但当数据量比较大时,还是应该尽量保证数据是边界对齐的,这样可以充分地进行并行存取。
6. 尽可能使用内嵌函数
内嵌函数来直接调用C6000的汇编操作,而这些操作往往用C语言实现很复杂。
The intrinsic operations are not function calls (though they have the appearance of function calls), so no branching is needed.Instead, the use of intrinsic is a way to tell the compiler to issue one or more particular instructions of the C6000 instruction set.
7. TI库函数
针对常见的运算,TI提供了高效的实现库函数,包含基础的通用模块如图4。当然,针对一些专用领域(如图像视频、通信等),也会有对应的库函数,这些库函数都经过了深度的优化,运行是非常高效率的。
图 4 TI基础库函数类别
8. 使用内联函数代替函数调用
内联函数(Inline Function)是C语言语法的一种,它的定义类似于普通的函数定义,但编译器在处理它的时候并不把它当函数对待,而是把它的函数体自动嵌入到被调用处。
由于在循环体中,函数调用将会影响到软件流水的编排,并且产生调用开销,用内联函数代替函数调用是个不错的选择。
但是注意:内联函数将可能增加代码的尺寸,应避免内联函数体过大或被频繁调用。
9. 尽可能使用逻辑运算代替乘除运算
乘除运算指令的执行时间要远远超过逻辑移位指令,尤其是除法指令,在设计的时候,可以根据实际情况,进行一些调整,尽量用逻辑移位运算来代替乘除运算,这样可以加快指令的运行时间。
参考文献/资料
【1】Introduction to TMS320C6000 DSP Optimization--SPRABF2,2011.
【2】TMS320C6000 Programmer's Guide--SPRU198K,2011.
【3】董言治, 娄树理, 刘松涛. TMS320C6000系列DSP系统结构原理与应用教程[M]. 清华大学出版社, 2014.
【4】TI C6000 优化 startup guide.
【5】Optimization Techniques for the TI C6000 Compiler.
【6】Optimizing Modems Using Code Composer Studio and TI Resources.
·END·
欢迎来我的微信公众号做客:信号君
专注于信号处理知识、高性能计算、现代处理器&计算机体系
技术成长 | 读书笔记 | 认知升级
幸会~
TI C6000优化手册——让代码看起来像钉子的更多相关文章
- TI C6000 优化进阶:循环最重要!
软件流水循环 1. C6000流水线(Pipeline) 一个指令的处理过程并不是一步完成,它被分为三个阶段:取指(Fetch).译码(Decode).执行(Excute).将每一个阶段放入独立的流程 ...
- Java 性能优化手册 — 提高 Java 代码性能的各种技巧
转载: Java 性能优化手册 - 提高 Java 代码性能的各种技巧 Java 6,7,8 中的 String.intern - 字符串池 这篇文章将要讨论 Java 6 中是如何实现 String ...
- TI C6000 数据存储处理与性能优化
存储器之于CPU好比仓库之于车间.车间加工过程中的原材料.半成品.成品等均需入出仓库,生产效率再快,如果仓库周转不善,也必然造成生产阻塞.如同仓库需要合理地规划管理一般,数据存储也需要恰当的处理技巧来 ...
- MySQL通用优化手册
转载: MySQL通用优化手册 内容提纲 MySQL的特点: 硬件.系统优化: MySQL 配置优化: SCHEMA设计优化: SQL 优化: 其他优化. MySQL 的特点 首先,需要明确的是.想要 ...
- CssStats – 分析和优化网站 CSS 代码的利器
CssStats 是一个在线的 CSS 代码分析工具,你只需要输入网址或者直接 CSS 地址即可进行 CSS 代码的全方位分析,是前端开发人员和网页设计师分析网站 CSS 代码的利器,可以统计出 CS ...
- 好看的IDE配色方案让代码看起来不再那么凶猛了
写这篇小文的初衷是,笔者是原教旨主义者,一直坚持用IDE默认的配色方案.另外也觉得网上黑色系的配色方案太过bling bling了.但今天尝试用新的配色方案后,兴奋地发现对代码的好感度大幅提升. 嗯, ...
- 优化C/C++代码的小技巧
说明: 无意看到一篇小短文,猜测作者应该是一个图形学领域的程序员或专家,介绍了在光线(射线)追踪程序中是如何优化C/C++代码的.倒也有一些参考意义,当然有的地方我并不赞同或者说我也不完全理解,原文在 ...
- 优化C/C++代码的小技巧(转)
源:http://www.cnblogs.com/lizhenghn/p/3969531.html 说明: 无意看到一篇小短文,猜测作者应该是一个图形学领域的程序员或专家,介绍了在光线(射线)追踪程序 ...
- 《阿里巴巴Java开发手册》代码格式部分应用——idea中checkstyle的使用教程
<阿里巴巴Java开发手册>代码格式部分应用--idea中checkstyle的使用教程 1.<阿里巴巴Java开发手册> 这是阿里巴巴工程师送给各位软件工程师的宝典,就像开车 ...
随机推荐
- Oracle同义词。。。
同义词 --私有同义词--私有同义词权限grant create synonym to scott;--创建私有同义词create synonym dp for scott.dept;--将查询dep ...
- 基于FCM的消息推送功能
需求背景 我方项目需要支持客户端消息推送,iOS终端可以借由苹果本身的apns很方便的实现,但是对于Android来说,必须集成第三方的SDK来处理.考虑到项目需要以及成本,我们选择使用谷歌的FCM框 ...
- BZOJ3624: [Apio2008]免费道路(最小生成树)
题意 题目链接 Sol 首先答案一定是一棵树 这棵树上有一些0边是必须要选的,我们先把他们找出来,如果数量$\geqslant k$显然无解 再考虑继续往里面加0的边,判断能否加到k条即可 具体做法是 ...
- 跨平台移动开发phonegap/cordova 3.3全系列教程-结合asp.net/jqmboile
遠程app配置 把編譯後的www資料夾,復制到遠程地址(目錄結構不要改變), 例如:建議使用app-framework 1.加入jquery mobile1.4点击打开链接 2.加入app-frame ...
- django ORM 简单示例简绍
简单 models 操作 class Host(models.Model): nid = models.AutoField(primary_key=True) #Nid为主键 hostname = m ...
- HDU 3351 Seinfeld 宋飞正传(水)
题意: 给出一个串,串内只有大括号,问经过几次改变可使全部括号合法?改变指的是可以将某一方向的括号变成另一方向. 思路: 利用栈的特点,若出现成对的合法括号,直接删掉,留下那些不合法的成为一串.既然不 ...
- hiho一下 第三十九周 归并排序求逆序数
题目链接:http://hihocoder.com/contest/hiho39/problem/1 ,归并排序求逆序数. 其实这道题也是可以用树状数组来做的,不过数据都比较大,所以要离散化预处理一下 ...
- UOJ#130 【NOI2015】荷马史诗 K叉哈夫曼树
[NOI2015]荷马史诗 链接:http://uoj.ac/problem/130 因为不能有前缀关系,所以单词均为叶子节点,就是K叉哈夫曼树.第一问直接求解,第二问即第二关键字为树的高度. #in ...
- 流媒体 5——MPEG声音
1. 听觉系统的感知特性: MPEG声音的数据压缩和编码不是依据波形本身的相关性和模拟人的发音器官的特性,而是利用人的听觉系统的特性来达到压缩声音数据的目的,这种压缩编码称为感知声音编码. 许多科学工 ...
- 编写WsHttpBinding的WCF通信方式
这个通信方式本人实验了好久,需要一个重要的条件是服务端和客户端的发送内容方式都是相同的声明,需要在配置文件写入,客户端: <system.serviceModel> <binding ...