让我们说我有一个lookuptable,它是在名为lut.h的标头中定义和声明的256个元素的数组。在程序的生存期内,将多次访问该阵列。
据我了解,如果将其定义并声明为静态,它将一直保留在内存中,直到程序完成为止,即,如果它是在uC上运行的任务,则数组将一直在内存中。
与没有静态一样,将在访问时将其加载到内存中。
在lut.h
static const float array[256] = {1.342, 14.21, 42.312, ...}
vs。
const float array[256] = {1.342, 14.21, 42.312, ...}
考虑到uC具有有限的spiflash和psram,什么是最注重性能的方法?
答案 0 :(得分:4)
您在这里有一些误解,因为MCU不是PC。只要MCU通电,MCU内存中的所有内容都会持续存在。程序不会结束资源或将资源返回给托管OS。
MCU上的“任务”表示您具有RTOS。他们使用自己的堆栈,这是它自己的主题,与您的问题无关。通常,RTOS上的所有任务都会永久执行,而不是像PC上的进程那样在运行时进行分配/取消分配。
static
与本地作用域上的自动相比确实意味着不同的RAM内存使用量,但不一定更多/更少的内存使用量。程序执行时,局部变量被压入/弹出堆栈。 static
个坐在他们指定的地址上。
与没有静态一样,将在访问时将其加载到内存中。
仅当要装入的数组在本地声明时。那就是:
void func (void)
{
int my_local_array[] = {1,2,3};
...
}
此处my_local_array
仅在执行该功能期间将值从闪存加载到RAM。这意味着两件事:
从闪存到RAM的实际复制速度为 slow 。首先,无论情况如何,复制内容总是很慢。但是在从RAM复制到闪存的特定情况下,这可能会特别慢,具体取决于MCU。
在闪存等待状态无法使用数据缓存进行复制的高端MCU上,这将格外慢。在无法直接寻址数据的怪异的哈佛架构MCU上,这将格外慢。等等
因此,很自然地,如果您每次调用一个函数时都执行此复制操作,而不仅仅是一次调用,您的程序将变慢得多。
大的局部对象导致需要更大的堆栈大小。堆栈必须足够大以应对最坏的情况。如果本地对象很大,则需要将堆栈大小设置得更大一些,以防止堆栈溢出。这意味着实际上可能导致内存使用效率降低。
因此,通过将对象设置为本地来判断是节省还是丢失内存并不是一件容易的事。
嵌入式系统编程中的一般最佳实践设计是不在栈上分配大对象,因为大对象使栈处理更加详细,并且栈溢出的可能性增加。在文件范围内,此类对象应声明为static
。尤其是在速度很重要的情况下。
static const float array
与const float array
这里还有另一个误解。在MCU系统中制作const
,同时将其放置在文件范围(“全局”)中,最有可能意味着该变量将最终存储在Flash ROM中,而不是RAM中。不论static
。
在大多数情况下,这是首选,因为一般而言,RAM是比闪存更有价值的资源。 static
在这里扮演的角色仅仅是良好的程序设计,因为它限制了对本地翻译单元对变量的访问,而不是使全局名称空间变得混乱。
在lut.h
永远不要在头文件中定义变量。
从程序设计的角度来看,这很不好,因为您到处都暴露了变量(“意大利面条编程”),而从链接器的角度来看,如果多个源文件包含相同的标头,则很糟糕。文件-这很有可能。
设计正确的程序将变量放置在.c文件中,并通过声明变量static
来限制访问。如有需要,可以通过设置器/获取器从外部进行访问。
uC具有有限的spiflash
什么是“ spiflash”?通过SPI访问外部串行闪存?然后,这一切都没有意义,因为此类闪存不是内存映射的,并且通常编译器无法利用它。对此类内存的访问必须由您的应用程序手动执行。
答案 1 :(得分:3)
如果您的数组是在文件级别定义的(您提到了lut.h
),并且它们都具有const
限定符,则不会将它们加载到RAM¹中。 static
关键字仅限制数组的范围,不会以任何方式更改其生存期。如果检查程序集的代码,则在编译时会看到两个数组look exactly the same:
static const int static_array[] = { 1, 2, 3 };
const int extern_array[] = { 1, 2, 3};
extern void do_something(const int * a);
int main(void)
{
do_something(static_array);
do_something(extern_array);
return 0;
}
结果程序集:
main:
sub rsp, 8
mov edi, OFFSET FLAT:static_array
call do_something
mov edi, OFFSET FLAT:extern_array
call do_something
xor eax, eax
add rsp, 8
ret
extern_array:
.long 1
.long 2
.long 3
static_array:
.long 1
.long 2
.long 3
另一方面,如果您在函数内部声明了数组,那么除非您添加了static
限定符,否则在函数运行期间,数组will be copied to temporary storage(堆栈)将一直存在:
extern void do_something(const int * a);
int main(void)
{
static const int static_local_array[] = { 1, 2, 3 };
const int local_array[] = { 1, 2, 3 };
do_something(static_local_array);
do_something(local_array);
return 0;
}
结果程序集:
main:
sub rsp, 24
mov edi, OFFSET FLAT:static_local_array
movabs rax, 8589934593
mov QWORD PTR [rsp+4], rax
mov DWORD PTR [rsp+12], 3
call do_something
lea rdi, [rsp+4]
call do_something
xor eax, eax
add rsp, 24
ret
static_local_array:
.long 1
.long 2
.long 3
¹更确切地说,它取决于编译器。一些编译器将需要其他自定义属性,以准确定义要存储数据的位置。一些编译器会在有足够的备用空间时尝试将阵列放入RAM中,以加快读取速度。