没有看过或者没有看到这里的小伙伴们,看到这个标题必定以为摸不着头脑。那这里就先来解释一下背景。函数
double poly(double a[], double x, long degree) { long i; double result = a[0]; double xpwr = x; for (i = 1; i <= degree; i++) { result += a[i] * xpwr; xpwr = x * xpwr; } return result; }
double polyh(double a[], double x, long degree) { long i; double result = a[degree]; for (i = degree; i >= 0; i--) { result = a[i] + x * result; } return result; }
这是 CSAPP 的两道题,每一题是一段代码,这两段代码实现了同一个功能。这两道题有一个共同的问题,比较这两段代码的性能。性能
这里的答案是,poly 的性能比 polyh 的性能要高。poly 的 CPE 是 5,而 polyh 的 CPE 是 8。code
这就显得很尴尬了,我原觉得两个函数的 CPE 都是 8。class
polyh 的 CPE 是 8 我没有疑问,由于这个循环里的操做是没法并行的,也就是下一次迭代会依赖上一次迭代产生的结果。因此,CPE = 5 + 3,5 是浮点数乘法的延迟下届,3 是浮点数加法的延迟下界。效率
poly 的 CPE 我本来认为也是 8,两个乘法是能够并行的,可是这个加法的是依赖于第一个乘法的值,没法并行,因此 CPE = 5 + 3 = 8。循环
上面的是个人猜测,因此我认为这里的答案是它们的 CPE 是相同的,性能也是相同的。可是如前面所写,答案并非这样的。因而,我把以前看的东西都翻出来想了一下,真的不是这样的。并行
现代 CPU 是有一个流水线的概念的。什么是流水线呢,想象一下汽车车间,咱们造一辆汽车,是分红了不少道工序的,好比装配发动机、装车门、轮子等等。现代 CPU 也是相似的,咱们看到的一条指令,在执行的时候,经历了一长串的流水线,致使了指令真正的执行顺序和咱们看到的多是不同的,可是因为现代出来的这种机制,能够确保最后的结果是和咱们看到的是同样的。总结
poly 函数,在执行的时候,因为有两个浮点数乘法单元,因此 a[i] * xpwr
和 xpwr = x * xpwr
能够并行执行。而 a[i] * xpwr
能够经过流水线的数据转移,让这个加法 result + a[i] * xpwr
能够在下一次迭代的时候执行,由于每次迭代的时候,两个乘法都不会依赖 result
这个结果。这样,加法和乘法能够并行执行。浮点乘法的延迟下界是 5,浮点加法的延迟下界是 3,因此浮点乘法是关键路径,CPE 也天然就是 5 了。数据
再来看看 polyh 函数。这个函数的循环里只有一个浮点乘法运算和一个浮点加法运算。先来看看浮点乘法运算,x * result
,很显然,每一次乘法都须要依赖上一次迭代的结果,致使了加法没法和乘法并行执行。因而,CPE 就成了 5 + 3 = 8 了。计算机
这个例子,我以为颇有趣,由于它涉及到了一个流水线的细节。同时,也说明了,并非操做少的代码,效率就高。
本文为做者本身读书总结的文章,因为做者的水平限制,不免会有错误,欢迎你们指正,感激涕零。
《深刻理解计算机系统(第 3 版)》第 四、5 章