类 的 C++ 布局新对齐(在 SAMD21 微控制器上)
C++ placement new alignment of classes (on a SAMD21 microcontroller)
我正在开发一个在 SAMD21 微控制器上 运行 的应用程序。对于那些不熟悉 SAMD21 的人,它包含一个 ARM Cortex-M0+ 处理器。我使用的具体型号有 32 kB 的 RAM。我的应用程序 运行 达到了 32 kB 的限制,因此我一直致力于优化内存使用代码。
我一直致力于的一项优化是减少堆碎片。比如我可能有这样一个场景:
A *my_obj = new A();
B *my_second_obj = new B();
delete A;
C *my_third_obj = new C();
在上面的示例中,在实例化 my_third_obj
时,内存分配器可能会尝试将其放置在最初由 my_obj
使用的空位置。但是,如果 my_third_obj
不适合那个位置,那么内存分配器将简单地在堆顶部分配更多 space 并移动堆指针。这将在 my_obj
所在的位置留下一个“洞”(稍后可能会被其他对象填充),但这会产生堆碎片。
在我的特定应用程序中,我确定我在任何时间点都只需要 classes A、B 或 C 的一个活动实例。因此,我正在考虑创建一个内存块来保存任何这些 classes 的当前实例,并简单地使内存块与最大的 class 一样大,以便它可以容纳class 中的任何一个。这将减少堆碎片,因为内存中总会有一个特定的位置,我将在其中分配这些特定的 classes.
这是我的想法的一个简单示例:
uint32_t max_size = sizeof(A);
max_size = (sizeof(B) > max_size) ? sizeof(B) : max_size;
max_size = (sizeof(C) > max_size) ? sizeof(C) : max_size;
uint8_t *buffer = new uint8_t[max_size];
//Some time later in the program...
C *my_obj = new(buffer) C();
//Some time later in the program...
my_obj->~C();
my_obj = NULL;
memset(buffer, 0, sizeof(max_size));
B *my_other_obj = new(buffer) B();
我在以前编写的代码中从未真正使用过 placement new,但我认为它对我目前的情况很有用。我的主要问题是:鉴于我已经列出的示例,我是否需要以任何方式更改代码来处理对齐问题? 类A、B、C的成员变量不同,大小也不同。这段代码是否“有效”,或者我是否需要做任何特殊的事情来处理内存对齐?
谢谢!
从 malloc 获得的缓冲区保证对任何基本类型都正确对齐,但我不确定从 new
获得的指针是否是,所以我更喜欢:
uint8_t *buffer = malloc(max_size); // delete it later with free
但您甚至可以通过使用 alignas
:
构建自定义缓冲区来摆脱任何动态分配
// only required for C++11, starting from C++14, std::max is constepr
constexpr size_t max3(size_t i, size_t j, size_t k) {
uint32_t max_size = i;
max_size = (i > j) ? i : j;
max_size = (k > max_size) ? k : max_size;
return max_size;
}
// declare a custom struct with required size and alignment
struct alignas(max3(alignof(A), alignof(B), alignof(C))) Buffer {
char buffer[max3(alignof(A), alignof(B), alignof(C))];
};
// build a statically allocated buffer of correct size and alignment
Buffer buffer;
从那时起,您可以安全地在 buffer
中构造一个对象,当然在重新使用内存之前明确地销毁它。
do I need to alter the code in any way to handle alignment issues?
是的。
do I need to do anything special to handle memory alignment?
是的。
uint8_t
在概念上表示一个 8 位的无符号整数。用char
或unsigned char
表示1个字节。
无论如何,请使用 operator new
大小和对齐方式:
auto maxsize = max_of_3(sizeof(A), sizeof(B), sizeof(C)),
auto neededalign = std::align_val_t(max_of_3(alignof(A), alignof(B), alignof(C));
void *buffer = operator new(maxsize, neededalign);
或静态:
std::aligned_storage<
max_of_3(sizeof(A), sizeof(B), sizeof(C)),
max_of_3(alignof(A), alignof(B), alignof(C))> buffer;
A *stuff = new(buffer.data) A;
我正在开发一个在 SAMD21 微控制器上 运行 的应用程序。对于那些不熟悉 SAMD21 的人,它包含一个 ARM Cortex-M0+ 处理器。我使用的具体型号有 32 kB 的 RAM。我的应用程序 运行 达到了 32 kB 的限制,因此我一直致力于优化内存使用代码。
我一直致力于的一项优化是减少堆碎片。比如我可能有这样一个场景:
A *my_obj = new A();
B *my_second_obj = new B();
delete A;
C *my_third_obj = new C();
在上面的示例中,在实例化 my_third_obj
时,内存分配器可能会尝试将其放置在最初由 my_obj
使用的空位置。但是,如果 my_third_obj
不适合那个位置,那么内存分配器将简单地在堆顶部分配更多 space 并移动堆指针。这将在 my_obj
所在的位置留下一个“洞”(稍后可能会被其他对象填充),但这会产生堆碎片。
在我的特定应用程序中,我确定我在任何时间点都只需要 classes A、B 或 C 的一个活动实例。因此,我正在考虑创建一个内存块来保存任何这些 classes 的当前实例,并简单地使内存块与最大的 class 一样大,以便它可以容纳class 中的任何一个。这将减少堆碎片,因为内存中总会有一个特定的位置,我将在其中分配这些特定的 classes.
这是我的想法的一个简单示例:
uint32_t max_size = sizeof(A);
max_size = (sizeof(B) > max_size) ? sizeof(B) : max_size;
max_size = (sizeof(C) > max_size) ? sizeof(C) : max_size;
uint8_t *buffer = new uint8_t[max_size];
//Some time later in the program...
C *my_obj = new(buffer) C();
//Some time later in the program...
my_obj->~C();
my_obj = NULL;
memset(buffer, 0, sizeof(max_size));
B *my_other_obj = new(buffer) B();
我在以前编写的代码中从未真正使用过 placement new,但我认为它对我目前的情况很有用。我的主要问题是:鉴于我已经列出的示例,我是否需要以任何方式更改代码来处理对齐问题? 类A、B、C的成员变量不同,大小也不同。这段代码是否“有效”,或者我是否需要做任何特殊的事情来处理内存对齐?
谢谢!
从 malloc 获得的缓冲区保证对任何基本类型都正确对齐,但我不确定从 new
获得的指针是否是,所以我更喜欢:
uint8_t *buffer = malloc(max_size); // delete it later with free
但您甚至可以通过使用 alignas
:
// only required for C++11, starting from C++14, std::max is constepr
constexpr size_t max3(size_t i, size_t j, size_t k) {
uint32_t max_size = i;
max_size = (i > j) ? i : j;
max_size = (k > max_size) ? k : max_size;
return max_size;
}
// declare a custom struct with required size and alignment
struct alignas(max3(alignof(A), alignof(B), alignof(C))) Buffer {
char buffer[max3(alignof(A), alignof(B), alignof(C))];
};
// build a statically allocated buffer of correct size and alignment
Buffer buffer;
从那时起,您可以安全地在 buffer
中构造一个对象,当然在重新使用内存之前明确地销毁它。
do I need to alter the code in any way to handle alignment issues?
是的。
do I need to do anything special to handle memory alignment?
是的。
uint8_t
在概念上表示一个 8 位的无符号整数。用char
或unsigned char
表示1个字节。
无论如何,请使用 operator new
大小和对齐方式:
auto maxsize = max_of_3(sizeof(A), sizeof(B), sizeof(C)),
auto neededalign = std::align_val_t(max_of_3(alignof(A), alignof(B), alignof(C));
void *buffer = operator new(maxsize, neededalign);
或静态:
std::aligned_storage<
max_of_3(sizeof(A), sizeof(B), sizeof(C)),
max_of_3(alignof(A), alignof(B), alignof(C))> buffer;
A *stuff = new(buffer.data) A;