我正在使用C语言处理嵌入式系统(主要是ARM Cortex M3-M4)。并且想知道将任务分成许多功能的优点/缺点是什么;
void Handle_Something(void)
{
// do Task-1
// do Task-2
// do Task-3
//etc.
}
到
void Hanlde_Something(void)
{
// Handle_Task1();
// Handle_Tasl2();
//etc.
}
如何在堆栈使用和整体处理速度方面检查这两种方法,哪种更安全/更好? (你可以假设这是在ISR之外)
据我所知,堆栈中的内存在每个调用/返回周期中被分配/解除分配给局部变量,因此在内存使用方面划分任务似乎是合理的但是在执行此操作时,我有时会从不同的来源获取Hardfaults(主要是总线或未定义的指令错误。)我无法弄清楚原因。
此外,工作速度对于我的领域中的许多应用程序非常重要,因此我需要知道哪种方法可以提供更快的响应。
我会很感激。提前谢谢大家
答案 0 :(得分:2)
这就是所谓的“预成熟优化”。
在过去编译器可怕的地方,他们不能自己内联函数。所以在过去,关键字inline
被添加到C中 - 类似的非标准版本在1999年之前也存在。这用于告诉错误的编译器它应该如何更好地生成代码。
如今这主要是历史。在确定什么以及何时内联时,编译器比程序员更好。然而,当被调用函数位于不同的“翻译单元”(基本上,在不同的.c文件中)时,它们可能会挣扎。但在你的情况下,我认为情况并非如此,但Handle_Task1()
等可以被视为同一文件中的函数。
考虑到上述因素:
如何根据堆栈使用情况和总体处理速度检查这两种方法
他们被认为是完全相同的。它们使用相同的堆栈空间并花费相同的时间来执行。
除非你有一个糟糕的旧编译器 - 在这种情况下,函数调用总是需要额外的空间和执行时间。既然您正在使用现代MCU:s,情况应该不是这样,或者您迫切需要获得更好的编译器。
根据经验,为了便于阅读和维护,将总是更好的做法分成几个较小的较大功能。即使在硬实时系统中,当使用错误的编译器时,很少有情况下函数调用开销是实际的瓶颈甚至。
答案 1 :(得分:1)
堆栈中的内存不能从某个复杂的内存池中分配/重新分配。堆栈指针简单地增加/减少。除了可以想象的最小/最小循环之外的所有操作基本上都是免费的(这些操作可能会被编译器优化)。
不要将功能组合在一起,因为它们可以重复使用变量,例如不要在整个程序中创建一堆int tempInt; long tempLong;
个变量。变量应仅用于单一目的,其范围应尽可能紧密。另请参阅:is it good or bad to reuse the variables?
扩展,将所有变量的范围尽可能保持为本地甚至可能导致编译器仅将变量保存在cpu寄存器中。实际上永远不会分配一个很快使用的变量!
尝试将函数限制为仅用于单个目的并尝试避免副作用:如果您避免使用全局变量,则每次使用完全相同的参数集调用函数时,函数变得更容易测试,优化和理解,它将执行完全相同的行动。看看:Why are global variables bad, in a single threaded, non-os, embedded application
答案 2 :(得分:1)
每种解决方案都有优点和缺点。
第一种方法允许更快地执行代码(先验),因为asm代码不会有与跳转相关的指令。但是,您必须考虑可读性,在同一功能中混合不同类型的功能(或创建大型功能,从指导的角度来看这不是一个好主意)。
第二种解决方案可能更容易理解,因为每个函数都包含一个简单的任务,而且它更容易记录(也就是说,你不必在同一个函数中解释不同的“目的”)。就像我说的那样 解决方案速度较慢,因为你的“调度程序”包含跳转,但你可以将简单任务声明为内联,因为你可以用适当的文档将代码拆分成几个简单的任务,编译器将生成一个汇编程序作为第一种方法,是,避免跳跃。
另一点是使用内存。如果从代码的不同部分调用简单任务,则内联的第一个解决方案和第二个解决方案比没有内联的第二个解决方案更差(就内存而言),因为函数被添加的次数与调用的次数相同来自代码的不同部分。
答案 3 :(得分:0)
在错误处理,调试和重新读取方面,使用模块总是更有效。考虑到一些繁重的工作库(SLAM,PCL等)作为函数,它们被用作外部函数并且它们不会导致性能的显着损失(有时它几乎是不可能将这么大的功能嵌入你的代码中)。 @Colin评论说,您可能会面临稍高的堆栈使用率。