GD32H7如何利用超标量流水线

描述

我们拿一个算法的代码实现来举例子,首先我们写一个求阶乘的子函数,这里我偷懒让 ChatGPT 帮忙生成了一个:

#include
// 阶乘函数intfactorial_iterative(int n) { int result = 1; // 从1乘到n for (int i = 1; i <= n; ++i) { result *= i; } return result;}
// 示例int main() { int result_iterative = factorial_iterative(5); printf("5的阶乘是: %dn", result_iterative);
return 0;}

这种简单的迭代算法的优点是比较容易理解,一眼就可以看出程序员想干什么。

但这样写出来的程序缺点也很大,就是运行效率非常低,我们在算法编写中最怕的就是for 循环,因为这里面会存在大量的比较和跳转,同时最容易产生一些代码被无效的循环执行。

处理器

这些缺点有的会被编译器的优化措施给规避掉,比如编译器可以把一些需要内存访问的变量先放到寄存器中,等计算完结果后,再把结果从寄存器中转移到内存中,因为 CPU 读取寄存器比读取内存可快多了。

但是编译器也不是万能的,有些优化他就做不到。比如,我们改成下面展开的样子,超标量的流水线就开始起作用了。

// 阶乘函数intfactorial_iterative(int n) {    int result0 = 1, result1 = 1, result2 = 1,result3 = 1;    // 从1乘到n    for (int i = 1; i < n; i += 4) {
result0 *= i; result1 *= i + 1; result2 *= i + 2; result3 *= i + 3;
} return (result0 * result1 * result2 * result3);}


首先,我们假设开启了编译器优化,编译器已经把所有内存访问的变量在函数开始都归置到了寄存器中,那么这时候我们可以看到,4 个 result 的乘法语句是相互独立的,他们的计算过程不依赖于其他 3 个语句的计算结果。

这就好比安排了四个人,给他们算 4 个单独的式子,假设他们计算能力相同,于是他们会在同一段时间后跑到黑板上来互相乘一下算个总的结果。

而如果我们只是简单的做循环展开,不增加新的寄存器变量,也就是不加人的情况下是怎么样的呢?

// 阶乘函数intfactorial_iterative(int n) {    int result = 1;    // 从1乘到n    for (int i = 1; i < n; i += 4) {
result *= i; result *= i + 1; result *= i + 2; result *= i + 3;
} return (result * result * result * result);}


这里只放了一个聪明的孩子做算式,不过你看他要做的 4 个算式,其中后一个算式总要用到前一个算式的结果,他即便再聪明也得一个一个的算。

这就是超标量流水线的用处,当然展开多少还需要我们自己衡量,本质上也是用空间换时间,另外寄存器可是稀缺资源。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分