本文译自: Performance Tuning of Code
http://www.newlc.com/performance-tuning-code
应用程序运行正常,可以执行用户或客户所需的所有任务,并不意味着程序在任何方面都是完美的。 使用性能分析工具,比如profiler,我们就能明白程序在内部是如何运行的,在速度、内存占用、 电量使用等方面的情况。 能降低你的应用程序性能的问题有许多,下面我将列举一些:
在Symbian,按链接方式可分为两种类型的链接库: 静态接口的dll和多态接口的dll。通常应用 程序与许多dll静态链接,需要这些dll与否取决于不同的用况,但所有的依赖dll都装入内存将会 降低应用程序性能,这是因为应用程序或服务程序的静态依赖对自由内存有很大影响。通过把逻辑 相关的功能分割成动态可装载的部件会将静态依赖减小到最少。
- void RuntimeLoadDllL()
- {
- RFs fileSession;
- User::LeaveIfError(fileSession.Connect());
- TFileName dllName;
- If ( SomeCondition )
- {
- .......
- dllName = KDllNameOne;
- }
- else
- {
- .........
- dllName = KDllNameTwo;
- }
- LoadDllL(dllName);
- .....................
-
- fileSession.Close();
- }
-
- void LoadDllL(const TFileName& aLibName)
- {
- RLibrary lib;
- User::LeaveIfErrorlib.Load(aLibName));
- .....
- }
void RuntimeLoadDllL()
{
RFs fileSession;
User::LeaveIfError(fileSession.Connect());
TFileName dllName;
If ( SomeCondition )
{
.......
dllName = KDllNameOne;
}
else
{
.........
dllName = KDllNameTwo;
}
LoadDllL(dllName);
.....................
fileSession.Close();
}
void LoadDllL(const TFileName& aLibName)
{
RLibrary lib;
User::LeaveIfErrorlib.Load(aLibName));
.....
}
ECOM也提供了一种标准的机制可在运行时装入指定的实现。
TRAP的使用应尽量少,TRAP会消耗比你以为的要多的资源。因为导致内核执行机构调用TTrap::Trap() 和TTrap::UnTrap(),以及在运行时分配一个结构存放当前堆栈上下文以便万一发生leave事件时回 退到之前的状态。若你的类含有超过6个以上TRAP,意味着class设计不正确,需要重新设计。只要可能, 就把TRAP移到函数调用的更外层。
应用程序启动时马上连接到服务是不可取的(当连接到瞬时服务时),因为这将导致连锁反应,多个服务 程序的依赖部件会装入内存。如果服务很少使用,这种方法可以看作是无效的资源使用。随需连接到瞬时 服务总是明智的。应当以这样的方式来设计,客户API的实现总是与实际的服务实现相分离,这样,当客 户端程序链接到客户API库,就不会自动装载服务程序依赖项,而这应发生在服务程序真正启动时。 在设计服务程序时总是考虑它的使用情况和运行时需要的各种资源。假定服务提供三个功能,比如读、写 和通知,读/写不是很频繁的操作。这种情况下,最好分成两个服务,一个提供重量级功能,诸如读/写/替 换等,将被按客户端需要转载/卸载,另外一个提供轻量级的通知服务。
在应用程序的MMP文件使用compresstarget声明指出目标可执行文件应该被压缩。通过这样做,可执行 文件的代码和数据段以Huffman+LZ77算法压缩。这将使得存储的可执行文件在文件系统中使用更少的空 间。在被装入时,可执行文件装载器解压这个文件。所有的可执行文件不应该被解压,只有那些占用大量空 间的可选择解压。
如果盘符已知,那么总是在文件路径指定驱动盘符。若没指定盘符,将以标准的Symbian OS顺讯搜索所 有可用的驱动盘。若你的文件在Z:,那么你将有极大的损失,因为Z:总是最后才搜索到。
在S60应用程序,缺省的线程堆栈大小只有8kb,因此只有很小的对象才该在栈上创建,并尽可能早地从 栈中移除。除了基本类型,我们应该总是以引用传递参数而不是按值。大对象和数组的创建应该在堆中完 成,并尽可能减小自动变量的生命周期。
若我们为函数参数声明了缺省值,额外的代码将被编译器生成,这可能也是一个费用。若缺省值常用,那 么值得重载这个方法,而不是提供缺省参数值。
- void MyFunction( TInt aCounterVal, TInt aParamLength = 2);
-
- Can be written as :
-
- void MyFunction( TInt aCounterVa );
- void MyFunction( TInt aCounterVal, TInt aParamLength );
void MyFunction( TInt aCounterVal, TInt aParamLength = 2);
Can be written as :
void MyFunction( TInt aCounterVa );
void MyFunction( TInt aCounterVal, TInt aParamLength );
避免按值传递大对象,总是引用或指针优先。除非可以接受NULL值,使用引用更好因为它更适合更简单易用。 引用不能被重新赋值,若你需要先指向一个对象后来指向其它的,那么就使用指针。引用总是比指针更好,因 为它更有效,基于这样的事实:编译器必须通过所有转换保留空指针的值,也就是说,编译器必须测试NULL值, 从而创建更多的指令。
inline关键字不是强制编译器那样做,完全依赖于编译器决定自己的行为。若函数较大(比如: 超过3-4行 代码)就避免使用inline。推荐使用inline方法的情况有:有一些get或set方法时; T类的简单构造函数; 以及使用瘦模板时。 关于inline方法有更多的问题,比如,实现代码改变将导致二进制的不兼容。若你有受限内存资源问题而不 是速度问题,方法调用成本优于大段的inline代码。 使用构造函数初始化列表比在构造函数体内赋值更为有效,因为使用缺省构造函数,类成员变量将自动构造, 优先于类构造函数内的那些条目。但构造函数列表不允许我们对变量做校验,因此并不总是适合的。
在写任何代码之前,我们应该总是考虑磁盘空间问题。特别是当我们在MMC或内存卡上写一些内容时。 SysUtil有 DiskspaceBelowCriticalLevelL() and MMCSpaceBelowCriticalLevelL()之类的 API函数,这些函数在传入程序员想写字节的大小就可以调用了。调用上述方法时不必传递fileserver会话 对象,这使得它们不那么昂贵,因此否则就需要创建一个临时的会话。RFs也提供了解决这个问题的API函数。
描述符被设计成是高效的,但是完全依赖于它们的使用。若有可能,尽量使用T类指针描述符诸如TPtrC和 TPtr而不是TBuf或TBufC,因为TBuf建在栈上(鲜有在堆上),只用于小字符串,一般地推荐最多16个字符。 就HBufC来说,尽量使用RBuf,因为它是优化的及其动态本质。避免创建类对象的多个局部实例,比如: TFileName、TParse等等,用成员变量更佳。 避免使用_L宏,因为与构造临时描述符对象有关的额外运行时成本,这也是它们在产品代码中被作废的原因。
数组的使用完全依赖于需要。若数组大小在编译时已知,那么可以用TFixedArray。然而绝大多数情况下需要 动态数据,我们可以用CArrayXXXX或指针型数组比如CArrayPtrFlat、CArrayPtrSeg和RPointerArray。 一般地,我们总是优先使用RArray或RPointerArray,而不是CArray或CArrayPtr。 基于运行时扩展的需要,我们可以考虑使用Segmented或Flat类型。在Flat数据上迭代是很快的因为连续,而 segment数组的扩展是很快的,因为内部实现为链表。