零散优化方法
1, 用inline或宏,可减少函数调用的开销(运行时的堆栈操作支持语句).另外在函数中的大量相似语句可以用
宏来实现(并不一定使变快,但源码可并小并更美观).
主意: (1),宏不做类型检查,仅仅是替换,在使用宏定义中的编排格式
(2), inline specifier仅仅是对编译器的建议,编译器有权利忽略这个建议。编译器根据函数体的大小,是否有局部对象被声明,函数的复杂性等等决定。
2,分清什么条件判断是运行时的,什么是编译时. 如果是在编译时可选择的代码段,可用宏实现.
3, 尽量将参数包成结构体指针或对象来传,因为这实际上就只是传个指针而已.
4, 能用更小的数据结构尽量用更小的,如C中无Bool类型,那么如果要用尽量不要用int,而是用char.另外尽量在结构体中使用位域运算符和union来节省空间.(union使用时应该注意任何时侯只能有一个数据成员有效,另外它没有运行时负担, 而在编译的代码生成阶段优化)
5, 将条件判断合并到起来.常用技巧是擅用Bit来代替byte或int, 假如有几个bool变量,并且需要根据它们值进行不同的处理.那么可以用一个byte,并将它的每一位表示一个bool变量.这样的好处在于你可以用一个位操作符判断一组bool变量.(同时这样也节约了空间,当发生内存拷贝或涉及到分布式,那其实就是提高效率)
6, 用内存换性能是不变的秘诀.如用一个大数组来代替动态在堆上分配.用多维数组来代替结构体数组.当然在使用上有它的局限性和复杂性.(值得注意的是当访问数组时.指针的效率却比数组下标,原因在于一个是指针变量++,另一个是数组的访问下标++,再将这个值加到数组起始地址上)
7, 结构体成员的对齐, 很多编译器有“使结构体字,双字或四字对齐”的选项。但是,还是需要改善结构体成员的对齐,有些编译器可能分配给结构体成员空间的顺序与他们声明的不同。但是,有些编译器并不提供这些功能,或者效果不好。所以,要在付出最少代价的情况下实现最好的结构体和结构体成员对齐.
实现策略: (1) 按数据类型的长度排序: 把结构体的成员按照它们的类型长度排序,声明成员时把长的类型放在短的前面。编译器要求把长型数据类型存放在偶数地址边界。在申明一个复杂的数据类型 (既有多字节数据又有单字节数据) 时,应该首先存放多字节数据,然后再存放单字节数据,这样可以避免内存的空洞。编译器自动地把结构的实例对齐在内存的偶数边界。
(2)把结构体填充成最长类型长度的整倍数 :把结构体填充成最长类型长度的整倍数。照这样,如果结构体的第一个成员对齐了,所有整个结构体自然也就对齐了。下面的例子演示了如何对结构体成员进行重新排序:
不好的代码,普通顺序:
struct
{
char a[5];
long k;
double x;
} baz;
推荐的代码,新的顺序并手动填充了几个字节:
struct
{
double x;
long k;
char a[5];
char pad[7];
} baz;
这个规则同样适用于类的成员的布局。
(3)按数据类型的长度排序本地变量 :当编译器分配给本地变量空间时,它们的顺序和它们在源代码中声明的顺序一样,和上一条规则一样,应该把长的变量放在短的变量前面。如果第一个变量对齐了,其它变量就会连续的存放,而且不用填充字节自然就会对齐。有些编译器在分配变量时不会自动改变变量顺序,有些编译器不能产生4字节对齐的栈,所以4字节可能不对齐。下面这个例子演示了本地变量声明的重新排序:
不好的代码,普通顺序
short ga, gu, gi;
long foo, bar;
double x, y, z[3];
char a, b;
float baz;
推荐的代码,改进的顺序
double z[3];
double x, y;
long foo, bar;
float baz;
short ga, gu, gi;
8 提高CPU的并行性
(1)使用并行代码
尽可能把长的有依赖的代码链分解成几个可以在流水线执行单元中并行执行的没有依赖的代码链。很多高级语言,包括C++,并不对产生的浮点表达式重新排序,因为那是一个相当复杂的过程。需要注意的是,重排序的代码和原来的代码在代码上一致并不等价于计算结果一致,因为浮点操作缺乏精确度。在一些情况下,这些优化可能导致意料之外的结果。幸运的是,在大部分情况下,最后结果可能只有最不重要的位(即最低位)是错误的。
不好的代码:
double a[100], sum;
int i;
sum = 0.0f;
for (i=0; i<100; i++)
sum += a[i];
推荐的代码:
double a[100], sum1, sum2, sum3, sum4, sum;
int i;
sum1 = sum2 = sum3 = sum4 = 0.0;
for (i = 0; i < 100; i += 4)
{
sum1 += a[i];
sum2 += a[i+1];
sum3 += a[i+2];
sum4 += a[i+3];
}
sum = (sum4+sum3)+(sum1+sum2);
要注意的是:使用4 路分解是因为这样使用了4段流水线浮点加法,浮点加法的每一个段占用一个时钟周期,保证了最大的资源利用率。
(2)避免没有必要的读写依赖
当数据保存到内存时存在读写依赖,即数据必须在正确写入后才能再次读取。虽然AMD Athlon等CPU有加速读写依赖延迟的硬件,允许在要保存的数据被写入内存前读取出来,但是,如果避免了读写依赖并把数据保存在内部寄存器中,速度会更快。在一段很长的又互相依赖的代码链中,避免读写依赖显得尤其重要。如果读写依赖发生在操作数组时,许多编译器不能自动优化代码以避免读写依赖。所以推荐程序员手动去消除读写依赖,举例来说,引进一个可以保存在寄存器中的临时变量。这样可以有很大的性能提升。下面一段代码是一个例子:
不好的代码:
float x[VECLEN], y[VECLEN], z[VECLEN];
。。。。。。
for (unsigned int k = 1; k < VECLEN; k ++)
{
x[k] = x[k-1] + y[k];
}
for (k = 1; k <VECLEN; k++)
{
x[k] = z[k] * (y[k] - x[k-1]);
}
推荐的代码:
float x[VECLEN], y[VECLEN], z[VECLEN];
。。。。。。
float t(x[0]);
for (unsigned int k = 1; k < VECLEN; k ++)
{
t = t + y[k];
x[k] = t;
}
t = x[0];
for (k = 1; k <; VECLEN; k ++)
{
t = z[k] * (y[k] - t);
x[k] = t;
}
9, 循环不变计算
对于一些不需要循环变量参加运算的计算任务可以把它们放到循环外面,现在许多编译器还是能自己干这件事,不过对于中间使用了变量的算式它们就不敢动了,所以很多情况下你还得自己干。对于那些在循环中调用的函数,凡是没必要执行多次的操作通通提出来,放到一个init函数里,循环前调用。另外尽量减少喂食次数,没必要的话尽量不给它传参,需要循环变量的话让它自己建立一个静态循环变量自己累加,速度会快一点。
还有就是结构体访问,东楼的经验,凡是在循环里对一个结构体的两个以上的元素执行了访问,就有必要建立中间变量了(结构这样,那C++的对象呢?想想看),看下面的例子:
旧代码:
total =
a->b->c[4]->aardvark +
a->b->c[4]->baboon +
a->b->c[4]->cheetah +
a->b->c[4]->dog;
新代码:
struct animals * temp = a->b->c[4];
total =
temp->aardvark +
temp->baboon +
temp->cheetah +
temp->dog;
一些老的C语言编译器不做聚合优化,而符合ANSI规范的新的编译器可以自动完成这个优化,看例子:
float a, b, c, d, f, g;
。。。
a = b / c * d;
f = b * g / c;
这种写法当然要得,但是没有优化
float a, b, c, d, f, g;
。。。
a = b / c * d;
f = b / c * g;
如果这么写的话,一个符合ANSI规范的新的编译器可以只计算b/c一次,然后将结果代入第二个式子,节约了一次除法运算。
10,采用递归
与LISP之类的语言不同,C语言一开始就病态地喜欢用重复代码循环,许多C程序员都是除非算法要求,坚决不用递归。事实上,C编译器们对优化递归调用一点都不反感,相反,它们还很喜欢干这件事。只有在递归函数需要传递大量参数,可能造成瓶颈的时候,才应该使用循环代码,其他时候,还是用递归好些。
三, 结构及算法优化.
1, 尽可能用大量的hash替代查找和搜索,常用的方法是用hash实现通过数组下标访问元素.当然如果数组中存放的是函数指针,那实际上就是快速动态改变代码执行流了.
2, 查表,原理就是先计算好,要使用时只需直接查表,而查表这个动作又可用hash.(不要在自己的主循环里搞什么运算工作,绝对是先计算好了,再到循环里查表。
分享到:
相关推荐
C代码优化经验总结.doc
### 编译原理之代码优化概述 #### 一、引言 代码优化是编译原理中的一个重要组成部分,它旨在提高程序的执行效率或减少资源占用。优化的目标通常是在不改变程序逻辑的前提下,使得生成的代码在运行时更快或更节省...
### SQL优化经验总结34条 #### 一、选择最有效率的表名顺序 - **要点**: 在基于规则的优化器(RBO)中,Oracle解析器从右至左处理FROM子句中的表名。为了提高性能,应将记录条数最少的表放在最后,即作为基础表。...
总结来说,代码优化是一个多层次的过程,需要根据具体情况选择合适的优化策略。算法级优化关注算法设计,C语言级优化利用编程语言特性,而汇编级优化则深入到硬件层面。每个层次都有其独特的作用和挑战,恰当的组合...
### Java代码重构经验总结 在软件开发过程中,代码重构是一项重要的技能,它旨在不改变代码外部行为的前提下,改进其内部结构,从而提升代码质量和可维护性。本文将深入探讨Java代码重构的关键点,涵盖重构原则、...
【标题】"JVM优化经验总结Java开发Java经验技巧共15页.p" 提供的信息表明,这是一份关于Java开发中的JVM优化经验的详细总结,共有15页的内容。在Java开发过程中,理解并掌握JVM(Java虚拟机)的优化技巧是至关重要的...
在移植优化的经验总结中,还涉及到了对术语的定义和参考资料的列举,这对于其他项目工作的知识积累和问题解决有着重要的参考价值。例如,“SIP”即Session Initiation Protocol(会话初始协议),它是应用层协议,...
在编译原理中,中间代码优化...6. **总结与展望**:总结实验经验,对未来工作提出建议和展望。 通过这个试验,学习者不仅可以深入理解编译原理,还能掌握实际编程技巧,为今后从事软件开发或系统优化奠定坚实的基础。
【C6XX优化经验总结】 在C6XX处理器的编程过程中,优化是提高程序性能的关键。C6XX系列是TI(德州仪器)推出的高性能数字信号处理器,主要用于嵌入式系统,尤其是在信号处理应用中。本篇文章主要围绕C6XX的程序优化...
"项目优化总结,经验积累" 该资源摘要信息是关于项目优化总结和经验积累的知识点总结。下面是从给定的文件信息中生成的相关知识点: 1. 个人介绍:该部分介绍了作者刘佳伟的个人信息,包括姓名、毕业院校、专业、...
C6000软件优化是针对德州仪器(TI)C6000系列DSP处理器进行的一种性能提升技术,旨在最大化代码执行效率,减少资源消耗,提高系统响应速度。C6000系列主要包括C62x、C64x、C66x等多个子系列,广泛应用于数字信号处理...
以上只是部分可能涵盖的Java代码优化知识点,实际的"java代码优化总结1.0版本.md"文件可能包含更详细的信息,如具体的代码示例、实战经验分享以及性能测试结果等。通过学习和应用这些知识,开发者可以显著提升Java...
### 优化SQL Server数据库的经验总结 #### 一、引言 在现代企业的信息化建设中,数据库作为信息系统的核心组件之一,其性能的好坏直接影响着整个系统的响应速度和用户体验。SQL Server作为一款广泛使用的数据库...
《VC编程经验总结源代码》是一份集合了Visual C++高级编程技巧与实践的宝贵资源,对于初学者和有经验的开发者来说都是极具价值的学习材料。这份压缩包中包含了一系列的开发代码实例,旨在帮助读者深入理解VC++编程的...
在VC编程领域,经验往往比...通过学习和借鉴"VC编程经验总结"中的实践,开发者可以避免常见陷阱,提高工作效率,提升代码质量,更好地应对复杂项目。无论是初学者还是有经验的开发者,这份资料都是一份值得参考的宝藏。
本文将分享一位开发者针对SqlSever2005一千万条以上记录的分页数据库优化经验,包括索引优化和代码优化,帮助读者理解如何应对大规模数据处理中的性能瓶颈。 首先,当数据量达到千万级别时,数据库的性能表现将直接...
**鲸鱼算法优化BP神经网络** 鲸鱼算法(Whale Optimization Algorithm, WOA)是一种新型的全局优化算法,灵感来源于鲸鱼捕食的行为。...同时,此案例也提供了对优化算法在机器学习领域应用的深入理解和实践经验。
在本文中,我们将深入探讨MTK程序开发的经验总结,为新手提供宝贵的指导。 一、MTK平台介绍 MTK平台是MediaTek推出的一系列芯片解决方案,广泛应用于智能手机和平板电脑等移动设备。其特点包括高度集成的硬件模块、...