更快的处理器/时钟可以执行更多代码吗?


9

我正在编写一个程序,以在以16Mhz运行的ATmega 328上运行(如果您知道的话,它是Arduino Duemilanove,它是AVR芯片)。

我有一个中断进程每100微秒运行一次。我想说,不可能计算出可以在100微秒的一个循环中执行多少“代码”(我正在用C编写,想必它会转换为汇编然后转换为二进制图像?)。

同样,这将取决于代码的复杂性(例如,一根巨大的衬板的运行速度可能比几条短线慢)。

我的理解是正确的,因为我的时钟速率为16Mhz的处理器每秒执行1600万个周期(这意味着每微秒16个周期为16,000,000 / 1,000 / 1,000);因此,如果我想在100微秒的循环中做更多的事情,那么购买更快的模型(例如72Mhz版本)会给我每微秒72个周期(72,000,000 / 1,000 / 1,000)?

目前,它的运行速度有点太慢,也就是说,它花费了不到100微秒的时间来完成循环(确切地说很难说多长时间,但是逐渐落后了),我希望它做得更多。这是获得更快芯片的理智方法,还是我发疯了?


.... ATmega328不是ARM芯片。这是一个AVR。
vicatcu 2011年

干杯,纠正!
jwbensley 2011年

Answers:


9

通常,设备每秒可执行的汇编指令数量取决于指令混合以及每种指令类型需要执行多少个周期(CPI)。从理论上讲,您可以通过查看反汇编的asm文件并查看您关注的功能,对其中的所有不同类型的指令进行计数,并从数据表中查找目标处理器的周期数来对代码进行计数。

在更复杂的处理器中,确定每秒有效指令数的问题由于以下事实而更加恶化:处理器是流水线的,并且具有缓存,而没有缓存。对于像ATMega328这样的简单设备而言,情况并非如此,它是飞行处理器中的一条指令。

至于实际问题,对于像AVR这样的简单设备,我的回答或多或少是“是”。时钟速度加倍应该是任何给定函数执行时间的一半。但是,对于AVR,它们的运行速度不会超过20MHz,因此您只能将Arduino“超频”另外4MHz。

这个建议并没有推广到具有更先进功能的处理器。实际上,将Intel处理器上的时钟速度提高一倍,实际上不会使它每秒执行的指令数量增加一倍(由于分支错误预测,高速缓存未命中等)。


嗨,谢谢您的翔实回答!我已经看过其中之一(coolcomponents.co.uk/catalog/product_info.php?products_id=808),您说AVR的运行速度不能超过20Mhz,为什么呢?上面板上的芯片(uk.farnell.com/stmicroelectronics/stm32f103rbt6/…)是72Mhz的ARM,我是否可以期望以上面所述的方式从中获得合理的性能提升?
jwbensley 2011年

2
将处理速度提高一倍可能不会增加指令吞吐量,因为您可能开始超过可以从闪存中提取指令的速度。在这一点上,您开始点击“ Flash等待状态”,在CPU等待指令从闪存到达时,CPU暂停。一些微控制器通过允许您从RAM执行比FLASH快得多的代码来解决这个问题。
Majenko

@Majenko:有趣的是,我们都在同一时间提出了相同的观点。
詹森·S

发生了……您的事比我的要好:)
Majenko 2011年

1
好的,我已将Vicatcu的答案标记为“答案”。对于我最初与表现有关的速度问题,我认为这是最合适的,尽管所有答案都很棒,而且我真的很满足于每个人的答案。他们向我展示了这门学科比我最初认识到的要广泛,因此,他们都教给我很多东西,并给了我很多研究机会,所以谢谢大家:D
jwbensley 2011年

8

@vicatcu的答案很全面。另外要注意的一点是,在访问I / O(包括程序和数据存储器)时,CPU可能会进入等待状态(停顿的CPU周期)。

例如,我们使用的是TI F28335 DSP。RAM的某些区域处于程序和数据存储器的0等待状态,因此在RAM中执行代码时,每条指令以1个周期运行(那些占用1个以上周期的指令除外)。但是,当您从FLASH存储器(或多或少内置EEPROM)执行代码时,它无法在150MHz的全频率下运行,并且速度慢了好几倍。


关于高速中断代码,您必须学习很多东西。

首先,非常熟悉您的编译器。如果编译器做得很好,那么在大多数情况下,它应该不会比手工编码的汇编程序慢很多。(“慢得多”:我可以接受2倍;不接受10倍)。您需要学习如何(以及何时)使用编译器优化标志,并且不时查看在编译器的输出中查看其工作方式。

您可以让编译器执行其他一些操作以加速代码:

  • 对于小型函数和仅执行一次或两次的函数,请使用内联函数(不记得C是否支持C还是仅是C ++形式)。缺点是内联函数很难调试,尤其是在打开编译器优化的情况下。但是它们为您节省了不必要的调用/返回序列,尤其是在“功能”抽象是出于概念设计目的而不是代码实现的情况下。

  • 查看您的编译器手册,看看它是否具有内在函数-这些是与编译器相关的内置函数,它们直接映射到处理器的汇编指令;一些处理器具有汇编指令,这些指令可以执行诸如最小/最大/位反转之类的有用操作,您可以节省时间。

  • 如果要进行数值计算,请确保没有不必要地调用数学库函数。我们有一种情况,代码类似于y = (y+1) % 4周期为4的计数器,期望编译器将模4实现为按位与。相反,它称为数学库。因此,我们替换y = (y+1) & 3为做我们想要的。

  • 熟悉混乱的hacks页面。我保证您会经常使用其中至少一种。

您还应该使用CPU的定时器外设来测量代码执行时间-它们中的大多数都具有可以设置为以CPU时钟频率运行的定时器/计数器。在关键代码的开头和结尾处捕获计数器的副本,您可以看到它花费了多长时间。如果您不能这样做,另一种选择是在代码的开头降低输出引脚,然后在代码的末尾提高输出引脚,然后在示波器上查看该输出以计时执行时间。每种方法都需要权衡:内部计时器/计数器更灵活(您可以为几件事计时),但更难获得信息,而设置/清除输出引脚在示波器上立即可见,并且您可以捕获统计信息,但是很难区分多个事件。

最后,经验带来了一项非常重要的技能-常规的和特定的处理器/编译器组合:知道何时以及何时不进行优化。通常,答案是不要优化。Donald Knuth的报价经常在StackOverflow上发布(通常只是最后一部分):

我们应该忘记效率低下的问题,大约有97%的时间是这样:过早的优化是万恶之源

但是您处在必须知道必须进行某种优化的情况下,因此该该硬着头皮进行优化了(或获得更快的处理器,或两者兼而有之)。千万不要写你的整个ISR组装。这几乎是可以保证的灾难-如果您这样做,那么在几个月甚至几周内,您会忘记所做的部分工作以及原因,并且代码可能非常脆弱且难以更改。有可能是你的代码的部分,但是,组装好的候选人。

迹象表明您的代码的一部分非常适合于汇编代码:

  • 功能齐全,定义明确的小例程,不太可能更改
  • 可以利用特定汇编指令的功能(最小/最大/右移/等)
  • 被多次调用的函数(获得乘数:如果每次调用节省0.5usec,并且被调用10次,则可以节省5usec,这对您而言很重要)

了解编译器的函数调用约定(例如,将参数放在寄存器中的位置以及将其保存/恢复到哪个寄存器中),以便您可以编写C调用的汇编例程。

在我当前的项目中,我们有一个相当大的代码库,其中包含必须在10kHz中断(100usec –听起来很熟悉?)中运行的关键代码,而且汇编中编写的函数并不多。诸如CRC计算,软件队列,ADC增益/偏移补偿之类的东西。

祝好运!


关于经验执行时间测量技术的好建议
vicatcu 2011年

我的问题的另一个很好的答案,非常感谢Jason S的丰富知识!阅读此书后有两件事很明显;首先,我可以将中断从每100uS提高到500uS,以使代码有更多的时间执行,我意识到现在这样做并没有真正使我受益。其次,我认为我的代码可能效率太低,中断时间更长,代码更好,可能都很好。Stackoverflow是更好的发布代码的地方,所以我将在这里发布并在此处放置链接,如果有人想看看并提出任何建议,请执行:D
jwbensley 2011年

5

还要注意的另一件事-您可能需要执行一些优化来使代码更高效。

例如-我有一个在计时器中断内运行的例程。该例程必须在52µS内完成,并且在执行过程中必须逐步遍历大量内存。

我通过将主计数器变量锁定到一个寄存器来实现大幅度的速度提升(在我的µC和编译器上-与您的不同):

register unsigned int pointer asm("W9");

我不知道您的编译器的格式-RTFM,但是您可以做一些事情来提高例程的速度,而不必切换到汇编。

话虽如此,您在优化例程方面可能比编译器做得更好,因此切换到汇编可能会大大提高速度。


大声笑我“同时”评论了我自己关于汇编器调整和寄存器分配的答案:)
vicatcu 2011年

如果在16 MHz处理器上要花费100us,则它显然很大,因此有很多代码需要优化。我听说今天的编译器生成的代码比手工优化的程序集生成的代码大约多1.1倍。如此庞大的例程绝对不值得。为使6线功能
省电

1
不一定...循环中可能只有5行代码。这与代码大小无关,而与代码效率有关。您可能能够以不同的方式编写代码,以使其运行更快。我知道我做了中断程序。例如,牺牲尺寸以提高速度。通过顺序运行相同的代码10次,可以节省执行代码的时间-以及相关的计数器变量。是的,代码长10倍,但运行速度更快。
Majenko

嗨,马延科(Majenko),我不知道汇编语言,但我一直在考虑学习汇编语言,并认为Arduino的复杂性将比台式计算机要小,所以这可能是学习的好时机,尤其是我想知道有关正在发生的事情的更多信息和一个较低的级别。正如其他人所说,我不会只重写某些部分的全部内容。我的理解是,我可以在C中加入和退出ASM,这是正确的吗,这是如何实现C和ASM的这种混合?我将在一个大致思路之后在stackoverflow上发布具体细节。
jwbensley,2011年

@javano:是的。您可以在C语言中插入和退出ASM。许多嵌入式系统都是这样编写的(混合使用C语言和汇编语言),主要是因为有些事情根本无法通过C语言提供的原始C编译器来完成。时间。但是,诸如gcc(这是Arduino使用的编译器)之类的现代C编译器现在可以处理大多数情况下需要汇编语言的所有事情。
davidcary
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.