RISC-V 不定长的指令周期

现代CPU由于存在多级流水线,对于分支处理又存在分支预测功能,因此在实际cpu的运行过程中,beq指令的周期不是固定的。
另外,即使对于其他指令,比如lw指令,其指令周期也不是固定的。取决于该指令跟后面的指令有没有依赖关系。RISC-V 不定长的指令周期_第1张图片 使用2周期的那些lw指令的bne指令都是需要用到前一条lw指令的结果,所以必须等前一条完成之后才能执行。比如 lw a0,1540(t1)与bne a0,x0,-1,bne指令必须等 a0 的 load 结果出来之后才能开始执行。lw 本身都是4周期的,如果前后没有依赖关系就可以pipeline起来,看起来就是一个周期完成一样。比如图中前面那些1个周期的lw指令都是没有依赖关系的。另外,lw 周期数跟具体实现有关系,最少2周期,如果存储接口插入了等待周期会更长,具体到某个指令多少得分析微架构。所以说,并不能直接从代码段知道一个函数的实际运行时间,需要实际运行看看。
执行一个小程序进行测试(测试cpu为8级流水线),从log中获知一共使用了31个不同的指令。指令和指令次数为:
RISC-V 不定长的指令周期_第2张图片

1, 跳转和返回指令

jal指令:

即使没有依赖关系,也是最少5周期:本测试一共有2983个jal指令,其指令周期和次数有:
RISC-V 不定长的指令周期_第3张图片
可以看出,jalr指令的大部分的指令周期是5。

jalr指令:

jalr指令一共有2279个jalr指令,其指令周期和次数有:
RISC-V 不定长的指令周期_第4张图片
可以看出,jalr指令的大部分的指令周期是5。

mret指令

mret指令,一共79个:
RISC-V 不定长的指令周期_第5张图片
大部分为9个周期。

2, add和addi指令

add指令,一共4129个,1个周期和4个周期大约各占一半。
addi指令,一共22708个,大部分是1个周期。
伪代码中的li指令,会转换为addi指令来执行。比如li a5,4指令,会转换为addr a5,x0,4。

3, load和store指令

RISC-V 不定长的指令周期_第6张图片

lw指令

lw指令,一共24338个,1个周期和2个周期的大约各占一半。
RISC-V 不定长的指令周期_第7张图片

lbu指令

lbu指令,一共1572个:
在这里插入图片描述
可以看出,lbu指令的时间周期并没有比lw指令少。

sw指令

sw指令,一共13636个,大部分需要1个时钟周期。
RISC-V 不定长的指令周期_第8张图片

sb和sh指令

sb和sh指令与sw指令类似,大部分需要1个时钟周期。

4,分支预测指令

RISC-V 不定长的指令周期_第9张图片

bne指令

bne指令,一共5389个:
RISC-V 不定长的指令周期_第10张图片
66%是7个周期。
有1次15 cycle是因为后面来了一个中断,需要跳转到中断服务函数。
有11次用了13个cycle, 主要就是分支开销, 没有指令预测, 结果遇到分支跳转,开销就大;一般跳转后面的时间长, 很大可能都是分支跳转的开销。开销最大的情况是跳转后 又遇到 cache miss; 计算这个开销是 从取指令到提交的流水线级数+cache miss的取指时间。
总结:所以我们在写代码的时候,最好把运行概率大的那个分支写到if中。

bge,begu,blt指令

这三个指令大部分都是6个周期。可以看到,分支跳转指令是比较耗时的。

5, 与状态寄存器有关的指令

csrrs,csrrw,csrrwi,这些指令在实际测试中一般耗费4个时钟周期。

6,nop指令

nop指令,一共1060个:
RISC-V 不定长的指令周期_第11张图片

7,其他指令

大部分都是1个时钟周期。

你可能感兴趣的:(C,语言)