算法设计新思路
算法设计,我们一般都从理论上去考虑一个算法,即怎么做才能使得时空复杂度最优。但是在实际情况下把一个算法完美的实现是一件不容易的事情。所以搞理论的人千万不要把实现算法看成很轻松的事情,因为你的算法可能根本在实际中没有一个有效的实现方法,而且即使能够很容易的实现,可是由于实现的人不了解计算机的结构而使得算法运行起来并不是令人满意。
例如如下的程序:
01 #include <stdio.h>
02 #include <time.h>
03 #define N 10000
04 #define M 10000
05
06 int sumrow(int (*a)[M], int n, int m)
07 {
08 int sum = 0, i, j;
09 for(i = 0; i < n; ++i)
10 for(j = 0; j < m; ++j)
11 sum += a[i][j];
02 #include <time.h>
03 #define N 10000
04 #define M 10000
05
06 int sumrow(int (*a)[M], int n, int m)
07 {
08 int sum = 0, i, j;
09 for(i = 0; i < n; ++i)
10 for(j = 0; j < m; ++j)
11 sum += a[i][j];
return sum;
12 }
13
14 int sumcol(int (*a)[M], int n, int m)
15 {
16 int sum = 0, i, j;
17 for(i = 0; i < m; ++i)
18 for(j = 0; j < n; ++j)
19 sum += a[j][i];
12 }
13
14 int sumcol(int (*a)[M], int n, int m)
15 {
16 int sum = 0, i, j;
17 for(i = 0; i < m; ++i)
18 for(j = 0; j < n; ++j)
19 sum += a[j][i];
return sum;
20 }
21
22 int a[N][M];
23
24 int main()
25 {
26 int pre, nex, sum, i, j;
27
28 for(i = 0; i < N; ++i)
29 for(j = 0; j < M; ++j)
30 a[i][j] = 1;
31
32 pre = clock();
33
34 //sum = sumrow(a, N, M);
35
36 sum = sumcol(a, N, M);
37
38 nex = clock();
39
40 printf("%dms\n", nex - pre); //时间统计
41
42 getchar();
43
44 return 0;
45 }
20 }
21
22 int a[N][M];
23
24 int main()
25 {
26 int pre, nex, sum, i, j;
27
28 for(i = 0; i < N; ++i)
29 for(j = 0; j < M; ++j)
30 a[i][j] = 1;
31
32 pre = clock();
33
34 //sum = sumrow(a, N, M);
35
36 sum = sumcol(a, N, M);
37
38 nex = clock();
39
40 printf("%dms\n", nex - pre); //时间统计
41
42 getchar();
43
44 return 0;
45 }
我的电脑配置: windows xp, AMD Turion 64 X2, 1.9GHz, 1.00G内存
sumrow约为800ms,而sumcol为11300ms,相差大约13倍。大家可以想想为什么?
我来说说。 这和计算机的存储结构有关。为了存储器能够跟得上cpu的处理速度,特意在cpu和内存之间加入了高速缓存。高速缓存的存取速度可以认为接近cpu的频率,所以数据如果放到这个里面那么我们的程序就快,否则就会慢几倍甚至10几倍。但是这个高速缓存的容量有限,所以计算机推测应该把什么放入里面才能提高命中率。在我们的这个程序中可以发现,计算机把访问单元后一小部分连续的单元认为是很有可能访问的单元,并把其放入告诉缓存,而C中二维数组是按行存储的。设高速缓存大小1M,一次能放入262144个整数,10^8个数据按行访问共出现381次不命中。那么按列访问呢,262144个元素只能覆盖每一列的26行,故访问每一列的时候,每隔27个元素就有一次不命中,那么10000行共有371次不命中,所以最差情况下访问这10^8次方个元素共不命中3710000次。当然计算不准确,但是足以说明问题:sumcol没有好的空间局部性,而且效率相差10几倍。所以有的时候优化一下代码实现可能比从理论上来优化算法更有效率。 而如果你要达到这种境界,你就不得不去摸摸计算机这位“小姐”的脾气。恰恰好玩的是搞理论的绝对不愿意去看计算机结构,而搞系统开发的基本不去搞算法。所以很难能把一个算法实现的尽善尽美。
在这里,我只想说,在优化程序效率的时候,如果理论上想不出好的办法,那么不妨就从计算机体系结构这方面去改进,毕竟你的程序是在计算机上运行的。