打包结构/避免填充
Posted
技术标签:
【中文标题】打包结构/避免填充【英文标题】:pack struct / avoid padding 【发布时间】:2015-06-23 07:07:08 【问题描述】:我有以下结构:
struct SkipListNode
void *data; // 8 bytes
uint8_t size; // 1 byte
// 7 bytes padding here...
void *next[1]; // dynamic array, 8 bytes each "cell"
;
我正在使用malloc()
,我分配的空间比sizeof(SkipListNode)
多,所以我正在扩展next[]
数组。
我想避免7 bytes
浪费。我可以完全删除 size 字段,但是我应该在数组末尾保留单个 NULL
(8 个字节)。但是,这无助于减小尺寸。
我应该使用__ attribute__((__ packed__))
还是有其他方法可以解决问题?
这也必须在 C 和 C++ 下编译。
编译器是 gcc。
【问题讨论】:
你考虑过#pragma pack(1)
只是为了确保,打包结构的编译器指令对于不同的编译器是不同的。请同时指定编译器。
@Nick 是正确的,但我会给操作系统一些管理它自己的资源的功劳。当您使用打包时,如果此结构可能的最小尺寸,则为实际尺寸。我相信 malloc 级别中的额外填充不会浪费(想想这样做的后果!),而是被重用于进一步的 malloc 调用。无论如何它很容易测试......只需使用 1000 个结构运行您的进程并查看它消耗了多少内存。最好在编写自己的分配器之前这样做
@Ishay:解压后的结构体中的内存被浪费了。添加它以便变量位于机器字(现在通常为 64 位)的“偶数”边界上。如果你打包这个,它通常用于跨机器边界传输,因为只有这样才知道真实的结构,vars一个接一个地紧挨着。这意味着,当您加载它们时,将会有(afaik)额外的操作来解决它。因此请注意,您正在用内存换取 CPU。只有在真的、真的、真的、必要时才应该打包。
@Lundin:不,它不支持灵活成员,这就是为什么 size 是 1 而不是 0 或只是 []。但是代码工作得很好,你只需要在分配时记住这个 1 。这甚至适用于 POD 类
【参考方案1】:
#include <stdio.h>
#include <stdint.h>
struct SkipListNode
void *data; // 8 bytes
uint8_t size; // 1 byte
void *next[1];
;
struct SkipListNode_pack
void *data; // 8 bytes
uint8_t size; // 1 byte
void *next[1];
__attribute__((packed));
int main(int argc, char** argv)
printf("%d:%d\n", sizeof(struct SkipListNode), sizeof(struct SkipListNode_pack));
return 0;
以上代码的输出:ishaypeled@arania sandbox]$ ./test
24:17
是的,如果你想节省内存,你绝对应该在你的情况下使用__attribute__((packed))
。另一方面,就像@MarioTheSpoon 所说的那样 - 它可能会带来性能损失。
我会检查填充解决方案,看看您是否可以在特定机器上忍受这种惩罚。我敢打赌,除非你真的很注重性能,否则你甚至不会注意到它。
【讨论】:
我不同意!仅在确实需要时使用#pragma pack(1)。在标准计算机上的标准程序中浪费约 100k 不应该是最担心的问题。当然,如果它是某种嵌入式系统,或者您要分配数百万条这些记录,则推理可能会有所不同。系统再次欺骗你,因为 malloc 将再次在 64 位边界上对齐缓冲区(在所描述的系统上)。 根据 OP 的struct
省略 next
成员有什么特别的原因吗?
@MarioTheSpoon 这个问题的动机是为了节省内存......我已经同意它可能会导致 CPU 权衡,但这确实是作者要求的......跨度>
这里有一个关于 CPU 惩罚的有趣讨论:***.com/questions/3454673/…【参考方案2】:
我接受了另一个答案,
但是,我确实考虑过重构程序,我找到了一种正确进行的方法,而无需知道数组的大小。
如果有人对此感兴趣,请联系:https://github.com/nmmmnu/HM3/blob/master/skiplist.cc
所以我消除了size
字段并消除了结尾的NULL。现在结构已对齐:)
在重构时,我记得可以使用malloc_usable_size()
来查找已分配块的大小。这是沉重的非便携式黑客,但在其他一些情况下可以很好地工作。
【讨论】:
以上是关于打包结构/避免填充的主要内容,如果未能解决你的问题,请参考以下文章