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

zzzzMing-大数据技术 zzzzMing-大数据技术     2022-08-04     635

关键词:

  其实大多数的编译器本身就能提供一些简单的优化,比如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,使程序得以并发同时计算,最后再将两组结果想加,提高程序性能。

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

《深入理解计算机系统》优化程序性能的几个方法(代码片段)

本文几个优化程序性能的方法出自CSAPP第五章,通过不断修改源代码,试图欺骗编译器产生有效的代码我们先引入度量标准每元素的周期数(CPE),表示程序性能。 我们先定义一个数据结构  data_t代表数据类型1typedefst... 查看详情

《深入理解计算机系统》速读提问

一、计算机系统漫游本章通过运行一个hello程序为例,概述了计算机操作系统的运行过程,讲述了组成计算机系统的硬件和系统软件,讲到了处理器处理一个程序的过程。这一章中出现了一个我首次听说到的词汇Amdahl定律,该定... 查看详情

[阅读笔记]深入理解计算机系统

5、优化程序性能1.存储器别名(memoryaliasing),妨碍优化。__restrict关键字2.消除不必要的存储器引用,使用临时变量。6、存储器层次结构 1.寄存器0周期,cache1-10,主存50-100. 冷不命中coldmiss和冲突不命中conflictmiss。 2.cache结... 查看详情

《机器学习系统:设计和实现》以mindspore为例的学习

机器学习系统:设计和实现1.导论1.1机器学习系统的需求:1.支持多种神经网络2.支持自动微分3.支持数据管理和处理4.支持模型的训练和部署5.高效使用计算加速器6.分布式计算1.2机器学习系统基本组成1.编程接口:提... 查看详情

2018-2019-120189215《深入理解计算机系统》第一章(代码片段)

第一章计算机系统漫游主要内容全面精炼的概括了本书的内容,也就是“计算机系统概述”,包括:1.解释计算机系统中“信息”的概念:就是二进制位;2.解释源程序(以C源程序为例)到可执行程序的过程:预处理→编译→汇... 查看详情

《深入理解计算机系统(第三版)》第一章

1.知识总结(主要对新知识)(1)计算机提供不同层次的抽象表示,来隐藏实际实现的复杂性文件是对I/O设备的抽象表示虚拟存储器是对主存和磁盘I/O设备的抽象表示进程是对处理器、主存和I/O设备的抽象表示(2)程序员必须... 查看详情

《深入理解计算机系统(原书第2版)》pdf

...p;· · · · ·本书从程序员的视角详细阐述计算机系统的本质概念,并展示这些概念如何实实在在地影响应用程序的正确性、性能和实用性。全书共12章,主要内容包括信息的表示和处理、程序的机器级表示、处理... 查看详情

深入理解计算机系统(第二版)----之一

第一部分:程序结构和执行  第1章:计算机系统漫游  第2章:信息的表示和处理  第3章:程序的机器级表示  第4章:处理器体系结构  第5章:优化程序性能  第6章:存储器层次结构第二部分:在系统上运行程序... 查看详情

深入理解计算机系统-读书笔记

第一章:计算机系统漫游编译过程:预处理阶段:预处理器根据字符#开头的命令,修改原始的c程序;编译阶段:编译器将ascii文本文件翻译成汇编语言程序;汇编阶段:汇编器将汇编语言程序翻译成机器指令,分为32位和64位系... 查看详情

深入理解计算机系统(3.1)------汇编语言和机器语言

  《深入理解计算机系统》第三章——程序的机器级表示。作者首先讲解了汇编代码和机器代码的关系,阐述了汇编承上启下的作用;接着从机器语言IA32着手,分别讲述了如何存储数据、如何访问数据、如何完成运算以... 查看详情

读书笔记《深入理解计算机系统》(第三版)概述

  《深入理解计算机系统》第三版刚出来不到一周,便买下了这本书;之所以阅读本书,一方面源于网友推荐以及豆瓣不错的评分、评价;另一方面是针对本人非科班出身,计算机系统相关的知识相对比较薄弱,很多情况下此... 查看详情

深入理解计算机系统(3.1)------汇编语言和机器语言

《深入理解计算机系统》第三章——程序的机器级表示。作者首先讲解了汇编代码和机器代码的关系,阐述了汇编承上启下的作用;接着从机器语言IA32着手,分别讲述了如何存储数据、如何访问数据、如何完成运算以及如何进... 查看详情

深入理解计算机系统之程序的机器级表示部分学习笔记

  不论我们是在用C语言还是用JAVA或是其他的语言编程时,我们会被屏蔽了程序的机器级的实现。机器语言不需要被编译,可以直接被CPU执行,其执行速度十分 快。但是机器语言的读写性与移植性较高级语言低。高级... 查看详情

《深入理解计算机系统》读书笔记

 第一部分概述综述本书从几个生动的计算机程序实例引入,带我们走进了一个庞大的计算机世界。计算机系统的诞生经历了无数个创新性时刻,每一个看似显然的成果都是来之不易的。比如,让计算机用一种我们能够理解的... 查看详情

深入理解spark原理,从性能优化入手

...他job又多了个计算阶段stage,如图中红圈所示。通过阅读程序代码,发现第一个job需要初始化一个空数组,从而产生了一个stage,但是这个stage在性能测试结果上显示,花费了14秒的时间,远远超出合理的 查看详情

3.2《深入理解计算机系统》笔记内存和高速缓存的原理插图

  《深入计算机系统》笔记(一)主要是讲解程序的构成、执行和控制。接下来就是运行了。我跳过了“处理器体系结构”和“优化程序性能”,这两章的笔记继续往后延迟!  《深入计算机系统》的一个很大的用... 查看详情

电子书深入理解计算机系统.pdf

内容简介  和第2版相比,本版内容上*大的变化是,从以IA32和x86-64为基础转变为完全以x86-64为基础。主要更新如下:  基于x86-64,大量地重写代码,首次介绍对处理浮点数据的程序的机器级支持。  处理器体系结构修改为... 查看详情

深入理解计算机系统第三章程序的机器级表示part1

 如题所示,这一章讲解了程序在机器中是怎样表示的,主要讲汇编语言与机器语言。 学习什么,为什么学,以及学了之后有什么用我们不用学习如何创建机器级的代码,但是我们要能够阅读和理解机器级的代码。虽然现... 查看详情