其实大多数的编译器本身就能提供一些简单的优化,比如gcc就能通过使用 -O2 或者 -O3 的选项来优化程序。但编译器的优化始终也是有限,因为它必须小心翼翼保证优化过程不对程序的功能有改动。故而程序员本身应该对程序有优化意识。在我看来,这也是应该有的一种良好的编程习惯。

  几种比较简单的优化措施:

  1.代码移动

  将要执行多次(比如在循环中)但计算结果不会改变的计算,移动到代码前面不会多次求值的部分。举一个比较极端的例子:

/* convert string to lowercase: slow*/
void lower( char *s ){
int i;
for( i = ;i < strlen(s);i++ )
if( s[i] >= 'A' && s[i] <= 'Z' )
s[i] -= ( 'A' - 'a');

}

因为C语言的字符串是以null结尾的,函数strlen也必须一步一步得检查这个序列,直到遇到null字符。那么假象一下,如果字符串s是一个很长的字符串,那么这个函数自然会造成许多不必要的开销!!
故而在循环体内,要注意将计算结果不改变的计算移动到前面避免多次重复计算。

  优化代码:

/* convert string to lowercase: faster*/
void lower( char *s ){
int i;
int len = strlen(s);
for( i = 0;i < len;i++ )
if( s[i] >= 'A' && s[i] <= 'Z' )
s[i] -= ( 'A' - 'a'); }

  2.消除不必要的存储器引用

  在C语言中用指针变量读写是用CPU寄存器间接寻址然后从内存中读写,而使用函数内部的局部变量,则是使用CPU中的通用寄存器。而主存读写和CPU内部通用寄存器的寻址的速度相差数十倍的。举一个小例子

for( i = 0;i < len;i++ ){
*dest = *dest + data[i];
}

这个循环体每次都会从主存中读写,优化如下:

int acc;
for( i = 0;i < len;i++ ){
acc = acc + data[i];
}
*dest = acc;

这样就会使那个指针只写入一次,而acc变量在cpu的执行过程中是使用cpu内部通用寄存器读写,故而能加快速度。

  3.循环展开

  循环展开,顾名思义就是将一次一步的迭代循环展开成一次两步或更多,减少迭代次数。循环展开从两个方面改善程序的性能,首先,它减少了不直接有助于程序结果的操作的数量,比如循环索引计算和条件分支。其次,它提供了一些方法,可以进一步变化代码,减少计算中关键路径上的操作数量。比较如下两个函数,第一个为常规循环,第二个为循环展开函数,

//normal function to add all element of v
void combine1( vec_ptr v,data_t *dest ){
int i = ;
long int length = vec_length( v );
   data_t *data = get_vec_start( v );
   data_t acc = IDENT;
for( i = ;i < length;i++ ){
acc = acc + data[i];
}
*dest = acc;
}
//unroll loop by 2
void combine2( vec_ptr v, data_t *dest ){
int i;
long int length = vec_length( v );
loing int limit = length -1;
data_t *data = get_vec_start( v );
data_t acc = IDENT; for( i = 0;i < limit;i += 2 ){
acc = ( acc + data[i] ) + data[i+1];
} for( ;i < length;i++ ){
acc = acc + data[i];
} *dest = acc;
}

第二个函数将循环展开,并在最后检查会不会遗漏。减少了一些关键步骤,故而优化了程序。

  4.提高并行性

  在cpu中,程序被翻译成汇编指令,但却并不是一条一条指令按顺序执行的,而是流水线并发执行的,即多条不相关指令共同执行。这是cpu的机器特性,而我们要做的,就是多多利用这种机器特性。

  让我们来分析程序的combine2中的核心循环内部语句:acc = ( acc + data[i] ) + data[i+1];在这个循环中,data[i+1]的计算必须放在( acc + data[i] )之后,因为它们是相互关联的,这明显是不利于程序的并行操作,改进如下。

//unroll loop by 2,2-way parallelism
void combine3( vec_ptr v, data_t *dest ){
int i;
long int length = vec_length( v );
loing int limit = length -1;
data_t *data = get_vec_start( v );
data_t acc0 = IDENT;
data_t acc1 = IDENT; for( i = 0;i < limit;i += 2 ){
acc0 = acc0 + data[i];
acc1 = acc1 + data[i+1];
} for( ;i < length;i++ ){
acc0 = acc0 + data[i];
} *dest = acc0 + acc1;
}

这段代码将acc拆分成acc0和acc1,使程序得以并发同时计算,最后再将两组结果想加,提高程序性能。

代码优化通常都会带来可读性的降低,如何取舍应该好好考虑清楚,必要时刻,或许应该多加一些注释说明。

以C语言为例的程序性能优化 --《深入理解计算机系统》第五章读书笔记的更多相关文章

  1. [JAVA] java程序性能优化

    一.避免在循环条件中使用复杂表达式 在不做编译优化的情况下,在循环中,循环条件会被反复计算,如果不使用复杂表达式,而使循环条件值不变的话,程序将会运行的更快. 例子: import java.util ...

  2. iOS程序性能优化

    iOS程序性能优化 一.初级 使用ARC进行内存管理 在iOS5发布的ARC,它解决了最常见的内存泄露问题.但是值得注意的是,ARC并不能避免所有的内存泄露.使用ARC之后,工程中可能还会有内存泄露, ...

  3. iOS 程序性能优化

    前言 转载自:http://www.samirchen.com/ios-performance-optimization/ 程序性能优化不应该是一件放在功能完成之后的事,对性能的概念应该从我们一开始写 ...

  4. [转]C#程序性能优化

    C#程序性能优化 1.显式注册的EvenHandler要显式注销以避免内存泄漏 将一个成员方法注册到某个对象的事件会造成后者持有前者的引用.在事件注销之前,前者不会被垃圾回收.   private v ...

  5. Java程序性能优化之性能概述

    性能的基本概念 一).什么叫程序的性能? 程序运行所需的内存和时间. 二).性能的表现形式: 1).执行速度: 程序的反应是否迅速,响应时间是否足够短. 2).启动时间:程序从运行到可以处理正常业务所 ...

  6. [深入浅出Cocoa]iOS程序性能优化

    本文转载至 http://blog.csdn.net/kesalin/article/details/8762032 [深入浅出Cocoa]iOS程序性能优化 罗朝辉 (http://blog.csd ...

  7. C++ 应用程序性能优化

    C++ 应用程序性能优化 eryar@163.com 1. Introduction 对于几何造型内核OpenCASCADE,由于会涉及到大量的数值算法,如矩阵相关计算,微积分,Newton迭代法解方 ...

  8. Java程序性能优化技巧

    Java程序性能优化技巧 多线程.集合.网络编程.内存优化.缓冲..spring.设计模式.软件工程.编程思想 1.生成对象时,合理分配空间和大小new ArrayList(100); 2.优化for ...

  9. 《Java程序性能优化:让你的Java程序更快、更稳定》

    Java程序性能优化:让你的Java程序更快.更稳定, 卓越网更便宜,不错的书吧

随机推荐

  1. Angular02 通过angular-cli来搭建web前端项目

    利用angular-cli的常见命令: npm i --save 包名   ->  软件依赖    npm i --save-dev 包名     ->  开发依赖    ng new 项 ...

  2. CSS3对于盒中容纳不下的内容的显示——overflow属性

    overflow属性 1.如果将overflow属性值设定为hidden,则超出容纳范围的文字将被隐藏起来. div{ overflow:hidden; } 2.如果将overflow属性值设定为sc ...

  3. SpringBoot(一)走进Springboot的世界

    什么是spring boot Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员 ...

  4. [51nod Round15 E ]Danganronpa

    AC自动机,树上莫队,树状数组.. 比赛的时候完全看不出来...赛后去膜了一波网上题解才明白= = 离线,先把AC自动机建出来,把fail边反向建出fail树. 发射言弹,就是将言弹结束节点的fail ...

  5. HDU6235-Permutation-水题-2017中国大学生程序设计竞赛-哈尔滨站-重现赛

    Permutation Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)Tot ...

  6. FineReport调用存储过程

    "总结一下本人在项目中遇到的问题,如何在数据库表名未知且作为一种查询条件的情况下查询出数据集,仅能通过FineReport+Oracle实现. 首先分析这个问题的条件和要求: 条件:只有一个 ...

  7. 获取Object对象的length

    所有JS程序猿(甚至不止JS)都知道,数组(Array)是有length的,通过length属性,可以很方便的获取数组的长度.可以说,只要使用到了数组,就必会使用到其length属性. 而Object ...

  8. UserView--第一种方式set去重,基于Spark算子的java代码实现

    UserView--第一种方式set去重,基于Spark算子的java代码实现 测试数据 java代码 package com.hzf.spark.study; import java.util.Ha ...

  9. dede的pagelist标签的listsize数字属性详解

    转载▼http://blog.sina.com.cn/s/blog_a4f3bd4e01012c8n.html dede的pagelist标签的listsize数字属性详解.见远seo经常用织梦搭建各 ...

  10. SSH会话连接超时问题

    目前大多数ssh服务是运行在Linux系统上的sshd服务.当访问终端在windows上时,各终端软件,如,putty,SecureCRT等,大多支持设置向服务器端自动发送消息,来防止终端定期超时.其 ...