【问题标题】:C performance on a PIC board global variables vs. method localPIC 板全局变量与本地方法的 C 性能
【发布时间】:2012-02-07 14:50:33
【问题描述】:

全部,

我有每秒调用多次的 C 函数,因为它们是 PIC18 板上控制循环的一部分。这些函数具有只需要方法范围的变量,但我想知道是否有任何开销不断分配这些变量与使用全局或至少更高范围的变量。 (如果性能要求不使用方法局部变量,则考虑对结构进行类型定义以从更高的范围传递以避免使用全局变量)

这里有一些很好的话题涵盖了这个主题,但我还没有看到一个明确的答案,因为大多数宣扬的最佳实践都是我同意的,只要每微秒都没有性能提升,我就会遵循这些最佳实践.

一个线程提到使用文件范围的静态变量代替全局变量,但我不禁想知道是否有必要这样做。

大家怎么看?

【问题讨论】:

  • 你应该做一些分析。
  • 要么做一些分析,要么至少阅读生成的汇编代码来弄清楚访问是如何表达的。
  • 您必须问自己的是:这里或那里的几个 CPU 节拍对我的应用程序有影响吗?如果是这样,那你到底为什么要为这个项目选择人类历史上代码效率最低的 CPU 之一呢?这有点像买了一个便宜的 VM 甲壳虫,然后坚持在它上面放一个扰流板。
  • @JohanLundberg:8 位嵌入式系统的分析?甚至有任何工具吗?不,不要分析,使用示波器。如果您正在编写 MCU 应用程序并且没有示波器,那么程序性能是您最不关心的问题。
  • @Lundin,对 - “你应该做一些测量”。并弄清楚您是否真的会从优化中受益。

标签: c performance global-variables pic pic18


【解决方案1】:

访问局部变量需要执行类似*(SP + offset) 的操作(其中SP 是堆栈指针),而访问静态变量(包括全局变量)需要执行类似*(address) 的操作。

据我回忆,PIC 指令集的寻址模式非常有限。所以很有可能访问全局会更快,至少在第一次访问时是这样。如果编译器将计算的地址保存在寄存器中,则后续访问可能相同。

正如@unwind 在 cmets 中所说,您应该查看编译器输出和配置文件以确认。如果您已经证明在程序的运行时方面是值得的,我只会牺牲清晰度/可维护性。

【讨论】:

  • 我从未见过 PIC 编译器将软件堆栈用于任何事情,除了具有 FSR2+偏移寻址模式的 PIC(此类 PIC 的编译器使用 FSR2 作为堆栈指针)。
【解决方案2】:

虽然我没有使用现有的每一个 PIC 编译器,但有两种风格。我使用的样式通过分析程序的调用图来静态分配所有局部变量。如果实际上执行了每个可能的调用,则本地人消耗的堆栈内存量将与静态分配所需的内存量相匹配,但有一些警告(描述 HiTech 的 PICC-18“标准”编译器的行为——其他可能会有所不同)

  1. 通过在 调用者 范围内定义局部变量存储,并将指向该存储的两字节指针传递给被调用的函数来处理可变参数函数。
  2. 对于间接函数指针的每个不同签名,编译器都会在调用图中生成一个“伪函数”;调用该签名的函数的所有内容都调用该伪函数,并且该伪函数调用具有该签名的每个函数,该签名已获取其地址。

在这种编译器风格中,对局部变量的连续访问将与对全局变量的连续访问一样快。但是,除了显式声明为“near”的全局变量和静态变量(总长度不得超过 64-128 个字节(因 PIC 的不同型号而异))之外,每个模块的全局变量和静态变量都与局部变量分开放置,并且需要银行切换指令来访问不同银行中的东西。

我没有使用过的一些编译器使用了“增强指令集”选项。这个选项会吞噬 96 个字节的“near”bank(或全部,在少于 96 个字节的 PIC 上),并使用它来访问相对于 FSR2 寄存器的 96 个字节。如果它使用前 16 个或 32 个字节作为堆栈帧,这将是一个很棒的概念。使用 96 字节意味着放弃所有“近”存储,这是一个非常严重的限制。尽管如此,使用该指令集的编译器可以像访问全局变量一样快(如果不快的话)访问堆栈上的局部变量(不需要银行切换)。我真的希望 Microchip 可以选择只为堆栈帧留出 16 个字节左右,留下有用数量的“公共银行”RAM,但尽管如此,有些人对这种模式还是有好运的。

【讨论】:

  • 那岂不是不可能编写可重入函数,同时不必要地增加 RAM 消耗?
  • 它只会在理论上可能但实际上不可能通过调用图的路径的情况下增加 RAM 消耗。但是,它避免了任何危险,即通过调用图的“意外”路径会溢出堆栈。许多嵌入式应用程序不需要重入,而且鉴于普通 PIC 指令集处理地址算术的能力很差,自动变量的链接时分配是一个有用的替代品。
  • 听起来确实很危险...我不同意您不需要重新进入:虽然 RTOS 在 8 位应用程序上可能很少见,但几乎每个嵌入式系统都有 ISR,如果您曾经从 ISR 调用过一个函数,该函数也从主程序调用,该函数需要可重入。
  • @Lundin:根据编译器的不同,从中断和主线代码调用相同的函数可能会导致(1)链接错误,(2)编译器生成代码以备份例程在进入时使用的变量,并在退出时恢复它们,或者 (3) 两个自动生成的例程副本——一个供主线使用,一个供 ISR 使用。通常,解决方案是避免从 ISR 中进行子例程调用。
【解决方案3】:

我想这在很大程度上取决于您使用的编译器。我不知道 PIC,但我猜一些(全部?)PIC 编译器会优化代码,以便尽可能将局部变量存储在 CPU 寄存器中。如果是这样,那么局部变量可能与全局变量一样快。

否则,如果在堆栈上分配了局部变量,则全局变量的访问速度可能会快一些(请参阅 Oli 的回答)。

【讨论】:

    猜你喜欢
    • 2010-10-03
    • 2012-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-12
    • 2022-06-11
    • 1970-01-01
    • 2011-05-12
    相关资源
    最近更新 更多