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

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

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

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

  1.代码移动

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

/* convert string to lowercase: slow*/
void lower( char *s ){
    int i;
    for( i = 0;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 = 0; long int length = vec_length( v );
   data_t *data = get_vec_start( v );
   data_t acc = IDENT;
for( i = 0;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,使程序得以并发同时计算,最后再将两组结果想加,提高程序性能。

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

相关文章
|
3月前
|
存储 自然语言处理 编译器
【C语言】编译与链接:深入理解程序构建过程
【C语言】编译与链接:深入理解程序构建过程
|
5月前
|
存储 算法 C语言
"揭秘C语言中的王者之树——红黑树:一场数据结构与算法的华丽舞蹈,让你的程序效率飙升,直击性能巅峰!"
【8月更文挑战第20天】红黑树是自平衡二叉查找树,通过旋转和重着色保持平衡,确保高效执行插入、删除和查找操作,时间复杂度为O(log n)。本文介绍红黑树的基本属性、存储结构及其C语言实现。红黑树遵循五项基本规则以保持平衡状态。在C语言中,节点包含数据、颜色、父节点和子节点指针。文章提供了一个示例代码框架,用于创建节点、插入节点并执行必要的修复操作以维护红黑树的特性。
118 1
|
5月前
|
NoSQL 编译器 程序员
【C语言】揭秘GCC:从平凡到卓越的编译艺术,一场代码与效率的激情碰撞,探索那些不为人知的秘密武器,让你的程序瞬间提速百倍!
【8月更文挑战第20天】GCC,GNU Compiler Collection,是GNU项目中的开源编译器集合,支持C、C++等多种语言。作为C语言程序员的重要工具,GCC具备跨平台性、高度可配置性及丰富的优化选项等特点。通过简单示例,如编译“Hello, GCC!”程序 (`gcc -o hello hello.c`),展示了GCC的基础用法及不同优化级别(`-O0`, `-O1`, `-O3`)对性能的影响。GCC还支持生成调试信息(`-g`),便于使用GDB等工具进行调试。尽管有如Microsoft Visual C++、Clang等竞品,GCC仍因其灵活性和强大的功能被广泛采用。
158 1
|
5月前
|
算法 NoSQL IDE
C语言性能优化:代码优化技巧与工具。
C语言性能优化:代码优化技巧与工具。
142 0
|
5月前
|
编译器 C语言 计算机视觉
C语言实现的图像处理程序
C语言实现的图像处理程序
227 0
|
2月前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
70 5
|
2月前
|
C语言
C语言编程中,错误处理至关重要,能提升程序的健壮性和可靠性
C语言编程中,错误处理至关重要,能提升程序的健壮性和可靠性。本文探讨了C语言中的错误类型(如语法错误、运行时错误)、基本处理方法(如返回值、全局变量、自定义异常处理)、常见策略(如检查返回值、设置标志位、记录错误信息)及错误处理函数(如perror、strerror)。强调了不忽略错误、保持处理一致性及避免过度处理的重要性,并通过文件操作和网络编程实例展示了错误处理的应用。
77 4
|
2月前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
65 1
|
2月前
|
网络协议 物联网 数据处理
C语言在网络通信程序实现中的应用,介绍了网络通信的基本概念、C语言的特点及其在网络通信中的优势
本文探讨了C语言在网络通信程序实现中的应用,介绍了网络通信的基本概念、C语言的特点及其在网络通信中的优势。文章详细讲解了使用C语言实现网络通信程序的基本步骤,包括TCP和UDP通信程序的实现,并讨论了关键技术、优化方法及未来发展趋势,旨在帮助读者掌握C语言在网络通信中的应用技巧。
50 2
|
2月前
|
存储 算法 C语言
用C语言开发游戏的实践过程,包括选择游戏类型、设计游戏框架、实现图形界面、游戏逻辑、调整游戏难度、添加音效音乐、性能优化、测试调试等内容
本文探讨了用C语言开发游戏的实践过程,包括选择游戏类型、设计游戏框架、实现图形界面、游戏逻辑、调整游戏难度、添加音效音乐、性能优化、测试调试等内容,旨在为开发者提供全面的指导和灵感。
52 2