我们拿一个算法的代码实现来举例子,首先我们写一个求阶乘的子函数,这里我偷懒让 ChatGPT 帮忙生成了一个:
// 阶乘函数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 个算式,其中后一个算式总要用到前一个算式的结果,他即便再聪明也得一个一个的算。
这就是超标量流水线的用处,当然展开多少还需要我们自己衡量,本质上也是用空间换时间,另外寄存器可是稀缺资源。
-
处理器
+关注
关注
68文章
20154浏览量
247376 -
mcu
+关注
关注
147文章
18635浏览量
387635 -
代码
+关注
关注
30文章
4942浏览量
73160 -
编译器
+关注
关注
1文章
1670浏览量
51088
发布评论请先 登录
什么是超标量处理器的流水线?超标量处理器的特点有哪些?
FPGA中的流水线设计
现代RISC中的流水线技术
什么是超标量技术/FADD?
流水线中的相关培训教程[4]
FPGA之流水线练习(3):设计思路
如何选择合适的LED生产流水线输送方式
嵌入式_流水线
什么是流水线 Jenkins的流水线详解
首款Cortex-M7内核超高性能MCU性能揭秘
H7的特点 什么是超标量流水线

GD32H7如何利用超标量流水线
评论