嵌入式Linux——解密 ARM 性能优化:LDR 未命中时,为何 STR 还能“插队”?

问题理解

  • ARM v6/v7 处理器会对以下指令顺序进行优化

    复制代码
    LDR r0, [r1] ;
    STR r2, [r3] ;
  • 假如说第一条LDR指令导致缓存未命中,这样缓存就会填充行,并需要较多的时钟周期才能完成。老的ARM处理器会等待这个动作完成,再执行下一条STR指令。而ARM v6/v7 及以后处理器会识别出下一条指令(STR)且不需要等待第一条指令完成(并不依赖r0的值),即先执行STR指令,而不是等待LDR指令完成。

什么叫缓存未命中

  • 错误理解:为什么还能LDR指令缓存未命中,那没命中岂不是没能从内存拿到数据?
  • "缓存未命中" 不等于 "获取数据失败"
  • "缓存未命中" 等于 "在最快的地方(缓存)没找到,必须去下一级更慢的地方(内存)找"

"缓存未命中"时,CPU 的完整处理流程

  • 这个流程是自动由硬件(CPU 和内存控制器)完成的,你的程序(LDR指令)不需要操心如何去做,只需要等待它完成。
  • 下述流程先抛开多级缓存和指令顺序优化。
  1. 尝试在"办公桌"(L1 Cache)上寻找

    • CPU 拿到地址 [r1]。
    • 它首先在最快的存储------你的**办公桌(L1 缓存)**上,寻找这个地址的数据。
    • 结果:没找到!这就是**"缓存未命中"(Cache Miss)**。
  2. "那怎么办?" ------ CPU 停顿 (Stall)

    • 你的反应(CPU 停顿):你(CPU)不能继续执行下一条指令 STR r2, [r3],因为你必须先完成 LDR。
    • 你"哎呀"一声,停下手中的工作,开始等待。
    • 这就是"需要较多的时钟周期"的开始。 CPU 在原地空转,等待数据送达。
  3. 去"大书柜"(Main Memory / 内存)拿数据

    • 因为办公桌(Cache)上没有,你(的内存控制器)只好站起来,走到**大书柜(主内存 RAM)**那里。
    • 根据地址 [r1],你在书柜中找到了那份文件(数据)。
    • 数据肯定是在"书柜(内存)"里的。(如果连内存里都没有,那就是"缺页异常/Page Fault",那是另一个更慢的故事了,但 LDR 指令本身假定数据在内存中)。
  4. "缓存就会填充行"(Cache Line Fill)

    • 这是最关键的一步!
    • 你(CPU)非常聪明,你不会只把 r0 需要的那 4 个字节(假设是32位ARM)从书柜拿到办公桌。
    • 你猜测:"既然我拿了这份文件,我待会儿很可能也会看它旁边的文件。"(这就是空间局部性原理)
    • 于是,你把包含 [r1] 数据的一整"行"(比如一整个文件夹,技术上称为一个 Cache Line,例如 64 字节)全部从"书柜(内存)"搬运到了"办公桌(缓存)"上。
    • 这个动作就叫做**"缓存填充行"(Cache Line Fill)**。
  5. 完成 LDR 指令

    • 现在,数据已经成功地被你从"书柜(内存)"复制到了"办公桌(缓存)"上。
    • CPU 再从"办公桌(缓存)"上,把 r0 真正需要的那 4 个字节的数据,抓取到"0号抽屉(r0 寄存器)"中。
    • LDR r0, [r1] 指令终于完成。
  6. CPU 恢复执行

    • "等待"结束。
    • CPU 继续执行下一条指令 STR r2, [r3]

什么叫指令顺序优化

  • 简单来说,就是 CPU 不严格按照你写的代码顺序来执行指令,而是在保证程序最终结果正确的前提下,打乱顺序,谁的条件先满足,谁就先执行,以此来"压榨"CPU的性能。

  • 想象一下,你(CPU)早上起床做早餐,你的"指令列表"如下:

    • 烧水(需要10分钟)
    • 烤面包(需要3分钟)
    • 从冰箱拿果酱(需要30秒)
  1. 场景一:老的ARM处理器(按序执行 In-Order Execution)

    • 这种处理器是个"死脑筋",它严格按照指令顺序来:
      • 执行 烧水:打开水壶。
      • 等待:站在原地干等10分钟,直到水烧开。(这就像LDR缓存未命中,CPU在"停顿/Stall")。
      • 执行 烤面包:水烧开后,把面包放进烤面包机。
      • 等待:再等3分钟。
      • 执行 拿果酱:面包烤好后,跑去冰箱拿果酱。
    • 总耗时:10分钟 + 3分钟 + 30秒 = 13.5 分钟。 效率极低!
  2. 场景二:ARM v6/v7 处理器(乱序执行 Out-of-Order Execution)

    • 这种处理器非常聪明,它会"预读"整个指令列表,并分析它们之间的依赖关系:

    • 烤面包 需要依赖 烧水 吗?不需要。

    • 拿果酱 需要依赖 烧水 或 烤面包 吗?也不需要。

    • 于是,它的执行流程变成:

      • 执行 烧水:打开水壶。(这是一个耗时操作,CPU知道它需要等,于是把它丢给"水壶"去处理)。
      • 立即执行 烤面包:在水壶烧水的同时,把面包放进烤面包机。(这就是你例子中的 STR 指令)。
      • 立即执行 拿果酱:在水壶烧水和面包机烤面包的同时,跑去冰箱拿果酱。
      • 等待:现在,所有能"偷跑"的指令都执行了,CPU只需等待最长的那个任务(烧水)完成即可。
    • 总耗时:取决于最长的那个任务 = 10 分钟。 效率极高!

相关推荐
茉莉玫瑰花茶4 小时前
从零搭建 C++ 在线五子棋对战项目:从环境到上线,全流程保姆级教程
开发语言·c++
天外飞雨4 小时前
各传感器消息解析
linux
一匹电信狗4 小时前
【C++】哈希表详解(开放定址法+哈希桶)
服务器·c++·leetcode·小程序·stl·哈希算法·散列表
Larry_Yanan4 小时前
QML学习笔记(五十一)QML与C++交互:数据转换——基本数据类型
c++·笔记·学习
007php0074 小时前
某游戏大厂的常用面试问题解析:Netty 与 NIO
java·数据库·游戏·面试·职场和发展·性能优化·nio
逐风&者4 小时前
CentsOS 7 “Could not resolve host: mirrorlist.centos.org; 未知的错误”问题解决
linux·运维·centos
梵尔纳多4 小时前
ffmpeg 使用滤镜实现播放倍速
c++·qt·ffmpeg
白曦5 小时前
switch语句的使用
c++
超越自己5 小时前
远程连接银河麒麟服务器-xrdp方式
linux·运维·服务器·远程桌面·银河麒麟