当前位置: 首页 > 知识库问答 >
问题:

为什么LDR有时需要20个CPU周期?

白修谨
2023-03-14

我对ARM Cortex M4组件中的LDR和STR指令有疑问。出于某种原因,它们在内存中写入/读取某些部分比读取其他部分需要更长的时间。

为了说明这一点,我设置了一个简单的示例:

我已经创建了一个项目,它有一个主C文件和一个相邻的。包含汇编代码的文件。我已经使用“extern”对象将汇编函数包含到我的C文件中。

//Add the asm functions to our C code
extern "C" void LoadTest(uint32_t *memory_adress);
extern "C" void LoadTestLoop(uint32_t *memory_adress);

以下是该程序的作用:

void perform_test()
{
  //Time
  register uint32_t register_before_time=before_time;
  register uint32_t register_after_time=after_time;

  register uint32_t* input_address=0x400E9000;
  
  register_before_time=ARM_DWT_CYCCNT; 

  //Time measurment occurs in here!
  LoadTestLoop(input_address);
  
  register_after_time=ARM_DWT_CYCCNT;

  Serial.print(" Time: ");
  Serial.println(register_after_time-register_before_time-time_error);
}

它向我们展示了在“register_before_time=ARM_DWT_CYCCNT”和“register_after_time=ARM_DWT_CYCCNT;”行之间执行某些操作所花费的时间。

以下是我们将测试其速度的汇编子程序:

.global LoadTest
LoadTest:
    ldr r1, [r0]                        /*Load value into r1 from memory_address*/
    orr r1, #0xC0                       /*OR bits 7,6 to be on.*/
    str r1, [r0]                        /*Store the changed value back into memory_address*/
    bx lr
.global LoadTestLoop
LoadTestLoop:
    mov r2, #255                        /* Set r2 to be 255 for the loop*/
    
    TestLoop:                           /*Same code as before*/
        ldr r1, [r0]                        
        orr r1, #0xC0                   
        str r1, [r0]
        
        subs r2, r2, #1                 /*Decrement r2 + set Z flag if it's zero*/
        bne TestLoop                    /*Repeat until r2==0*/
    bx lr

LoadTest–从我们提供的地址加载一个值。将值与0xC0进行OR,然后将其存储回同一地址。

LoadTestLoop–做同样的事情,但是,在一个循环中做255次,这样我们可以获得一个循环迭代所需的平均时间,并最小化从函数进出的分支指令产生的时间测量误差。

注: 为了最大限度地减少测量误差,在input_address指针中,将向时区以外的两个函数提供要使用的地址。

register uint32_t* input_address=0x400E9000;

测试结果和问题:

我对两个正常的C变量进行了这两个测试

uint32_t test_value=255;
register uint32_t* input_address=&test_value;

以及微控制器内部的配置寄存器。请注意,在数据表中,它们仅显示为内存。

register uint32_t* input_address=0x400E9000;

标准变量的平均 LoadTest 需要 9 个周期来执行,但在控制寄存器的 27 个周期下要长得多。LoadTestLoop 测试通过标准变量平均占用 1541 个周期(每次迭代 6 个周期)来强化这一点,而控件记录了惊人的 12227 个周期,每次迭代可得出疯狂的 47 个周期!

为什么会发生这种情况?

有人知道为什么会这样吗?这个问题困扰我很久了,很想知道。

谢谢你的帮助。

共有1个答案

闻人越
2023-03-14

这是完全正常的。

一般来说,从内存加载需要多长时间就需要多长时间。时间不受CPU本身的控制,所以引用的周期计数只能代表“最佳情况”。如果CPU不能从自己的内部结构(例如存储缓冲区或L1缓存)完成负载,那么它只需要将请求放在内存总线上并停止,直到内存子系统做出响应。(或者继续乱序执行后面的指令,如果有的话,如果它能找到一些不依赖于负载结果的指令。)

实际花费的时间可能变化很大,例如取决于加载是命中还是未命中L2或L3高速缓存、另一个内核或外部设备是否持有总线锁等。如果机器没有缓存,所有内存都是快速SRAM,那么时间可能会相当稳定。

但在您的情况下,您正在加载的地址实际上映射到硬件设备。所以,你根本不是在读RAM,而是在做I/O。在这种情况下,响应必须来自设备本身,并且设备基本上可以需要多长时间。如果您需要能够预测时间,那么您需要查看该设备的文档(以及其间的任何接口硬件),而不是CPU手册中的周期计数。

 类似资料:
  • 问题内容: 我对JAX-WS进行了概述,并注意到了(和)的一些引用。 在什么情况下需要?(我认为JSR 109服务器?!) 问题答案: 是使用SUN的参考实现将Web服务作为标准存档部署在非Java EE5 Servlet容器上时所需的专有部署描述符。 Sun的RI 用作servlet上下文事件的侦听器和调度程序servlet。两者都必须在中声明。然后需要该文件为定义Web服务端点,以使其知道必须

  • 互联网是超文本标记语言(HTML)页面的集合,它们彼此链接以形成概念性信息网络。随着时间的推移,静态资源数量增加,图像等更丰富的项目开始成为Web结构的一部分。 高级服务器技术允许动态服务器页面 - 其内容基于查询生成的页面。 很快,需要拥有更多动态网页才能获得动态超文本标记语言(DHTML)。一切都归功于JavaScript。在接下来的几年中,我们看到了跨帧通信,试图避免页面重新加载,然后在帧内

  • 当前信息时代,哪里都是应用程序。这些应用程序们不仅仅是运行人们工作场所的工具 - 它们现在正在经营人们的生活。 对即时响应的需求,完美的行为和更多的功能是前所未有的。 而且,当然,人们期望应用程序在不同类型的设备上运行平稳,特别是在移动设备上。 应用程序执行的速度与它所做的一样重要。 NGINX的核心功能,例如其具有高性能HTTP和反向代理服务器的大规模可扩展事件驱动架构,访问和带宽控制以及与各种

  • 开发人员和运营工程师是两个不同的组织团队,如果发现这两个团队在错误的轨道上协作,则表明需要DevOps。以下是两个团队经常出现的一些问题: 在DevOps之前,开发和运营团队完全孤立。 测试和部署是在设计构建之后完成的独立活动。因此,他们比实际构建周期消耗更多时间。 在不使用DevOps的情况下,团队成员将大量时间花在测试,部署和设计上,而不是构建项目。 手动代码部署会导致生产中出现人为错误 编码

  • 问题内容: 我知道单例类是只能有一个实例化的类,但是我不明白为什么这样做会有用。为什么不只创建带有静态变量和方法的类,并在需要时使用sync来确保没有两个线程同时执行该类中的方法。我只是不明白为什么有人会遇到创建此类课程的麻烦。我知道我在这里想念什么。 谢谢, 问题答案: 当我同意其他答案时,OP询问为什么不使用具有所有静态方法的类(可能具有静态字段),而不是拥有一个实例的单例。 为什么使用单身人