变量与寄存器程序员
未指定优化选项时,编译器会将程序中的变量储存到内存上,程序在执行相关运算时会根据须要装载到寄存器,当运算结束后再将运算器上的内容移到内存,而后生成目标代码。算法
但若是指定了优化选项,变量就会尽量地储存在寄存器上,从而减小访存时间。数据库
===============express
变量间的乘法运算编程
和加法运算相同,CPU内部有多个乘法器,它们是为了同时计算乘法运算而存在的。数组
变量与常量的乘法缓存
当编译器进行常量乘法计算时,尽量不使用CPU内的乘法计算器,而使用移位结合加法运算。性能优化
···················网络
当咱们作“2的乘方+1”(即3,5,9等)的乘法运算时,采用CPU中具有的lea(load effective address)指令,也能下降执行成本。好比:函数
将变量与常量5相乘,至关于将寄存器内的值*4+寄存器的值。 注意编译器在进行计算时可能利用cpu内部全部的计算功能来计算,原本LEA是用于计算地址值的,但这里却用于简化乘法计算。
负数的乘法经过移位运算也能够代替乘法运算。
=================
除法操做是比乘法更为耗时的运算。
与乘法运算相同,除以2的乘方时,能够经过寄存器内数据的移位操做来提升效率。可是对于负数的除法运算还必须另外考虑????
除数不是2的乘方的除法计算
当除数不是2的乘方时,编译器也可能会进行优化。可是相关计算比较繁琐,对于有符号数的要转化为无符号数进行计算。
利用减法和移位求除数是5的除法运算。
A/5能够转化为先计算A=A>>2; 因为除4带来看“偏差”miss=A
单核CPU中一些运算也多是并行的,好比浮点数乘法运算。
尽管许多程序仍是依赖单线程的执行,现代处理器在单核中也提供了很多的并行性。例如:单个CPU能够同时执行4个浮点数乘,等待4个内存请求并执行一个分支预判。
避免或减小使用本地变量。
本地变量一般都存储在栈上。不过若是数量比较少,它们能够存储在CPU寄存器中。在这种状况下,函数不但获得了更快访问存储在寄存器中的数据的好处,也避免了初始化一个栈
帧的开销。
编译器、中间件篇
操做系统和编译器的基础软件,数据库软件、网络协议栈,扩充文件系统这些软件叫作中间件。它们舍弃了复杂且原始的计算机操做,将程序员要解决的问题集中起来处理。
输入输出操做十分耗时,若是能够将屡次输入输出操做集中起来归并处理。
有效利用输入输出缓冲区很是重要。
将strcpy函数替换为memcpy函数可以提升效率。
算法
结合数据来调整算法。
4.2 硬件篇--数组和缓存 的有效利用
若存取的数据在内存中是连续的,缓存的做用就能获得更好发挥。但若是数据在内存中不是连续的,就会发生缓存未命中,从而增长存取时间。
下面经过一个例子来讲明如何安排循环来提升缓存利用效率。
矩阵乘法
Cij=sigma(Aik*Bkj)
若是直接将这个式翻译为程序的话,结果以下:
for (int i = 0; i < 2; i++){
for (int k = 0; k < 2; k++){
for (int j = 0; j < 3; j++){
//设计循环时,注意到对数组b,当内层循环为对j的循环时,b[k][j]是连续的,
//同时在内层环中a[i][k]是一个固定值。并且调换顺序后,对结果并不产生影响。但计算速度会加快。但这只是理论上的分析,实际上,并无发现快了。反而是慢了。
res[i][j] += a[i][k] * b[k][j];
//有时候为了提升速度能够考虑展开内层循环。
}
}
}
4.3 库函数篇
说到字符串比较,你们通常用的是strcmp函数,它是经过对字符串逐个字符进行比较来实现的,但若是要对比的字符串比较长的话,则执行时间也会比较长,这是个难点。
对于须要处理较长的字符串的状况,或字符串的问题比较大的状况,能够考虑将strcmp函数替换为memcmp函数以实现高效编程。
分析:strcmp是一个字符一个字符进行比较,而memcmp函数将须要比较的字符串分割成4个字节或8个字节一组的词为单位进行对比操做,这样就节约了时间。但请注意:这只在要比较的字符串的长度比较大时有效。若是字符串长度比较小,可能不如strcmp.
4.5 对比各类输入输出函数
行输入函数的比较
【C语言】-->语法 fgets函数原理初探 - 省 - 博客频道 - CSDN.NET
http://blog.csdn.net/chenglibin1988/article/details/8738070fgets函数在运行过程当中使用到了两个缓冲区,一个是stdio缓冲区,一个是用于存放用户输入的缓冲区。系统调用read把数据读入缓冲区stdiok ,fgets函数从stdio缓冲区中读取一行,或规定数量的内容到本身函数参数中规定的缓冲区。
缓冲区stdio的标准容量是4kb,可是可使用setvbuf来扩容。
fgets函数原型为
char *fgets(char *s, int n, FILE *stream);从stream(一般是stdin)所指的文件读入字符到s所指的内存空间中,直到读到换行符、文件尾或n-1个字符为止,最后会加上NULL 做为字符串结束,即s[n-1] = NULL;若是在未读到n - 1个字符时,读到了换行符或者 文件结束标志(EOF),那么 就将换行符或者文件结束标志(EOF)都读到s中,此时,再在换行符或是文件结束标志(EOF)后面添加 NULL。 由于这个函数会将换行读入,因此通常不使用。仍是用gets_s函数。
对于须要进行海量数据的输入输出的程序,须要对输入输出环节进行优化。所以通常,不要使用getline函数来读取数据,而是在自定义大小的输入输出函数中定义4MB的输入缓冲区,而后将数据直接读入缓冲区,须要时再取出一行的数据就能够了。
另外,本来咱们在用字段分解输入行的过程当中使用的是strtok函数,若是能将strspn函数和strcspn函数结合起来使用,效率将会获得提高。
C语言中strspn()函数和strcspn()函数的对比使用_C 语言_脚本之家
http://www.jb51.net/article/71441.htm性能优化上须要有总体的成本意识,尽可能避免太依赖于运行环境或安装等固定成本,让其余人看到代码后也能尝试运行一下。
公共子表达式的消除(CSE: common sub-expression elimination)是编译器的优化方法之一,若是值相同的表达式在程序中出现屡次,则只须要计算一次的一种运算方法。
》》若是一个函数不须要返回值,必定不要定义相应的返回值。
统计带小数部分的数
因为浮点数的计算相比整数类型计算要慢 ,并且浮点数与字符串之间的转换也很耗时,因此在须要大量进行浮点数计算时,能够将其转化为整数运算。
方案一:对全部数据规定一个最多小数位N,全部输入的数据在系统内部都保存成实际数据*10^N,这样能够将小数运算转化为整数运算。
方案二:若是对浮点数须要很高的精度,64位整数中能处理的十进制数最多19位,若超过这个位数,能够将小数和整数部分分开存放在两个64位整数中。例如:
23.333 在整数部分,存入23;小数部分存入333,固然也能够对小数部分进行扩大后存储。
60.1 整数部分60,小数部分100。
注意因为小数部分和整数部分进行了分开存放,因此在进行加法减法运算时,相应的进位和借位须要本身处理。因此虽然64位整数在十进制中能够有19位数,但在程序中的小数部分只能存放18位,最高位被看成进位和借位标志。
整数转换成字符串
sprintf函数能够将整数输出到字符串中。
断定字符的字节数
处理UTF-8字符串的程序必须先断定输入字符的字节数。UTF-8的字节数根据UNICODE的值定义为1-6个字节不等。但在2006年的iso中规定,不能对U+200000以上的字符位置进行分配,因此实际上UTF-8的字节数最大只能到4字节。
对于汉字而言,少数汉字占用3个字节,大多数汉字占用4个字节。
半角/全角字符和UNICODE
所谓半角字符和全角字符其实并无标准定义,它是PC在性能低下时,限制字符表示为等宽字体(fixed width font)的术语。
对于一些特定的问题可使用表格来减小计算量,或用来减小条件分支。
在UTF-8中ascii码的编码与原来一致,使用U+0000~U+007F.