系列文章:
操作系统详解(1)——操作系统的作用
操作系统详解(2)——异常处理(Exception)
操作系统详解(3)——进程、并发和并行
操作系统详解(4)——进程控制(fork, waitpid, sleep, execve)
操作系统详解(5)——信号(Signal)
文章目录
如何优化一个程序的运行速度?可以从以下几个方面着手:
- 算法
- 数据结构
- 执行的步骤
- 循环
本文将主要从计算机系统底层方面,探讨如何降低运行时间。
一些概念
CPE
即Cycles Per Element, 运行每一个操作需要的时钟周期
T = CPE*n + Overhead
n: 操作数量
overhead: 其它操作时延
初步优化
以下是一个粗糙的c程序代码:
void combine1(vec_ptr v, data_t *dest)
{
long i;
*dest = IDENT;
for (i = 0; i < vec_length(v); i++) {
data_t val;
get_vec_element(v, i, &val);
*dest = *dest OP val;
}
}
/*
v: 一个向量
dest: 存储执行结果
OP: 运算符, 可以是+, *等
vec_length: 返回向量长度
get_vec_element: 范围下标为i的元素, 存放在val中
IDENT: 单位元, 加法就是0, 乘法就是1
*/
CPE如下:(Element为执行一次循环)
可见gcc自带的优化也能对性能起到很大影响.本例中是O1优化.
消除不必要的函数调用
void combine2(vec_ptr v, data_t *dest)
{
long i;
long length = vec_length(v);
*dest = IDENT;
for (i = 0; i < length; i++) {
data_t val;
get_vec_element(v, i, &val);
*dest = *dest OP val;
}
}
由于v的长度是定长的, 不会被循环改变, 所以可以在循环前面先得到, 这样就不用每次循环都调用一次了.
在本例中优化幅度很小.但是由于length()的时间复杂度是O(n), 当v的长度很大的时候, 循环执行n次, 时间复杂度为O(n^2), 增长速度远远大于O(n)
消除不必要的内存引用
假设v结构体中存储数据的部分是一个数组, 我们用get_vec_start
函数能够获取v的指向数组开头的指针, 那么可以得到一下代码:
void combine3(vec_ptr v, data_t *dest)
{
long i;
long length = vec_length(v);
data_t *data = get_vec_start