std::array 模板实例会占用更多的代码内存吗?



我有一个没有MMU的微控制器,但我们使用的是C和C++。

我们避免了所有动态内存使用(即没有new SomeClass()malloc())和大多数标准库。

半问题0:

据我了解std::array不使用任何动态内存,因此它的使用应该没问题(它仅在堆栈上)。查看std::array源代码,它看起来不错,因为它创建了一个 c 样式的数组,然后将功能包装在该数组周围。

我们使用的芯片有1MB的闪存来存储代码。

问题1:

我担心在std::array中使用模板会导致二进制文件变大,这可能会导致二进制文件超过 1MB 代码内存限制。

我认为如果您创建一个std::array< int, 5 >实例,那么对该std::array上函数的所有调用都将占用一定数量的代码内存,例如 X 字节的内存。

如果您创建std::array< SomeObject, 5 >的另一个实例,然后调用该std::array函数,这些函数中的每一个现在都会在二进制文件中复制,从而占用更多的代码内存吗?X 字节内存 + Y 字节内存。

如果是这样,您认为在有限的代码内存容量下生成的代码量会是一个问题吗?

问题2:

在上面的示例中,如果您创建了第二个std::array< int, 10 >实例,对函数的调用是否也会复制生成的代码中的函数调用?即使两个实例属于同一类型,int

std::array

被认为是零成本抽象,这意味着编译器应该可以相当优化它。

对于任何零成本抽象,它可能会导致较小的编译时间损失,并且如果不支持真正零成本所需的优化,则可能会导致较小的大小或运行时损失。

但是,请注意,编译器可以自由地在结构的末尾添加填充。由于std::array是一个结构体,你应该检查你的平台如何处理std::array,但我非常怀疑你是否如此。

以这个数组和std::array情况为例:

#include <numeric>
#include <iterator>
template<std::size_t n>
int stuff(const int(&arr)[n]) {
return std::accumulate(std::begin(arr), std::end(arr), 0);
}
int main() {
int arr[] = {1, 2, 3, 4, 5, 6};
return stuff(arr);
}

#include <numeric>
#include <iterator>
#include <array>
template<std::size_t n>
int stuff(const std::array<int, n>& arr) {
return std::accumulate(std::begin(arr), std::end(arr), 0);
}
int main() {
std::array arr = {1, 2, 3, 4, 5, 6};
return stuff(arr);
}

Clang很好地支持了这种情况。 所有具有std::array或原始数组的情况都以相同的方式处理:

-O2/-O3数组和带有叮当声的std::array

main: # @main
mov eax, 21
ret

但是,GCC 似乎在优化它时遇到了问题,对于 bithstd::array和原始数组的情况:

使用 GCC-O3用于阵列和std::array

main:
movdqa xmm0, XMMWORD PTR .LC0[rip]
movaps XMMWORD PTR [rsp-40], xmm0
mov edx, DWORD PTR [rsp-32]
mov eax, DWORD PTR [rsp-28]
lea eax, [rdx+14+rax]
ret
.LC0:
.long 1
.long 2
.long 3
.long 4

然后,在原始数组的情况下,它似乎使用-O2进行了更好的优化,并且在std::array时失败:

-O2海湾合作委员会std::array

main:
movabs rax, 8589934593
lea rdx, [rsp-40]
mov ecx, 1
mov QWORD PTR [rsp-40], rax
movabs rax, 17179869187
mov QWORD PTR [rsp-32], rax
movabs rax, 25769803781
lea rsi, [rdx+24]
mov QWORD PTR [rsp-24], rax
xor eax, eax
jmp .L3
.L5:
mov ecx, DWORD PTR [rdx]
.L3:
add rdx, 4
add eax, ecx
cmp rdx, rsi
jne .L5
rep ret 

-O2GCC 原始数组:

main:
mov eax, 21
ret

似乎 GCC 错误无法优化-O3-O2成功,在最新版本中得到了修复。

这是一个编译器资源管理器,其中包含所有O2O3


在上述所有这些情况下,您可以看到一个常见的模式:二进制文件中不输出有关std::array的信息。没有构造函数,没有operator[],甚至没有迭代器,也没有算法。一切都是内联的。编译器擅长内联简单的函数。std::array成员函数通常非常简单。

如果你创建另一个 std::array<SomeObject,5>的实例,然后调用该 std::array 的函数,这些函数中的每一个现在都会在二进制文件中复制,从而占用更多的闪存吗?X 字节内存 + Y 字节内存。

好吧,您更改了数组包含的数据类型。如果您手动添加所有函数的重载来处理这种附加情况,那么是的,所有这些新功能可能会占用一些空间。如果您的函数很小,则很有可能将它们内联并占用更少的空间。正如您在上面的示例中所看到的,内联和恒定折叠可能会大大减少您的二进制大小。

在上面的示例中,如果您创建了第二个 std::array 实例,对函数的调用是否也会复制闪存中的函数调用?即使两个实例的类型相同,int?

再次视情况而定。如果数组大小中有许多函数模板化,则std::array数组和原始数组都可能"创建"不同的函数。但同样,如果它们是内联的,则无需担心重复。

两者都将原始数组和std::array,您可以传递指向数组开头的指针并传递大小。如果您发现这更适合您的情况,请使用它,但仍然原始数组和std::array可以做到这一点。对于原始数组,它隐式衰减为指针,对于std::array,您必须使用arr.data()来获取指针。

相关内容

  • 没有找到相关文章

最新更新